comparison forum/Sources/Profile-View.php @ 76:e3e11437ecea website

Add forum code
author Chris Cannam
date Sun, 07 Jul 2013 11:25:48 +0200
parents
children
comparison
equal deleted inserted replaced
75:72f59aa7e503 76:e3e11437ecea
1 <?php
2
3 /**
4 * Simple Machines Forum (SMF)
5 *
6 * @package SMF
7 * @author Simple Machines http://www.simplemachines.org
8 * @copyright 2011 Simple Machines
9 * @license http://www.simplemachines.org/about/smf/license.php BSD
10 *
11 * @version 2.0
12 */
13
14 if (!defined('SMF'))
15 die('Hacking attempt...');
16
17 /*
18
19 void summary(int id_member)
20 // !!!
21
22 void showPosts(int id_member)
23 // !!!
24
25 void showAttachments(int id_member)
26 // !!!
27
28 void statPanel(int id_member)
29 // !!!
30
31 void tracking(int id_member)
32 // !!!
33
34 void trackUser(int id_member)
35 // !!!
36
37 int list_getUserErrorCount(string where)
38 // !!!
39
40 array list_getUserErrors(int start, int items_per_page, string sort, string where, array where_vars)
41 // !!!
42
43 int list_getIPMessageCount(string where)
44 // !!!
45
46 array list_getIPMessages(int start, int items_per_page, string sort, string where, array where_vars)
47 // !!!
48
49 void TrackIP(int id_member = none)
50 // !!!
51
52 void trackEdits(int id_member)
53 // !!!
54
55 int list_getProfileEditCount(int id_member)
56 // !!!
57
58 array list_getProfileEdits(int start, int items_per_page, string sort, int id_member)
59 // !!!
60
61 void showPermissions(int id_member)
62 // !!!
63
64 void viewWarning(int id_member)
65 // !!!
66 */
67
68 // View a summary.
69 function summary($memID)
70 {
71 global $context, $memberContext, $txt, $modSettings, $user_info, $user_profile, $sourcedir, $scripturl, $smcFunc;
72
73 // Attempt to load the member's profile data.
74 if (!loadMemberContext($memID) || !isset($memberContext[$memID]))
75 fatal_lang_error('not_a_user', false);
76
77 // Set up the stuff and load the user.
78 $context += array(
79 'page_title' => sprintf($txt['profile_of_username'], $memberContext[$memID]['name']),
80 'can_send_pm' => allowedTo('pm_send'),
81 'can_have_buddy' => allowedTo('profile_identity_own') && !empty($modSettings['enable_buddylist']),
82 'can_issue_warning' => in_array('w', $context['admin_features']) && allowedTo('issue_warning') && $modSettings['warning_settings'][0] == 1,
83 );
84 $context['member'] = &$memberContext[$memID];
85 $context['can_view_warning'] = in_array('w', $context['admin_features']) && (allowedTo('issue_warning') && !$context['user']['is_owner']) || (!empty($modSettings['warning_show']) && ($modSettings['warning_show'] > 1 || $context['user']['is_owner']));
86
87 // Set a canonical URL for this page.
88 $context['canonical_url'] = $scripturl . '?action=profile;u=' . $memID;
89
90 // Are there things we don't show?
91 $context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array();
92
93 // See if they have broken any warning levels...
94 list ($modSettings['warning_enable'], $modSettings['user_limit']) = explode(',', $modSettings['warning_settings']);
95 if (!empty($modSettings['warning_mute']) && $modSettings['warning_mute'] <= $context['member']['warning'])
96 $context['warning_status'] = $txt['profile_warning_is_muted'];
97 elseif (!empty($modSettings['warning_moderate']) && $modSettings['warning_moderate'] <= $context['member']['warning'])
98 $context['warning_status'] = $txt['profile_warning_is_moderation'];
99 elseif (!empty($modSettings['warning_watch']) && $modSettings['warning_watch'] <= $context['member']['warning'])
100 $context['warning_status'] = $txt['profile_warning_is_watch'];
101
102 // They haven't even been registered for a full day!?
103 $days_registered = (int) ((time() - $user_profile[$memID]['date_registered']) / (3600 * 24));
104 if (empty($user_profile[$memID]['date_registered']) || $days_registered < 1)
105 $context['member']['posts_per_day'] = $txt['not_applicable'];
106 else
107 $context['member']['posts_per_day'] = comma_format($context['member']['real_posts'] / $days_registered, 3);
108
109 // Set the age...
110 if (empty($context['member']['birth_date']))
111 {
112 $context['member'] += array(
113 'age' => $txt['not_applicable'],
114 'today_is_birthday' => false
115 );
116 }
117 else
118 {
119 list ($birth_year, $birth_month, $birth_day) = sscanf($context['member']['birth_date'], '%d-%d-%d');
120 $datearray = getdate(forum_time());
121 $context['member'] += array(
122 'age' => $birth_year <= 4 ? $txt['not_applicable'] : $datearray['year'] - $birth_year - (($datearray['mon'] > $birth_month || ($datearray['mon'] == $birth_month && $datearray['mday'] >= $birth_day)) ? 0 : 1),
123 'today_is_birthday' => $datearray['mon'] == $birth_month && $datearray['mday'] == $birth_day
124 );
125 }
126
127 if (allowedTo('moderate_forum'))
128 {
129 // Make sure it's a valid ip address; otherwise, don't bother...
130 if (preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $memberContext[$memID]['ip']) == 1 && empty($modSettings['disableHostnameLookup']))
131 $context['member']['hostname'] = host_from_ip($memberContext[$memID]['ip']);
132 else
133 $context['member']['hostname'] = '';
134
135 $context['can_see_ip'] = true;
136 }
137 else
138 $context['can_see_ip'] = false;
139
140 if (!empty($modSettings['who_enabled']))
141 {
142 include_once($sourcedir . '/Who.php');
143 $action = determineActions($user_profile[$memID]['url']);
144
145 if ($action !== false)
146 $context['member']['action'] = $action;
147 }
148
149 // If the user is awaiting activation, and the viewer has permission - setup some activation context messages.
150 if ($context['member']['is_activated'] % 10 != 1 && allowedTo('moderate_forum'))
151 {
152 $context['activate_type'] = $context['member']['is_activated'];
153 // What should the link text be?
154 $context['activate_link_text'] = in_array($context['member']['is_activated'], array(3, 4, 5, 13, 14, 15)) ? $txt['account_approve'] : $txt['account_activate'];
155
156 // Should we show a custom message?
157 $context['activate_message'] = isset($txt['account_activate_method_' . $context['member']['is_activated'] % 10]) ? $txt['account_activate_method_' . $context['member']['is_activated'] % 10] : $txt['account_not_activated'];
158 }
159
160 // Is the signature even enabled on this forum?
161 $context['signature_enabled'] = substr($modSettings['signature_settings'], 0, 1) == 1;
162
163 // How about, are they banned?
164 $context['member']['bans'] = array();
165 if (allowedTo('moderate_forum'))
166 {
167 // Can they edit the ban?
168 $context['can_edit_ban'] = allowedTo('manage_bans');
169
170 $ban_query = array();
171 $ban_query_vars = array(
172 'time' => time(),
173 );
174 $ban_query[] = 'id_member = ' . $context['member']['id'];
175
176 // Valid IP?
177 if (preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $memberContext[$memID]['ip'], $ip_parts) == 1)
178 {
179 $ban_query[] = '((' . $ip_parts[1] . ' BETWEEN bi.ip_low1 AND bi.ip_high1)
180 AND (' . $ip_parts[2] . ' BETWEEN bi.ip_low2 AND bi.ip_high2)
181 AND (' . $ip_parts[3] . ' BETWEEN bi.ip_low3 AND bi.ip_high3)
182 AND (' . $ip_parts[4] . ' BETWEEN bi.ip_low4 AND bi.ip_high4))';
183
184 // Do we have a hostname already?
185 if (!empty($context['member']['hostname']))
186 {
187 $ban_query[] = '({string:hostname} LIKE hostname)';
188 $ban_query_vars['hostname'] = $context['member']['hostname'];
189 }
190 }
191 // Use '255.255.255.255' for 'unknown' - it's not valid anyway.
192 elseif ($memberContext[$memID]['ip'] == 'unknown')
193 $ban_query[] = '(bi.ip_low1 = 255 AND bi.ip_high1 = 255
194 AND bi.ip_low2 = 255 AND bi.ip_high2 = 255
195 AND bi.ip_low3 = 255 AND bi.ip_high3 = 255
196 AND bi.ip_low4 = 255 AND bi.ip_high4 = 255)';
197
198 // Check their email as well...
199 if (strlen($context['member']['email']) != 0)
200 {
201 $ban_query[] = '({string:email} LIKE bi.email_address)';
202 $ban_query_vars['email'] = $context['member']['email'];
203 }
204
205 // So... are they banned? Dying to know!
206 $request = $smcFunc['db_query']('', '
207 SELECT bg.id_ban_group, bg.name, bg.cannot_access, bg.cannot_post, bg.cannot_register,
208 bg.cannot_login, bg.reason
209 FROM {db_prefix}ban_items AS bi
210 INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group AND (bg.expire_time IS NULL OR bg.expire_time > {int:time}))
211 WHERE (' . implode(' OR ', $ban_query) . ')',
212 $ban_query_vars
213 );
214 while ($row = $smcFunc['db_fetch_assoc']($request))
215 {
216 // Work out what restrictions we actually have.
217 $ban_restrictions = array();
218 foreach (array('access', 'register', 'login', 'post') as $type)
219 if ($row['cannot_' . $type])
220 $ban_restrictions[] = $txt['ban_type_' . $type];
221
222 // No actual ban in place?
223 if (empty($ban_restrictions))
224 continue;
225
226 // Prepare the link for context.
227 $ban_explanation = sprintf($txt['user_cannot_due_to'], implode(', ', $ban_restrictions), '<a href="' . $scripturl . '?action=admin;area=ban;sa=edit;bg=' . $row['id_ban_group'] . '">' . $row['name'] . '</a>');
228
229 $context['member']['bans'][$row['id_ban_group']] = array(
230 'reason' => empty($row['reason']) ? '' : '<br /><br /><strong>' . $txt['ban_reason'] . ':</strong> ' . $row['reason'],
231 'cannot' => array(
232 'access' => !empty($row['cannot_access']),
233 'register' => !empty($row['cannot_register']),
234 'post' => !empty($row['cannot_post']),
235 'login' => !empty($row['cannot_login']),
236 ),
237 'explanation' => $ban_explanation,
238 );
239 }
240 $smcFunc['db_free_result']($request);
241 }
242
243 loadCustomFields($memID);
244 }
245
246 // !!! This function needs to be split up properly.
247 // Show all posts by the current user
248 function showPosts($memID)
249 {
250 global $txt, $user_info, $scripturl, $modSettings;
251 global $context, $user_profile, $sourcedir, $smcFunc, $board;
252
253 // Some initial context.
254 $context['start'] = (int) $_REQUEST['start'];
255 $context['current_member'] = $memID;
256
257 // Create the tabs for the template.
258 $context[$context['profile_menu_name']]['tab_data'] = array(
259 'title' => $txt['showPosts'],
260 'description' => $txt['showPosts_help'],
261 'icon' => 'profile_sm.gif',
262 'tabs' => array(
263 'messages' => array(
264 ),
265 'topics' => array(
266 ),
267 'attach' => array(
268 ),
269 ),
270 );
271
272 // Set the page title
273 $context['page_title'] = $txt['showPosts'] . ' - ' . $user_profile[$memID]['real_name'];
274
275 // Is the load average too high to allow searching just now?
276 if (!empty($context['load_average']) && !empty($modSettings['loadavg_show_posts']) && $context['load_average'] >= $modSettings['loadavg_show_posts'])
277 fatal_lang_error('loadavg_show_posts_disabled', false);
278
279 // If we're specifically dealing with attachments use that function!
280 if (isset($_GET['sa']) && $_GET['sa'] == 'attach')
281 return showAttachments($memID);
282
283 // Are we just viewing topics?
284 $context['is_topics'] = isset($_GET['sa']) && $_GET['sa'] == 'topics' ? true : false;
285
286 // If just deleting a message, do it and then redirect back.
287 if (isset($_GET['delete']) && !$context['is_topics'])
288 {
289 checkSession('get');
290
291 // We need msg info for logging.
292 $request = $smcFunc['db_query']('', '
293 SELECT subject, id_member, id_topic, id_board
294 FROM {db_prefix}messages
295 WHERE id_msg = {int:id_msg}',
296 array(
297 'id_msg' => (int) $_GET['delete'],
298 )
299 );
300 $info = $smcFunc['db_fetch_row']($request);
301 $smcFunc['db_free_result']($request);
302
303 // Trying to remove a message that doesn't exist.
304 if (empty($info))
305 redirectexit('action=profile;u=' . $memID . ';area=showposts;start=' . $_GET['start']);
306
307 // We can be lazy, since removeMessage() will check the permissions for us.
308 require_once($sourcedir . '/RemoveTopic.php');
309 removeMessage((int) $_GET['delete']);
310
311 // Add it to the mod log.
312 if (allowedTo('delete_any') && (!allowedTo('delete_own') || $info[1] != $user_info['id']))
313 logAction('delete', array('topic' => $info[2], 'subject' => $info[0], 'member' => $info[1], 'board' => $info[3]));
314
315 // Back to... where we are now ;).
316 redirectexit('action=profile;u=' . $memID . ';area=showposts;start=' . $_GET['start']);
317 }
318
319 // Default to 10.
320 if (empty($_REQUEST['viewscount']) || !is_numeric($_REQUEST['viewscount']))
321 $_REQUEST['viewscount'] = '10';
322
323 if ($context['is_topics'])
324 $request = $smcFunc['db_query']('', '
325 SELECT COUNT(*)
326 FROM {db_prefix}topics AS t' . ($user_info['query_see_board'] == '1=1' ? '' : '
327 INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board AND {query_see_board})') . '
328 WHERE t.id_member_started = {int:current_member}' . (!empty($board) ? '
329 AND t.id_board = {int:board}' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
330 AND t.approved = {int:is_approved}'),
331 array(
332 'current_member' => $memID,
333 'is_approved' => 1,
334 'board' => $board,
335 )
336 );
337 else
338 $request = $smcFunc['db_query']('', '
339 SELECT COUNT(*)
340 FROM {db_prefix}messages AS m' . ($user_info['query_see_board'] == '1=1' ? '' : '
341 INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})') . '
342 WHERE m.id_member = {int:current_member}' . (!empty($board) ? '
343 AND m.id_board = {int:board}' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
344 AND m.approved = {int:is_approved}'),
345 array(
346 'current_member' => $memID,
347 'is_approved' => 1,
348 'board' => $board,
349 )
350 );
351 list ($msgCount) = $smcFunc['db_fetch_row']($request);
352 $smcFunc['db_free_result']($request);
353
354 $request = $smcFunc['db_query']('', '
355 SELECT MIN(id_msg), MAX(id_msg)
356 FROM {db_prefix}messages AS m
357 WHERE m.id_member = {int:current_member}' . (!empty($board) ? '
358 AND m.id_board = {int:board}' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
359 AND m.approved = {int:is_approved}'),
360 array(
361 'current_member' => $memID,
362 'is_approved' => 1,
363 'board' => $board,
364 )
365 );
366 list ($min_msg_member, $max_msg_member) = $smcFunc['db_fetch_row']($request);
367 $smcFunc['db_free_result']($request);
368
369 $reverse = false;
370 $range_limit = '';
371 $maxIndex = (int) $modSettings['defaultMaxMessages'];
372
373 // Make sure the starting place makes sense and construct our friend the page index.
374 $context['page_index'] = constructPageIndex($scripturl . '?action=profile;u=' . $memID . ';area=showposts' . ($context['is_topics'] ? ';sa=topics' : '') . (!empty($board) ? ';board=' . $board : ''), $context['start'], $msgCount, $maxIndex);
375 $context['current_page'] = $context['start'] / $maxIndex;
376
377 // Reverse the query if we're past 50% of the pages for better performance.
378 $start = $context['start'];
379 $reverse = $_REQUEST['start'] > $msgCount / 2;
380 if ($reverse)
381 {
382 $maxIndex = $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] + 1 && $msgCount > $context['start'] ? $msgCount - $context['start'] : (int) $modSettings['defaultMaxMessages'];
383 $start = $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] + 1 || $msgCount < $context['start'] + $modSettings['defaultMaxMessages'] ? 0 : $msgCount - $context['start'] - $modSettings['defaultMaxMessages'];
384 }
385
386 // Guess the range of messages to be shown.
387 if ($msgCount > 1000)
388 {
389 $margin = floor(($max_msg_member - $min_msg_member) * (($start + $modSettings['defaultMaxMessages']) / $msgCount) + .1 * ($max_msg_member - $min_msg_member));
390 // Make a bigger margin for topics only.
391 if ($context['is_topics'])
392 {
393 $margin *= 5;
394 $range_limit = $reverse ? 't.id_first_msg < ' . ($min_msg_member + $margin) : 't.id_first_msg > ' . ($max_msg_member - $margin);
395 }
396 else
397 $range_limit = $reverse ? 'm.id_msg < ' . ($min_msg_member + $margin) : 'm.id_msg > ' . ($max_msg_member - $margin);
398 }
399
400 // Find this user's posts. The left join on categories somehow makes this faster, weird as it looks.
401 $looped = false;
402 while (true)
403 {
404 if ($context['is_topics'])
405 {
406 $request = $smcFunc['db_query']('', '
407 SELECT
408 b.id_board, b.name AS bname, c.id_cat, c.name AS cname, t.id_member_started, t.id_first_msg, t.id_last_msg,
409 t.approved, m.body, m.smileys_enabled, m.subject, m.poster_time, m.id_topic, m.id_msg
410 FROM {db_prefix}topics AS t
411 INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
412 LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
413 INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
414 WHERE t.id_member_started = {int:current_member}' . (!empty($board) ? '
415 AND t.id_board = {int:board}' : '') . (empty($range_limit) ? '' : '
416 AND ' . $range_limit) . '
417 AND {query_see_board}' . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
418 AND t.approved = {int:is_approved} AND m.approved = {int:is_approved}') . '
419 ORDER BY t.id_first_msg ' . ($reverse ? 'ASC' : 'DESC') . '
420 LIMIT ' . $start . ', ' . $maxIndex,
421 array(
422 'current_member' => $memID,
423 'is_approved' => 1,
424 'board' => $board,
425 )
426 );
427 }
428 else
429 {
430 $request = $smcFunc['db_query']('', '
431 SELECT
432 b.id_board, b.name AS bname, c.id_cat, c.name AS cname, m.id_topic, m.id_msg,
433 t.id_member_started, t.id_first_msg, t.id_last_msg, m.body, m.smileys_enabled,
434 m.subject, m.poster_time, m.approved
435 FROM {db_prefix}messages AS m
436 INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
437 INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
438 LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
439 WHERE m.id_member = {int:current_member}' . (!empty($board) ? '
440 AND b.id_board = {int:board}' : '') . (empty($range_limit) ? '' : '
441 AND ' . $range_limit) . '
442 AND {query_see_board}' . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
443 AND t.approved = {int:is_approved} AND m.approved = {int:is_approved}') . '
444 ORDER BY m.id_msg ' . ($reverse ? 'ASC' : 'DESC') . '
445 LIMIT ' . $start . ', ' . $maxIndex,
446 array(
447 'current_member' => $memID,
448 'is_approved' => 1,
449 'board' => $board,
450 )
451 );
452 }
453
454 // Make sure we quit this loop.
455 if ($smcFunc['db_num_rows']($request) === $maxIndex || $looped)
456 break;
457 $looped = true;
458 $range_limit = '';
459 }
460
461 // Start counting at the number of the first message displayed.
462 $counter = $reverse ? $context['start'] + $maxIndex + 1 : $context['start'];
463 $context['posts'] = array();
464 $board_ids = array('own' => array(), 'any' => array());
465 while ($row = $smcFunc['db_fetch_assoc']($request))
466 {
467 // Censor....
468 censorText($row['body']);
469 censorText($row['subject']);
470
471 // Do the code.
472 $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
473
474 // And the array...
475 $context['posts'][$counter += $reverse ? -1 : 1] = array(
476 'body' => $row['body'],
477 'counter' => $counter,
478 'alternate' => $counter % 2,
479 'category' => array(
480 'name' => $row['cname'],
481 'id' => $row['id_cat']
482 ),
483 'board' => array(
484 'name' => $row['bname'],
485 'id' => $row['id_board']
486 ),
487 'topic' => $row['id_topic'],
488 'subject' => $row['subject'],
489 'start' => 'msg' . $row['id_msg'],
490 'time' => timeformat($row['poster_time']),
491 'timestamp' => forum_time(true, $row['poster_time']),
492 'id' => $row['id_msg'],
493 'can_reply' => false,
494 'can_mark_notify' => false,
495 'can_delete' => false,
496 'delete_possible' => ($row['id_first_msg'] != $row['id_msg'] || $row['id_last_msg'] == $row['id_msg']) && (empty($modSettings['edit_disable_time']) || $row['poster_time'] + $modSettings['edit_disable_time'] * 60 >= time()),
497 'approved' => $row['approved'],
498 );
499
500 if ($user_info['id'] == $row['id_member_started'])
501 $board_ids['own'][$row['id_board']][] = $counter;
502 $board_ids['any'][$row['id_board']][] = $counter;
503 }
504 $smcFunc['db_free_result']($request);
505
506 // All posts were retrieved in reverse order, get them right again.
507 if ($reverse)
508 $context['posts'] = array_reverse($context['posts'], true);
509
510 // These are all the permissions that are different from board to board..
511 if ($context['is_topics'])
512 $permissions = array(
513 'own' => array(
514 'post_reply_own' => 'can_reply',
515 ),
516 'any' => array(
517 'post_reply_any' => 'can_reply',
518 'mark_any_notify' => 'can_mark_notify',
519 )
520 );
521 else
522 $permissions = array(
523 'own' => array(
524 'post_reply_own' => 'can_reply',
525 'delete_own' => 'can_delete',
526 ),
527 'any' => array(
528 'post_reply_any' => 'can_reply',
529 'mark_any_notify' => 'can_mark_notify',
530 'delete_any' => 'can_delete',
531 )
532 );
533
534 // For every permission in the own/any lists...
535 foreach ($permissions as $type => $list)
536 {
537 foreach ($list as $permission => $allowed)
538 {
539 // Get the boards they can do this on...
540 $boards = boardsAllowedTo($permission);
541
542 // Hmm, they can do it on all boards, can they?
543 if (!empty($boards) && $boards[0] == 0)
544 $boards = array_keys($board_ids[$type]);
545
546 // Now go through each board they can do the permission on.
547 foreach ($boards as $board_id)
548 {
549 // There aren't any posts displayed from this board.
550 if (!isset($board_ids[$type][$board_id]))
551 continue;
552
553 // Set the permission to true ;).
554 foreach ($board_ids[$type][$board_id] as $counter)
555 $context['posts'][$counter][$allowed] = true;
556 }
557 }
558 }
559
560 // Clean up after posts that cannot be deleted and quoted.
561 $quote_enabled = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));
562 foreach ($context['posts'] as $counter => $dummy)
563 {
564 $context['posts'][$counter]['can_delete'] &= $context['posts'][$counter]['delete_possible'];
565 $context['posts'][$counter]['can_quote'] = $context['posts'][$counter]['can_reply'] && $quote_enabled;
566 }
567 }
568
569 // Show all the attachments of a user.
570 function showAttachments($memID)
571 {
572 global $txt, $user_info, $scripturl, $modSettings, $board;
573 global $context, $user_profile, $sourcedir, $smcFunc;
574
575 // OBEY permissions!
576 $boardsAllowed = boardsAllowedTo('view_attachments');
577 // Make sure we can't actually see anything...
578 if (empty($boardsAllowed))
579 $boardsAllowed = array(-1);
580
581 // Get the total number of attachments they have posted.
582 $request = $smcFunc['db_query']('', '
583 SELECT COUNT(*)
584 FROM {db_prefix}attachments AS a
585 INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
586 INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
587 WHERE a.attachment_type = {int:attachment_type}
588 AND a.id_msg != {int:no_message}
589 AND m.id_member = {int:current_member}' . (!empty($board) ? '
590 AND b.id_board = {int:board}' : '') . (!in_array(0, $boardsAllowed) ? '
591 AND b.id_board IN ({array_int:boards_list})' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
592 AND m.approved = {int:is_approved}'),
593 array(
594 'boards_list' => $boardsAllowed,
595 'attachment_type' => 0,
596 'no_message' => 0,
597 'current_member' => $memID,
598 'is_approved' => 1,
599 'board' => $board,
600 )
601 );
602 list ($attachCount) = $smcFunc['db_fetch_row']($request);
603 $smcFunc['db_free_result']($request);
604
605 $maxIndex = (int) $modSettings['defaultMaxMessages'];
606
607 // What about ordering?
608 $sortTypes = array(
609 'filename' => 'a.filename',
610 'downloads' => 'a.downloads',
611 'subject' => 'm.subject',
612 'posted' => 'm.poster_time',
613 );
614 $context['sort_order'] = isset($_GET['sort']) && isset($sortTypes[$_GET['sort']]) ? $_GET['sort'] : 'posted';
615 $context['sort_direction'] = isset($_GET['asc']) ? 'up' : 'down';
616
617 $sort = $sortTypes[$context['sort_order']];
618
619 // Let's get ourselves a lovely page index.
620 $context['page_index'] = constructPageIndex($scripturl . '?action=profile;u=' . $memID . ';area=showposts;sa=attach;sort=' . $context['sort_order'] . ($context['sort_direction'] == 'up' ? ';asc' : ''), $context['start'], $attachCount, $maxIndex);
621
622 // Retrieve some attachments.
623 $request = $smcFunc['db_query']('', '
624 SELECT a.id_attach, a.id_msg, a.filename, a.downloads, a.approved, m.id_msg, m.id_topic,
625 m.id_board, m.poster_time, m.subject, b.name
626 FROM {db_prefix}attachments AS a
627 INNER JOIN {db_prefix}messages AS m ON (m.id_msg = a.id_msg)
628 INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
629 WHERE a.attachment_type = {int:attachment_type}
630 AND a.id_msg != {int:no_message}
631 AND m.id_member = {int:current_member}' . (!empty($board) ? '
632 AND b.id_board = {int:board}' : '') . (!in_array(0, $boardsAllowed) ? '
633 AND b.id_board IN ({array_int:boards_list})' : '') . (!$modSettings['postmod_active'] || $context['user']['is_owner'] ? '' : '
634 AND m.approved = {int:is_approved}') . '
635 ORDER BY {raw:sort}
636 LIMIT {int:offset}, {int:limit}',
637 array(
638 'boards_list' => $boardsAllowed,
639 'attachment_type' => 0,
640 'no_message' => 0,
641 'current_member' => $memID,
642 'is_approved' => 1,
643 'board' => $board,
644 'sort' => $sort . ' ' . ($context['sort_direction'] == 'down' ? 'DESC' : 'ASC'),
645 'offset' => $context['start'],
646 'limit' => $maxIndex,
647 )
648 );
649 $context['attachments'] = array();
650 while ($row = $smcFunc['db_fetch_assoc']($request))
651 {
652 $row['subject'] = censorText($row['subject']);
653
654 $context['attachments'][] = array(
655 'id' => $row['id_attach'],
656 'filename' => $row['filename'],
657 'downloads' => $row['downloads'],
658 'subject' => $row['subject'],
659 'posted' => timeformat($row['poster_time']),
660 'msg' => $row['id_msg'],
661 'topic' => $row['id_topic'],
662 'board' => $row['id_board'],
663 'board_name' => $row['name'],
664 'approved' => $row['approved'],
665 );
666 }
667 $smcFunc['db_free_result']($request);
668 }
669
670 function statPanel($memID)
671 {
672 global $txt, $scripturl, $context, $user_profile, $user_info, $modSettings, $smcFunc;
673
674 $context['page_title'] = $txt['statPanel_showStats'] . ' ' . $user_profile[$memID]['real_name'];
675
676 // General user statistics.
677 $timeDays = floor($user_profile[$memID]['total_time_logged_in'] / 86400);
678 $timeHours = floor(($user_profile[$memID]['total_time_logged_in'] % 86400) / 3600);
679 $context['time_logged_in'] = ($timeDays > 0 ? $timeDays . $txt['totalTimeLogged2'] : '') . ($timeHours > 0 ? $timeHours . $txt['totalTimeLogged3'] : '') . floor(($user_profile[$memID]['total_time_logged_in'] % 3600) / 60) . $txt['totalTimeLogged4'];
680 $context['num_posts'] = comma_format($user_profile[$memID]['posts']);
681
682 // Number of topics started.
683 $result = $smcFunc['db_query']('', '
684 SELECT COUNT(*)
685 FROM {db_prefix}topics
686 WHERE id_member_started = {int:current_member}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
687 AND id_board != {int:recycle_board}' : ''),
688 array(
689 'current_member' => $memID,
690 'recycle_board' => $modSettings['recycle_board'],
691 )
692 );
693 list ($context['num_topics']) = $smcFunc['db_fetch_row']($result);
694 $smcFunc['db_free_result']($result);
695
696 // Number polls started.
697 $result = $smcFunc['db_query']('', '
698 SELECT COUNT(*)
699 FROM {db_prefix}topics
700 WHERE id_member_started = {int:current_member}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
701 AND id_board != {int:recycle_board}' : '') . '
702 AND id_poll != {int:no_poll}',
703 array(
704 'current_member' => $memID,
705 'recycle_board' => $modSettings['recycle_board'],
706 'no_poll' => 0,
707 )
708 );
709 list ($context['num_polls']) = $smcFunc['db_fetch_row']($result);
710 $smcFunc['db_free_result']($result);
711
712 // Number polls voted in.
713 $result = $smcFunc['db_query']('distinct_poll_votes', '
714 SELECT COUNT(DISTINCT id_poll)
715 FROM {db_prefix}log_polls
716 WHERE id_member = {int:current_member}',
717 array(
718 'current_member' => $memID,
719 )
720 );
721 list ($context['num_votes']) = $smcFunc['db_fetch_row']($result);
722 $smcFunc['db_free_result']($result);
723
724 // Format the numbers...
725 $context['num_topics'] = comma_format($context['num_topics']);
726 $context['num_polls'] = comma_format($context['num_polls']);
727 $context['num_votes'] = comma_format($context['num_votes']);
728
729 // Grab the board this member posted in most often.
730 $result = $smcFunc['db_query']('', '
731 SELECT
732 b.id_board, MAX(b.name) AS name, MAX(b.num_posts) AS num_posts, COUNT(*) AS message_count
733 FROM {db_prefix}messages AS m
734 INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
735 WHERE m.id_member = {int:current_member}
736 AND b.count_posts = {int:count_enabled}
737 AND {query_see_board}
738 GROUP BY b.id_board
739 ORDER BY message_count DESC
740 LIMIT 10',
741 array(
742 'current_member' => $memID,
743 'count_enabled' => 0,
744 )
745 );
746 $context['popular_boards'] = array();
747 while ($row = $smcFunc['db_fetch_assoc']($result))
748 {
749 $context['popular_boards'][$row['id_board']] = array(
750 'id' => $row['id_board'],
751 'posts' => $row['message_count'],
752 'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
753 'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>',
754 'posts_percent' => $user_profile[$memID]['posts'] == 0 ? 0 : ($row['message_count'] * 100) / $user_profile[$memID]['posts'],
755 'total_posts' => $row['num_posts'],
756 'total_posts_member' => $user_profile[$memID]['posts'],
757 );
758 }
759 $smcFunc['db_free_result']($result);
760
761 // Now get the 10 boards this user has most often participated in.
762 $result = $smcFunc['db_query']('profile_board_stats', '
763 SELECT
764 b.id_board, MAX(b.name) AS name, b.num_posts, COUNT(*) AS message_count,
765 CASE WHEN COUNT(*) > MAX(b.num_posts) THEN 1 ELSE COUNT(*) / MAX(b.num_posts) END * 100 AS percentage
766 FROM {db_prefix}messages AS m
767 INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
768 WHERE m.id_member = {int:current_member}
769 AND {query_see_board}
770 GROUP BY b.id_board, b.num_posts
771 ORDER BY percentage DESC
772 LIMIT 10',
773 array(
774 'current_member' => $memID,
775 )
776 );
777 $context['board_activity'] = array();
778 while ($row = $smcFunc['db_fetch_assoc']($result))
779 {
780 $context['board_activity'][$row['id_board']] = array(
781 'id' => $row['id_board'],
782 'posts' => $row['message_count'],
783 'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
784 'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>',
785 'percent' => comma_format((float) $row['percentage'], 2),
786 'posts_percent' => (float) $row['percentage'],
787 'total_posts' => $row['num_posts'],
788 );
789 }
790 $smcFunc['db_free_result']($result);
791
792 // Posting activity by time.
793 $result = $smcFunc['db_query']('user_activity_by_time', '
794 SELECT
795 HOUR(FROM_UNIXTIME(poster_time + {int:time_offset})) AS hour,
796 COUNT(*) AS post_count
797 FROM {db_prefix}messages
798 WHERE id_member = {int:current_member}' . ($modSettings['totalMessages'] > 100000 ? '
799 AND id_topic > {int:top_ten_thousand_topics}' : '') . '
800 GROUP BY hour',
801 array(
802 'current_member' => $memID,
803 'top_ten_thousand_topics' => $modSettings['totalTopics'] - 10000,
804 'time_offset' => (($user_info['time_offset'] + $modSettings['time_offset']) * 3600),
805 )
806 );
807 $maxPosts = $realPosts = 0;
808 $context['posts_by_time'] = array();
809 while ($row = $smcFunc['db_fetch_assoc']($result))
810 {
811 // Cast as an integer to remove the leading 0.
812 $row['hour'] = (int) $row['hour'];
813
814 $maxPosts = max($row['post_count'], $maxPosts);
815 $realPosts += $row['post_count'];
816
817 $context['posts_by_time'][$row['hour']] = array(
818 'hour' => $row['hour'],
819 'hour_format' => stripos($user_info['time_format'], '%p') === false ? $row['hour'] : date('g a', mktime($row['hour'])),
820 'posts' => $row['post_count'],
821 'posts_percent' => 0,
822 'is_last' => $row['hour'] == 23,
823 );
824 }
825 $smcFunc['db_free_result']($result);
826
827 if ($maxPosts > 0)
828 for ($hour = 0; $hour < 24; $hour++)
829 {
830 if (!isset($context['posts_by_time'][$hour]))
831 $context['posts_by_time'][$hour] = array(
832 'hour' => $hour,
833 'hour_format' => stripos($user_info['time_format'], '%p') === false ? $hour : date('g a', mktime($hour)),
834 'posts' => 0,
835 'posts_percent' => 0,
836 'relative_percent' => 0,
837 'is_last' => $hour == 23,
838 );
839 else
840 {
841 $context['posts_by_time'][$hour]['posts_percent'] = round(($context['posts_by_time'][$hour]['posts'] * 100) / $realPosts);
842 $context['posts_by_time'][$hour]['relative_percent'] = round(($context['posts_by_time'][$hour]['posts'] * 100) / $maxPosts);
843 }
844 }
845
846 // Put it in the right order.
847 ksort($context['posts_by_time']);
848 }
849
850 function tracking($memID)
851 {
852 global $sourcedir, $context, $txt, $scripturl, $modSettings, $user_profile;
853
854 $subActions = array(
855 'activity' => array('trackActivity', $txt['trackActivity']),
856 'ip' => array('TrackIP', $txt['trackIP']),
857 'edits' => array('trackEdits', $txt['trackEdits']),
858 );
859
860 $context['tracking_area'] = isset($_GET['sa']) && isset($subActions[$_GET['sa']]) ? $_GET['sa'] : 'activity';
861
862 if (isset($types[$context['tracking_area']][1]))
863 require_once($sourcedir . '/' . $types[$context['tracking_area']][1]);
864
865 // Create the tabs for the template.
866 $context[$context['profile_menu_name']]['tab_data'] = array(
867 'title' => $txt['tracking'],
868 'description' => $txt['tracking_description'],
869 'icon' => 'profile_sm.gif',
870 'tabs' => array(
871 'activity' => array(),
872 'ip' => array(),
873 'edits' => array(),
874 ),
875 );
876
877 // Moderation must be on to track edits.
878 if (empty($modSettings['modlog_enabled']))
879 unset($context[$context['profile_menu_name']]['tab_data']['edits']);
880
881 // Set a page title.
882 $context['page_title'] = $txt['trackUser'] . ' - ' . $subActions[$context['tracking_area']][1] . ' - ' . $user_profile[$memID]['real_name'];
883
884 // Pass on to the actual function.
885 $context['sub_template'] = $subActions[$context['tracking_area']][0];
886 $subActions[$context['tracking_area']][0]($memID);
887 }
888
889 function trackActivity($memID)
890 {
891 global $scripturl, $txt, $modSettings, $sourcedir;
892 global $user_profile, $context, $smcFunc;
893
894 // Verify if the user has sufficient permissions.
895 isAllowedTo('moderate_forum');
896
897 $context['last_ip'] = $user_profile[$memID]['member_ip'];
898 if ($context['last_ip'] != $user_profile[$memID]['member_ip2'])
899 $context['last_ip2'] = $user_profile[$memID]['member_ip2'];
900 $context['member']['name'] = $user_profile[$memID]['real_name'];
901
902 // Set the options for the list component.
903 $listOptions = array(
904 'id' => 'track_user_list',
905 'title' => $txt['errors_by'] . ' ' . $context['member']['name'],
906 'items_per_page' => $modSettings['defaultMaxMessages'],
907 'no_items_label' => $txt['no_errors_from_user'],
908 'base_href' => $scripturl . '?action=profile;area=tracking;sa=user;u=' . $memID,
909 'default_sort_col' => 'date',
910 'get_items' => array(
911 'function' => 'list_getUserErrors',
912 'params' => array(
913 'le.id_member = {int:current_member}',
914 array('current_member' => $memID),
915 ),
916 ),
917 'get_count' => array(
918 'function' => 'list_getUserErrorCount',
919 'params' => array(
920 'id_member = {int:current_member}',
921 array('current_member' => $memID),
922 ),
923 ),
924 'columns' => array(
925 'ip_address' => array(
926 'header' => array(
927 'value' => $txt['ip_address'],
928 ),
929 'data' => array(
930 'sprintf' => array(
931 'format' => '<a href="' . $scripturl . '?action=profile;area=tracking;sa=ip;searchip=%1$s;u=' . $memID. '">%1$s</a>',
932 'params' => array(
933 'ip' => false,
934 ),
935 ),
936 ),
937 'sort' => array(
938 'default' => 'le.ip',
939 'reverse' => 'le.ip DESC',
940 ),
941 ),
942 'message' => array(
943 'header' => array(
944 'value' => $txt['message'],
945 ),
946 'data' => array(
947 'sprintf' => array(
948 'format' => '%1$s<br /><a href="%2$s">%2$s</a>',
949 'params' => array(
950 'message' => false,
951 'url' => false,
952 ),
953 ),
954 ),
955 ),
956 'date' => array(
957 'header' => array(
958 'value' => $txt['date'],
959 ),
960 'data' => array(
961 'db' => 'time',
962 ),
963 'sort' => array(
964 'default' => 'le.id_error DESC',
965 'reverse' => 'le.id_error',
966 ),
967 ),
968 ),
969 'additional_rows' => array(
970 array(
971 'position' => 'after_title',
972 'value' => $txt['errors_desc'],
973 'class' => 'smalltext',
974 'style' => 'padding: 2ex;',
975 ),
976 ),
977 );
978
979 // Create the list for viewing.
980 require_once($sourcedir . '/Subs-List.php');
981 createList($listOptions);
982
983 // If this is a big forum, or a large posting user, let's limit the search.
984 if ($modSettings['totalMessages'] > 50000 && $user_profile[$memID]['posts'] > 500)
985 {
986 $request = $smcFunc['db_query']('', '
987 SELECT MAX(id_msg)
988 FROM {db_prefix}messages AS m
989 WHERE m.id_member = {int:current_member}',
990 array(
991 'current_member' => $memID,
992 )
993 );
994 list ($max_msg_member) = $smcFunc['db_fetch_row']($request);
995 $smcFunc['db_free_result']($request);
996
997 // There's no point worrying ourselves with messages made yonks ago, just get recent ones!
998 $min_msg_member = max(0, $max_msg_member - $user_profile[$memID]['posts'] * 3);
999 }
1000
1001 // Default to at least the ones we know about.
1002 $ips = array(
1003 $user_profile[$memID]['member_ip'],
1004 $user_profile[$memID]['member_ip2'],
1005 );
1006
1007 // Get all IP addresses this user has used for his messages.
1008 $request = $smcFunc['db_query']('', '
1009 SELECT poster_ip
1010 FROM {db_prefix}messages
1011 WHERE id_member = {int:current_member}
1012 ' . (isset($min_msg_member) ? '
1013 AND id_msg >= {int:min_msg_member} AND id_msg <= {int:max_msg_member}' : '') . '
1014 GROUP BY poster_ip',
1015 array(
1016 'current_member' => $memID,
1017 'min_msg_member' => !empty($min_msg_member) ? $min_msg_member : 0,
1018 'max_msg_member' => !empty($max_msg_member) ? $max_msg_member : 0,
1019 )
1020 );
1021 $context['ips'] = array();
1022 while ($row = $smcFunc['db_fetch_assoc']($request))
1023 {
1024 $context['ips'][] = '<a href="' . $scripturl . '?action=profile;area=tracking;sa=ip;searchip=' . $row['poster_ip'] . ';u=' . $memID . '">' . $row['poster_ip'] . '</a>';
1025 $ips[] = $row['poster_ip'];
1026 }
1027 $smcFunc['db_free_result']($request);
1028
1029 // Now also get the IP addresses from the error messages.
1030 $request = $smcFunc['db_query']('', '
1031 SELECT COUNT(*) AS error_count, ip
1032 FROM {db_prefix}log_errors
1033 WHERE id_member = {int:current_member}
1034 GROUP BY ip',
1035 array(
1036 'current_member' => $memID,
1037 )
1038 );
1039 $context['error_ips'] = array();
1040 while ($row = $smcFunc['db_fetch_assoc']($request))
1041 {
1042 $context['error_ips'][] = '<a href="' . $scripturl . '?action=profile;area=tracking;sa=ip;searchip=' . $row['ip'] . ';u=' . $memID . '">' . $row['ip'] . '</a>';
1043 $ips[] = $row['ip'];
1044 }
1045 $smcFunc['db_free_result']($request);
1046
1047 // Find other users that might use the same IP.
1048 $ips = array_unique($ips);
1049 $context['members_in_range'] = array();
1050 if (!empty($ips))
1051 {
1052 // Get member ID's which are in messages...
1053 $request = $smcFunc['db_query']('', '
1054 SELECT mem.id_member
1055 FROM {db_prefix}messages AS m
1056 INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1057 WHERE m.poster_ip IN ({array_string:ip_list})
1058 GROUP BY mem.id_member
1059 HAVING mem.id_member != {int:current_member}',
1060 array(
1061 'current_member' => $memID,
1062 'ip_list' => $ips,
1063 )
1064 );
1065 $message_members = array();
1066 while ($row = $smcFunc['db_fetch_assoc']($request))
1067 $message_members[] = $row['id_member'];
1068 $smcFunc['db_free_result']($request);
1069
1070 // Fetch their names, cause of the GROUP BY doesn't like giving us that normally.
1071 if (!empty($message_members))
1072 {
1073 $request = $smcFunc['db_query']('', '
1074 SELECT id_member, real_name
1075 FROM {db_prefix}members
1076 WHERE id_member IN ({array_int:message_members})',
1077 array(
1078 'message_members' => $message_members,
1079 'ip_list' => $ips,
1080 )
1081 );
1082 while ($row = $smcFunc['db_fetch_assoc']($request))
1083 $context['members_in_range'][$row['id_member']] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>';
1084 $smcFunc['db_free_result']($request);
1085 }
1086
1087 $request = $smcFunc['db_query']('', '
1088 SELECT id_member, real_name
1089 FROM {db_prefix}members
1090 WHERE id_member != {int:current_member}
1091 AND member_ip IN ({array_string:ip_list})',
1092 array(
1093 'current_member' => $memID,
1094 'ip_list' => $ips,
1095 )
1096 );
1097 while ($row = $smcFunc['db_fetch_assoc']($request))
1098 $context['members_in_range'][$row['id_member']] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>';
1099 $smcFunc['db_free_result']($request);
1100 }
1101 }
1102
1103 function list_getUserErrorCount($where, $where_vars = array())
1104 {
1105 global $smcFunc;
1106
1107 $request = $smcFunc['db_query']('', '
1108 SELECT COUNT(*) AS error_count
1109 FROM {db_prefix}log_errors
1110 WHERE ' . $where,
1111 $where_vars
1112 );
1113 list ($count) = $smcFunc['db_fetch_row']($request);
1114 $smcFunc['db_free_result']($request);
1115
1116 return $count;
1117 }
1118
1119 function list_getUserErrors($start, $items_per_page, $sort, $where, $where_vars = array())
1120 {
1121 global $smcFunc, $txt, $scripturl;
1122
1123 // Get a list of error messages from this ip (range).
1124 $request = $smcFunc['db_query']('', '
1125 SELECT
1126 le.log_time, le.ip, le.url, le.message, IFNULL(mem.id_member, 0) AS id_member,
1127 IFNULL(mem.real_name, {string:guest_title}) AS display_name, mem.member_name
1128 FROM {db_prefix}log_errors AS le
1129 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = le.id_member)
1130 WHERE ' . $where . '
1131 ORDER BY ' . $sort . '
1132 LIMIT ' . $start . ', ' . $items_per_page,
1133 array_merge($where_vars, array(
1134 'guest_title' => $txt['guest_title'],
1135 ))
1136 );
1137 $error_messages = array();
1138 while ($row = $smcFunc['db_fetch_assoc']($request))
1139 $error_messages[] = array(
1140 'ip' => $row['ip'],
1141 'member_link' => $row['id_member'] > 0 ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['display_name'] . '</a>' : $row['display_name'],
1142 'message' => strtr($row['message'], array('&lt;span class=&quot;remove&quot;&gt;' => '', '&lt;/span&gt;' => '')),
1143 'url' => $row['url'],
1144 'time' => timeformat($row['log_time']),
1145 'timestamp' => forum_time(true, $row['log_time']),
1146 );
1147 $smcFunc['db_free_result']($request);
1148
1149 return $error_messages;
1150 }
1151
1152 function list_getIPMessageCount($where, $where_vars = array())
1153 {
1154 global $smcFunc;
1155
1156 $request = $smcFunc['db_query']('', '
1157 SELECT COUNT(*) AS message_count
1158 FROM {db_prefix}messages AS m
1159 INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1160 WHERE {query_see_board} AND ' . $where,
1161 $where_vars
1162 );
1163 list ($count) = $smcFunc['db_fetch_row']($request);
1164 $smcFunc['db_free_result']($request);
1165
1166 return $count;
1167 }
1168
1169 function list_getIPMessages($start, $items_per_page, $sort, $where, $where_vars = array())
1170 {
1171 global $smcFunc, $txt, $scripturl;
1172
1173 // Get all the messages fitting this where clause.
1174 // !!!SLOW This query is using a filesort.
1175 $request = $smcFunc['db_query']('', '
1176 SELECT
1177 m.id_msg, m.poster_ip, IFNULL(mem.real_name, m.poster_name) AS display_name, mem.id_member,
1178 m.subject, m.poster_time, m.id_topic, m.id_board
1179 FROM {db_prefix}messages AS m
1180 INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1181 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
1182 WHERE {query_see_board} AND ' . $where . '
1183 ORDER BY ' . $sort . '
1184 LIMIT ' . $start . ', ' . $items_per_page,
1185 array_merge($where_vars, array(
1186 ))
1187 );
1188 $messages = array();
1189 while ($row = $smcFunc['db_fetch_assoc']($request))
1190 $messages[] = array(
1191 'ip' => $row['poster_ip'],
1192 'member_link' => empty($row['id_member']) ? $row['display_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['display_name'] . '</a>',
1193 'board' => array(
1194 'id' => $row['id_board'],
1195 'href' => $scripturl . '?board=' . $row['id_board']
1196 ),
1197 'topic' => $row['id_topic'],
1198 'id' => $row['id_msg'],
1199 'subject' => $row['subject'],
1200 'time' => timeformat($row['poster_time']),
1201 'timestamp' => forum_time(true, $row['poster_time'])
1202 );
1203 $smcFunc['db_free_result']($request);
1204
1205 return $messages;
1206 }
1207
1208 function TrackIP($memID = 0)
1209 {
1210 global $user_profile, $scripturl, $txt, $user_info, $modSettings, $sourcedir;
1211 global $context, $smcFunc;
1212
1213 // Can the user do this?
1214 isAllowedTo('moderate_forum');
1215
1216 if ($memID == 0)
1217 {
1218 $context['ip'] = $user_info['ip'];
1219 loadTemplate('Profile');
1220 loadLanguage('Profile');
1221 $context['sub_template'] = 'trackIP';
1222 $context['page_title'] = $txt['profile'];
1223 $context['base_url'] = $scripturl . '?action=trackip';
1224 }
1225 else
1226 {
1227 $context['ip'] = $user_profile[$memID]['member_ip'];
1228 $context['base_url'] = $scripturl . '?action=profile;area=tracking;sa=ip;u=' . $memID;
1229 }
1230
1231 // Searching?
1232 if (isset($_REQUEST['searchip']))
1233 $context['ip'] = trim($_REQUEST['searchip']);
1234
1235 if (preg_match('/^\d{1,3}\.(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)$/', $context['ip']) == 0)
1236 fatal_lang_error('invalid_tracking_ip', false);
1237
1238 $ip_var = str_replace('*', '%', $context['ip']);
1239 $ip_string = strpos($ip_var, '%') === false ? '= {string:ip_address}' : 'LIKE {string:ip_address}';
1240
1241 if (empty($context['tracking_area']))
1242 $context['page_title'] = $txt['trackIP'] . ' - ' . $context['ip'];
1243
1244 $request = $smcFunc['db_query']('', '
1245 SELECT id_member, real_name AS display_name, member_ip
1246 FROM {db_prefix}members
1247 WHERE member_ip ' . $ip_string,
1248 array(
1249 'ip_address' => $ip_var,
1250 )
1251 );
1252 $context['ips'] = array();
1253 while ($row = $smcFunc['db_fetch_assoc']($request))
1254 $context['ips'][$row['member_ip']][] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['display_name'] . '</a>';
1255 $smcFunc['db_free_result']($request);
1256
1257 ksort($context['ips']);
1258
1259 // Gonna want this for the list.
1260 require_once($sourcedir . '/Subs-List.php');
1261
1262 // Start with the user messages.
1263 $listOptions = array(
1264 'id' => 'track_message_list',
1265 'title' => $txt['messages_from_ip'] . ' ' . $context['ip'],
1266 'start_var_name' => 'messageStart',
1267 'items_per_page' => $modSettings['defaultMaxMessages'],
1268 'no_items_label' => $txt['no_messages_from_ip'],
1269 'base_href' => $context['base_url'] . ';searchip=' . $context['ip'],
1270 'default_sort_col' => 'date',
1271 'get_items' => array(
1272 'function' => 'list_getIPMessages',
1273 'params' => array(
1274 'm.poster_ip ' . $ip_string,
1275 array('ip_address' => $ip_var),
1276 ),
1277 ),
1278 'get_count' => array(
1279 'function' => 'list_getIPMessageCount',
1280 'params' => array(
1281 'm.poster_ip ' . $ip_string,
1282 array('ip_address' => $ip_var),
1283 ),
1284 ),
1285 'columns' => array(
1286 'ip_address' => array(
1287 'header' => array(
1288 'value' => $txt['ip_address'],
1289 ),
1290 'data' => array(
1291 'sprintf' => array(
1292 'format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a>',
1293 'params' => array(
1294 'ip' => false,
1295 ),
1296 ),
1297 ),
1298 'sort' => array(
1299 'default' => 'INET_ATON(m.poster_ip)',
1300 'reverse' => 'INET_ATON(m.poster_ip) DESC',
1301 ),
1302 ),
1303 'poster' => array(
1304 'header' => array(
1305 'value' => $txt['poster'],
1306 ),
1307 'data' => array(
1308 'db' => 'member_link',
1309 ),
1310 ),
1311 'subject' => array(
1312 'header' => array(
1313 'value' => $txt['subject'],
1314 ),
1315 'data' => array(
1316 'sprintf' => array(
1317 'format' => '<a href="' . $scripturl . '?topic=%1$s.msg%2$s#msg%2$s" rel="nofollow">%3$s</a>',
1318 'params' => array(
1319 'topic' => false,
1320 'id' => false,
1321 'subject' => false,
1322 ),
1323 ),
1324 ),
1325 ),
1326 'date' => array(
1327 'header' => array(
1328 'value' => $txt['date'],
1329 ),
1330 'data' => array(
1331 'db' => 'time',
1332 ),
1333 'sort' => array(
1334 'default' => 'm.id_msg DESC',
1335 'reverse' => 'm.id_msg',
1336 ),
1337 ),
1338 ),
1339 'additional_rows' => array(
1340 array(
1341 'position' => 'after_title',
1342 'value' => $txt['messages_from_ip_desc'],
1343 'class' => 'smalltext',
1344 'style' => 'padding: 2ex;',
1345 ),
1346 ),
1347 );
1348
1349 // Create the messages list.
1350 createList($listOptions);
1351
1352 // Set the options for the error lists.
1353 $listOptions = array(
1354 'id' => 'track_user_list',
1355 'title' => $txt['errors_from_ip'] . ' ' . $context['ip'],
1356 'start_var_name' => 'errorStart',
1357 'items_per_page' => $modSettings['defaultMaxMessages'],
1358 'no_items_label' => $txt['no_errors_from_ip'],
1359 'base_href' => $context['base_url'] . ';searchip=' . $context['ip'],
1360 'default_sort_col' => 'date2',
1361 'get_items' => array(
1362 'function' => 'list_getUserErrors',
1363 'params' => array(
1364 'le.ip ' . $ip_string,
1365 array('ip_address' => $ip_var),
1366 ),
1367 ),
1368 'get_count' => array(
1369 'function' => 'list_getUserErrorCount',
1370 'params' => array(
1371 'ip ' . $ip_string,
1372 array('ip_address' => $ip_var),
1373 ),
1374 ),
1375 'columns' => array(
1376 'ip_address2' => array(
1377 'header' => array(
1378 'value' => $txt['ip_address'],
1379 ),
1380 'data' => array(
1381 'sprintf' => array(
1382 'format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a>',
1383 'params' => array(
1384 'ip' => false,
1385 ),
1386 ),
1387 ),
1388 'sort' => array(
1389 'default' => 'INET_ATON(le.ip)',
1390 'reverse' => 'INET_ATON(le.ip) DESC',
1391 ),
1392 ),
1393 'display_name' => array(
1394 'header' => array(
1395 'value' => $txt['display_name'],
1396 ),
1397 'data' => array(
1398 'db' => 'member_link',
1399 ),
1400 ),
1401 'message' => array(
1402 'header' => array(
1403 'value' => $txt['message'],
1404 ),
1405 'data' => array(
1406 'sprintf' => array(
1407 'format' => '%1$s<br /><a href="%2$s">%2$s</a>',
1408 'params' => array(
1409 'message' => false,
1410 'url' => false,
1411 ),
1412 ),
1413 ),
1414 ),
1415 'date2' => array(
1416 'header' => array(
1417 'value' => $txt['date'],
1418 ),
1419 'data' => array(
1420 'db' => 'time',
1421 ),
1422 'sort' => array(
1423 'default' => 'le.id_error DESC',
1424 'reverse' => 'le.id_error',
1425 ),
1426 ),
1427 ),
1428 'additional_rows' => array(
1429 array(
1430 'position' => 'after_title',
1431 'value' => $txt['errors_from_ip_desc'],
1432 'class' => 'smalltext',
1433 'style' => 'padding: 2ex;',
1434 ),
1435 ),
1436 );
1437
1438 // Create the error list.
1439 createList($listOptions);
1440
1441 $context['single_ip'] = strpos($context['ip'], '*') === false;
1442 if ($context['single_ip'])
1443 {
1444 $context['whois_servers'] = array(
1445 'afrinic' => array(
1446 'name' => $txt['whois_afrinic'],
1447 'url' => 'http://www.afrinic.net/cgi-bin/whois?searchtext=' . $context['ip'],
1448 'range' => array(41, 154, 196),
1449 ),
1450 'apnic' => array(
1451 'name' => $txt['whois_apnic'],
1452 'url' => 'http://wq.apnic.net/apnic-bin/whois.pl?searchtext=' . $context['ip'],
1453 'range' => array(58, 59, 60, 61, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
1454 125, 126, 133, 150, 153, 163, 171, 202, 203, 210, 211, 218, 219, 220, 221, 222),
1455 ),
1456 'arin' => array(
1457 'name' => $txt['whois_arin'],
1458 'url' => 'http://whois.arin.net/rest/ip/' . $context['ip'],
1459 'range' => array(7, 24, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 96, 97, 98, 99,
1460 128, 129, 130, 131, 132, 134, 135, 136, 137, 138, 139, 140, 142, 143, 144, 146, 147, 148, 149,
1461 152, 155, 156, 157, 158, 159, 160, 161, 162, 164, 165, 166, 167, 168, 169, 170, 172, 173, 174,
1462 192, 198, 199, 204, 205, 206, 207, 208, 209, 216),
1463 ),
1464 'lacnic' => array(
1465 'name' => $txt['whois_lacnic'],
1466 'url' => 'http://lacnic.net/cgi-bin/lacnic/whois?query=' . $context['ip'],
1467 'range' => array(186, 187, 189, 190, 191, 200, 201),
1468 ),
1469 'ripe' => array(
1470 'name' => $txt['whois_ripe'],
1471 'url' => 'http://www.db.ripe.net/whois?searchtext=' . $context['ip'],
1472 'range' => array(62, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
1473 141, 145, 151, 188, 193, 194, 195, 212, 213, 217),
1474 ),
1475 );
1476
1477 foreach ($context['whois_servers'] as $whois)
1478 {
1479 // Strip off the "decimal point" and anything following...
1480 if (in_array((int) $context['ip'], $whois['range']))
1481 $context['auto_whois_server'] = $whois;
1482 }
1483 }
1484 }
1485
1486 function trackEdits($memID)
1487 {
1488 global $scripturl, $txt, $modSettings, $sourcedir, $context, $smcFunc;
1489
1490 require_once($sourcedir . '/Subs-List.php');
1491
1492 // Get the names of any custom fields.
1493 $request = $smcFunc['db_query']('', '
1494 SELECT col_name, field_name, bbc
1495 FROM {db_prefix}custom_fields',
1496 array(
1497 )
1498 );
1499 $context['custom_field_titles'] = array();
1500 while ($row = $smcFunc['db_fetch_assoc']($request))
1501 $context['custom_field_titles']['customfield_' . $row['col_name']] = array(
1502 'title' => $row['field_name'],
1503 'parse_bbc' => $row['bbc'],
1504 );
1505 $smcFunc['db_free_result']($request);
1506
1507 // Set the options for the error lists.
1508 $listOptions = array(
1509 'id' => 'edit_list',
1510 'title' => $txt['trackEdits'],
1511 'items_per_page' => $modSettings['defaultMaxMessages'],
1512 'no_items_label' => $txt['trackEdit_no_edits'],
1513 'base_href' => $scripturl . '?action=profile;area=tracking;sa=edits;u=' . $memID,
1514 'default_sort_col' => 'time',
1515 'get_items' => array(
1516 'function' => 'list_getProfileEdits',
1517 'params' => array(
1518 $memID,
1519 ),
1520 ),
1521 'get_count' => array(
1522 'function' => 'list_getProfileEditCount',
1523 'params' => array(
1524 $memID,
1525 ),
1526 ),
1527 'columns' => array(
1528 'action' => array(
1529 'header' => array(
1530 'value' => $txt['trackEdit_action'],
1531 ),
1532 'data' => array(
1533 'db' => 'action_text',
1534 ),
1535 ),
1536 'before' => array(
1537 'header' => array(
1538 'value' => $txt['trackEdit_before'],
1539 ),
1540 'data' => array(
1541 'db' => 'before',
1542 ),
1543 ),
1544 'after' => array(
1545 'header' => array(
1546 'value' => $txt['trackEdit_after'],
1547 ),
1548 'data' => array(
1549 'db' => 'after',
1550 ),
1551 ),
1552 'time' => array(
1553 'header' => array(
1554 'value' => $txt['date'],
1555 ),
1556 'data' => array(
1557 'db' => 'time',
1558 ),
1559 'sort' => array(
1560 'default' => 'id_action DESC',
1561 'reverse' => 'id_action',
1562 ),
1563 ),
1564 'applicator' => array(
1565 'header' => array(
1566 'value' => $txt['trackEdit_applicator'],
1567 ),
1568 'data' => array(
1569 'db' => 'member_link',
1570 ),
1571 ),
1572 ),
1573 );
1574
1575 // Create the error list.
1576 createList($listOptions);
1577
1578 $context['sub_template'] = 'show_list';
1579 $context['default_list'] = 'edit_list';
1580 }
1581
1582 // How many edits?
1583 function list_getProfileEditCount($memID)
1584 {
1585 global $smcFunc;
1586
1587 $request = $smcFunc['db_query']('', '
1588 SELECT COUNT(*) AS edit_count
1589 FROM {db_prefix}log_actions
1590 WHERE id_log = {int:log_type}
1591 AND id_member = {int:owner}',
1592 array(
1593 'log_type' => 2,
1594 'owner' => $memID,
1595 )
1596 );
1597 list ($edit_count) = $smcFunc['db_fetch_row']($request);
1598 $smcFunc['db_free_result']($request);
1599
1600 return $edit_count;
1601 }
1602
1603 function list_getProfileEdits($start, $items_per_page, $sort, $memID)
1604 {
1605 global $smcFunc, $txt, $scripturl, $context;
1606
1607 // Get a list of error messages from this ip (range).
1608 $request = $smcFunc['db_query']('', '
1609 SELECT
1610 id_action, id_member, ip, log_time, action, extra
1611 FROM {db_prefix}log_actions
1612 WHERE id_log = {int:log_type}
1613 AND id_member = {int:owner}
1614 ORDER BY ' . $sort . '
1615 LIMIT ' . $start . ', ' . $items_per_page,
1616 array(
1617 'log_type' => 2,
1618 'owner' => $memID,
1619 )
1620 );
1621 $edits = array();
1622 $members = array();
1623 while ($row = $smcFunc['db_fetch_assoc']($request))
1624 {
1625 $extra = @unserialize($row['extra']);
1626 if (!empty($extra['applicator']))
1627 $members[] = $extra['applicator'];
1628
1629 // Work out what the name of the action is.
1630 if (isset($txt['trackEdit_action_' . $row['action']]))
1631 $action_text = $txt['trackEdit_action_' . $row['action']];
1632 elseif (isset($txt[$row['action']]))
1633 $action_text = $txt[$row['action']];
1634 // Custom field?
1635 elseif (isset($context['custom_field_titles'][$row['action']]))
1636 $action_text = $context['custom_field_titles'][$row['action']]['title'];
1637 else
1638 $action_text = $row['action'];
1639
1640 // Parse BBC?
1641 $parse_bbc = isset($context['custom_field_titles'][$row['action']]) && $context['custom_field_titles'][$row['action']]['parse_bbc'] ? true : false;
1642
1643 $edits[] = array(
1644 'id' => $row['id_action'],
1645 'ip' => $row['ip'],
1646 'id_member' => !empty($extra['applicator']) ? $extra['applicator'] : 0,
1647 'member_link' => $txt['trackEdit_deleted_member'],
1648 'action' => $row['action'],
1649 'action_text' => $action_text,
1650 'before' => !empty($extra['previous']) ? ($parse_bbc ? parse_bbc($extra['previous']) : $extra['previous']) : '',
1651 'after' => !empty($extra['new']) ? ($parse_bbc ? parse_bbc($extra['new']) : $extra['new']) : '',
1652 'time' => timeformat($row['log_time']),
1653 );
1654 }
1655 $smcFunc['db_free_result']($request);
1656
1657 // Get any member names.
1658 if (!empty($members))
1659 {
1660 $request = $smcFunc['db_query']('', '
1661 SELECT
1662 id_member, real_name
1663 FROM {db_prefix}members
1664 WHERE id_member IN ({array_int:members})',
1665 array(
1666 'members' => $members,
1667 )
1668 );
1669 $members = array();
1670 while ($row = $smcFunc['db_fetch_assoc']($request))
1671 $members[$row['id_member']] = $row['real_name'];
1672 $smcFunc['db_free_result']($request);
1673
1674 foreach ($edits as $key => $value)
1675 if (isset($members[$value['id_member']]))
1676 $edits[$key]['member_link'] = '<a href="' . $scripturl . '?action=profile;u=' . $value['id_member'] . '">' . $members[$value['id_member']] . '</a>';
1677 }
1678
1679 return $edits;
1680 }
1681
1682 function showPermissions($memID)
1683 {
1684 global $scripturl, $txt, $board, $modSettings;
1685 global $user_profile, $context, $user_info, $sourcedir, $smcFunc;
1686
1687 // Verify if the user has sufficient permissions.
1688 isAllowedTo('manage_permissions');
1689
1690 loadLanguage('ManagePermissions');
1691 loadLanguage('Admin');
1692 loadTemplate('ManageMembers');
1693
1694 // Load all the permission profiles.
1695 require_once($sourcedir . '/ManagePermissions.php');
1696 loadPermissionProfiles();
1697
1698 $context['member']['id'] = $memID;
1699 $context['member']['name'] = $user_profile[$memID]['real_name'];
1700
1701 $context['page_title'] = $txt['showPermissions'];
1702 $board = empty($board) ? 0 : (int) $board;
1703 $context['board'] = $board;
1704
1705 // Determine which groups this user is in.
1706 if (empty($user_profile[$memID]['additional_groups']))
1707 $curGroups = array();
1708 else
1709 $curGroups = explode(',', $user_profile[$memID]['additional_groups']);
1710 $curGroups[] = $user_profile[$memID]['id_group'];
1711 $curGroups[] = $user_profile[$memID]['id_post_group'];
1712
1713 // Load a list of boards for the jump box - except the defaults.
1714 $request = $smcFunc['db_query']('order_by_board_order', '
1715 SELECT b.id_board, b.name, b.id_profile, b.member_groups, IFNULL(mods.id_member, 0) AS is_mod
1716 FROM {db_prefix}boards AS b
1717 LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})
1718 WHERE {query_see_board}',
1719 array(
1720 'current_member' => $memID,
1721 )
1722 );
1723 $context['boards'] = array();
1724 $context['no_access_boards'] = array();
1725 while ($row = $smcFunc['db_fetch_assoc']($request))
1726 {
1727 if (count(array_intersect($curGroups, explode(',', $row['member_groups']))) === 0 && !$row['is_mod'])
1728 $context['no_access_boards'][] = array(
1729 'id' => $row['id_board'],
1730 'name' => $row['name'],
1731 'is_last' => false,
1732 );
1733 elseif ($row['id_profile'] != 1 || $row['is_mod'])
1734 $context['boards'][$row['id_board']] = array(
1735 'id' => $row['id_board'],
1736 'name' => $row['name'],
1737 'selected' => $board == $row['id_board'],
1738 'profile' => $row['id_profile'],
1739 'profile_name' => $context['profiles'][$row['id_profile']]['name'],
1740 );
1741 }
1742 $smcFunc['db_free_result']($request);
1743
1744 if (!empty($context['no_access_boards']))
1745 $context['no_access_boards'][count($context['no_access_boards']) - 1]['is_last'] = true;
1746
1747 $context['member']['permissions'] = array(
1748 'general' => array(),
1749 'board' => array()
1750 );
1751
1752 // If you're an admin we know you can do everything, we might as well leave.
1753 $context['member']['has_all_permissions'] = in_array(1, $curGroups);
1754 if ($context['member']['has_all_permissions'])
1755 return;
1756
1757 $denied = array();
1758
1759 // Get all general permissions.
1760 $result = $smcFunc['db_query']('', '
1761 SELECT p.permission, p.add_deny, mg.group_name, p.id_group
1762 FROM {db_prefix}permissions AS p
1763 LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = p.id_group)
1764 WHERE p.id_group IN ({array_int:group_list})
1765 ORDER BY p.add_deny DESC, p.permission, mg.min_posts, CASE WHEN mg.id_group < {int:newbie_group} THEN mg.id_group ELSE 4 END, mg.group_name',
1766 array(
1767 'group_list' => $curGroups,
1768 'newbie_group' => 4,
1769 )
1770 );
1771 while ($row = $smcFunc['db_fetch_assoc']($result))
1772 {
1773 // We don't know about this permission, it doesn't exist :P.
1774 if (!isset($txt['permissionname_' . $row['permission']]))
1775 continue;
1776
1777 if (empty($row['add_deny']))
1778 $denied[] = $row['permission'];
1779
1780 // Permissions that end with _own or _any consist of two parts.
1781 if (in_array(substr($row['permission'], -4), array('_own', '_any')) && isset($txt['permissionname_' . substr($row['permission'], 0, -4)]))
1782 $name = $txt['permissionname_' . substr($row['permission'], 0, -4)] . ' - ' . $txt['permissionname_' . $row['permission']];
1783 else
1784 $name = $txt['permissionname_' . $row['permission']];
1785
1786 // Add this permission if it doesn't exist yet.
1787 if (!isset($context['member']['permissions']['general'][$row['permission']]))
1788 $context['member']['permissions']['general'][$row['permission']] = array(
1789 'id' => $row['permission'],
1790 'groups' => array(
1791 'allowed' => array(),
1792 'denied' => array()
1793 ),
1794 'name' => $name,
1795 'is_denied' => false,
1796 'is_global' => true,
1797 );
1798
1799 // Add the membergroup to either the denied or the allowed groups.
1800 $context['member']['permissions']['general'][$row['permission']]['groups'][empty($row['add_deny']) ? 'denied' : 'allowed'][] = $row['id_group'] == 0 ? $txt['membergroups_members'] : $row['group_name'];
1801
1802 // Once denied is always denied.
1803 $context['member']['permissions']['general'][$row['permission']]['is_denied'] |= empty($row['add_deny']);
1804 }
1805 $smcFunc['db_free_result']($result);
1806
1807 $request = $smcFunc['db_query']('', '
1808 SELECT
1809 bp.add_deny, bp.permission, bp.id_group, mg.group_name' . (empty($board) ? '' : ',
1810 b.id_profile, CASE WHEN mods.id_member IS NULL THEN 0 ELSE 1 END AS is_moderator') . '
1811 FROM {db_prefix}board_permissions AS bp' . (empty($board) ? '' : '
1812 INNER JOIN {db_prefix}boards AS b ON (b.id_board = {int:current_board})
1813 LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})') . '
1814 LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = bp.id_group)
1815 WHERE bp.id_profile = {raw:current_profile}
1816 AND bp.id_group IN ({array_int:group_list}' . (empty($board) ? ')' : ', {int:moderator_group})
1817 AND (mods.id_member IS NOT NULL OR bp.id_group != {int:moderator_group})'),
1818 array(
1819 'current_board' => $board,
1820 'group_list' => $curGroups,
1821 'current_member' => $memID,
1822 'current_profile' => empty($board) ? '1' : 'b.id_profile',
1823 'moderator_group' => 3,
1824 )
1825 );
1826
1827 while ($row = $smcFunc['db_fetch_assoc']($request))
1828 {
1829 // We don't know about this permission, it doesn't exist :P.
1830 if (!isset($txt['permissionname_' . $row['permission']]))
1831 continue;
1832
1833 // The name of the permission using the format 'permission name' - 'own/any topic/event/etc.'.
1834 if (in_array(substr($row['permission'], -4), array('_own', '_any')) && isset($txt['permissionname_' . substr($row['permission'], 0, -4)]))
1835 $name = $txt['permissionname_' . substr($row['permission'], 0, -4)] . ' - ' . $txt['permissionname_' . $row['permission']];
1836 else
1837 $name = $txt['permissionname_' . $row['permission']];
1838
1839 // Create the structure for this permission.
1840 if (!isset($context['member']['permissions']['board'][$row['permission']]))
1841 $context['member']['permissions']['board'][$row['permission']] = array(
1842 'id' => $row['permission'],
1843 'groups' => array(
1844 'allowed' => array(),
1845 'denied' => array()
1846 ),
1847 'name' => $name,
1848 'is_denied' => false,
1849 'is_global' => empty($board),
1850 );
1851
1852 $context['member']['permissions']['board'][$row['permission']]['groups'][empty($row['add_deny']) ? 'denied' : 'allowed'][$row['id_group']] = $row['id_group'] == 0 ? $txt['membergroups_members'] : $row['group_name'];
1853
1854 $context['member']['permissions']['board'][$row['permission']]['is_denied'] |= empty($row['add_deny']);
1855 }
1856 $smcFunc['db_free_result']($request);
1857 }
1858
1859 // View a members warnings?
1860 function viewWarning($memID)
1861 {
1862 global $modSettings, $context, $sourcedir, $txt, $scripturl;
1863
1864 // Firstly, can we actually even be here?
1865 if (!allowedTo('issue_warning') && (empty($modSettings['warning_show']) || ($modSettings['warning_show'] == 1 && !$context['user']['is_owner'])))
1866 fatal_lang_error('no_access', false);
1867
1868 // Make sure things which are disabled stay disabled.
1869 $modSettings['warning_watch'] = !empty($modSettings['warning_watch']) ? $modSettings['warning_watch'] : 110;
1870 $modSettings['warning_moderate'] = !empty($modSettings['warning_moderate']) && !empty($modSettings['postmod_active']) ? $modSettings['warning_moderate'] : 110;
1871 $modSettings['warning_mute'] = !empty($modSettings['warning_mute']) ? $modSettings['warning_mute'] : 110;
1872
1873 // Let's use a generic list to get all the current warnings, and use the issue warnings grab-a-granny thing.
1874 require_once($sourcedir . '/Subs-List.php');
1875 require_once($sourcedir . '/Profile-Actions.php');
1876
1877 $listOptions = array(
1878 'id' => 'view_warnings',
1879 'title' => $txt['profile_viewwarning_previous_warnings'],
1880 'items_per_page' => $modSettings['defaultMaxMessages'],
1881 'no_items_label' => $txt['profile_viewwarning_no_warnings'],
1882 'base_href' => $scripturl . '?action=profile;area=viewwarning;sa=user;u=' . $memID,
1883 'default_sort_col' => 'log_time',
1884 'get_items' => array(
1885 'function' => 'list_getUserWarnings',
1886 'params' => array(
1887 $memID,
1888 ),
1889 ),
1890 'get_count' => array(
1891 'function' => 'list_getUserWarningCount',
1892 'params' => array(
1893 $memID,
1894 ),
1895 ),
1896 'columns' => array(
1897 'log_time' => array(
1898 'header' => array(
1899 'value' => $txt['profile_warning_previous_time'],
1900 ),
1901 'data' => array(
1902 'db' => 'time',
1903 ),
1904 'sort' => array(
1905 'default' => 'lc.log_time DESC',
1906 'reverse' => 'lc.log_time',
1907 ),
1908 ),
1909 'reason' => array(
1910 'header' => array(
1911 'value' => $txt['profile_warning_previous_reason'],
1912 'style' => 'width: 50%',
1913 ),
1914 'data' => array(
1915 'db' => 'reason',
1916 ),
1917 ),
1918 'level' => array(
1919 'header' => array(
1920 'value' => $txt['profile_warning_previous_level'],
1921 ),
1922 'data' => array(
1923 'db' => 'counter',
1924 ),
1925 'sort' => array(
1926 'default' => 'lc.counter DESC',
1927 'reverse' => 'lc.counter',
1928 ),
1929 ),
1930 ),
1931 'additional_rows' => array(
1932 array(
1933 'position' => 'after_title',
1934 'value' => $txt['profile_viewwarning_desc'],
1935 'class' => 'smalltext',
1936 'style' => 'padding: 2ex;',
1937 ),
1938 ),
1939 );
1940
1941 // Create the list for viewing.
1942 require_once($sourcedir . '/Subs-List.php');
1943 createList($listOptions);
1944
1945 // Create some common text bits for the template.
1946 $context['level_effects'] = array(
1947 0 => '',
1948 $modSettings['warning_watch'] => $txt['profile_warning_effect_own_watched'],
1949 $modSettings['warning_moderate'] => $txt['profile_warning_effect_own_moderated'],
1950 $modSettings['warning_mute'] => $txt['profile_warning_effect_own_muted'],
1951 );
1952 $context['current_level'] = 0;
1953 foreach ($context['level_effects'] as $limit => $dummy)
1954 if ($context['member']['warning'] >= $limit)
1955 $context['current_level'] = $limit;
1956 }
1957
1958 ?>