PageRenderTime 42ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/sources/controllers/ProfileHistory.controller.php

https://github.com/Arantor/Elkarte
PHP | 1074 lines | 838 code | 80 blank | 156 comment | 38 complexity | 1f9d1e71e373a22b1aa2bc7c46edd178 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0
  1. <?php
  2. /**
  3. * @name ElkArte Forum
  4. * @copyright ElkArte Forum contributors
  5. * @license BSD http://opensource.org/licenses/BSD-3-Clause
  6. *
  7. * This software is a derived product, based on:
  8. *
  9. * Simple Machines Forum (SMF)
  10. * copyright: 2011 Simple Machines (http://www.simplemachines.org)
  11. * license: BSD, See included LICENSE.TXT for terms and conditions.
  12. *
  13. * @version 1.0 Alpha
  14. *
  15. */
  16. if (!defined('ELKARTE'))
  17. die('No access...');
  18. /**
  19. * Profile history main function.
  20. * Re-directs to sub-actions (@todo it should only set the context)
  21. *
  22. * @param int $memID id_member
  23. */
  24. function action_history($memID)
  25. {
  26. global $context, $txt, $scripturl, $modSettings, $user_profile;
  27. $subActions = array(
  28. 'activity' => array('action_trackactivity', $txt['trackActivity']),
  29. 'ip' => array('action_trackip', $txt['trackIP']),
  30. 'edits' => array('action_trackedits', $txt['trackEdits']),
  31. 'logins' => array('action_tracklogin', $txt['trackLogins']),
  32. );
  33. $context['history_area'] = isset($_GET['sa']) && isset($subActions[$_GET['sa']]) ? $_GET['sa'] : 'activity';
  34. // @todo what is $types? it is never set so this will never be true
  35. if (isset($types[$context['history_area']][1]))
  36. require_once(SOURCEDIR . '/' . $types[$context['history_area']][1]);
  37. // Create the tabs for the template.
  38. $context[$context['profile_menu_name']]['tab_data'] = array(
  39. 'title' => $txt['history'],
  40. 'description' => $txt['history_description'],
  41. 'icon' => 'profile_hd.png',
  42. 'tabs' => array(
  43. 'activity' => array(),
  44. 'ip' => array(),
  45. 'edits' => array(),
  46. ),
  47. );
  48. // Moderation must be on to track edits.
  49. if (empty($modSettings['modlog_enabled']))
  50. unset($context[$context['profile_menu_name']]['tab_data']['edits']);
  51. // Set a page title.
  52. $context['page_title'] = $txt['trackUser'] . ' - ' . $subActions[$context['history_area']][1] . ' - ' . $user_profile[$memID]['real_name'];
  53. // Pass on to the actual function.
  54. $subActions[$context['history_area']][0]($memID);
  55. }
  56. /**
  57. * Subaction for profile history actions: activity log.
  58. *
  59. * @param int $memID id_member
  60. */
  61. function action_trackactivity($memID)
  62. {
  63. global $scripturl, $txt, $modSettings;
  64. global $user_profile, $context, $smcFunc;
  65. // Verify if the user has sufficient permissions.
  66. isAllowedTo('moderate_forum');
  67. $context['last_ip'] = $user_profile[$memID]['member_ip'];
  68. if ($context['last_ip'] != $user_profile[$memID]['member_ip2'])
  69. $context['last_ip2'] = $user_profile[$memID]['member_ip2'];
  70. $context['member']['name'] = $user_profile[$memID]['real_name'];
  71. // Set the options for the list component.
  72. $listOptions = array(
  73. 'id' => 'track_user_list',
  74. 'title' => $txt['errors_by'] . ' ' . $context['member']['name'],
  75. 'items_per_page' => $modSettings['defaultMaxMessages'],
  76. 'no_items_label' => $txt['no_errors_from_user'],
  77. 'base_href' => $scripturl . '?action=profile;area=history;sa=user;u=' . $memID,
  78. 'default_sort_col' => 'date',
  79. 'get_items' => array(
  80. 'function' => 'list_getUserErrors',
  81. 'params' => array(
  82. 'le.id_member = {int:current_member}',
  83. array('current_member' => $memID),
  84. ),
  85. ),
  86. 'get_count' => array(
  87. 'function' => 'list_getUserErrorCount',
  88. 'params' => array(
  89. 'id_member = {int:current_member}',
  90. array('current_member' => $memID),
  91. ),
  92. ),
  93. 'columns' => array(
  94. 'ip_address' => array(
  95. 'header' => array(
  96. 'value' => $txt['ip_address'],
  97. ),
  98. 'data' => array(
  99. 'sprintf' => array(
  100. 'format' => '<a href="' . $scripturl . '?action=profile;area=history;sa=ip;searchip=%1$s;u=' . $memID. '">%1$s</a>',
  101. 'params' => array(
  102. 'ip' => false,
  103. ),
  104. ),
  105. ),
  106. 'sort' => array(
  107. 'default' => 'le.ip',
  108. 'reverse' => 'le.ip DESC',
  109. ),
  110. ),
  111. 'message' => array(
  112. 'header' => array(
  113. 'value' => $txt['message'],
  114. ),
  115. 'data' => array(
  116. 'sprintf' => array(
  117. 'format' => '%1$s<br /><a href="%2$s">%2$s</a>',
  118. 'params' => array(
  119. 'message' => false,
  120. 'url' => false,
  121. ),
  122. ),
  123. ),
  124. ),
  125. 'date' => array(
  126. 'header' => array(
  127. 'value' => $txt['date'],
  128. ),
  129. 'data' => array(
  130. 'db' => 'time',
  131. ),
  132. 'sort' => array(
  133. 'default' => 'le.id_error DESC',
  134. 'reverse' => 'le.id_error',
  135. ),
  136. ),
  137. ),
  138. 'additional_rows' => array(
  139. array(
  140. 'position' => 'after_title',
  141. 'value' => $txt['errors_desc'],
  142. 'class' => 'windowbg2',
  143. 'style' => 'padding: 1ex 2ex;',
  144. ),
  145. ),
  146. );
  147. // Create the list for viewing.
  148. require_once(SUBSDIR . '/List.subs.php');
  149. createList($listOptions);
  150. // @todo cache this
  151. // If this is a big forum, or a large posting user, let's limit the search.
  152. if ($modSettings['totalMessages'] > 50000 && $user_profile[$memID]['posts'] > 500)
  153. {
  154. $request = $smcFunc['db_query']('', '
  155. SELECT MAX(id_msg)
  156. FROM {db_prefix}messages AS m
  157. WHERE m.id_member = {int:current_member}',
  158. array(
  159. 'current_member' => $memID,
  160. )
  161. );
  162. list ($max_msg_member) = $smcFunc['db_fetch_row']($request);
  163. $smcFunc['db_free_result']($request);
  164. // There's no point worrying ourselves with messages made yonks ago, just get recent ones!
  165. $min_msg_member = max(0, $max_msg_member - $user_profile[$memID]['posts'] * 3);
  166. }
  167. // Default to at least the ones we know about.
  168. $ips = array(
  169. $user_profile[$memID]['member_ip'],
  170. $user_profile[$memID]['member_ip2'],
  171. );
  172. // @todo cache this
  173. // Get all IP addresses this user has used for his messages.
  174. $request = $smcFunc['db_query']('', '
  175. SELECT poster_ip
  176. FROM {db_prefix}messages
  177. WHERE id_member = {int:current_member}
  178. ' . (isset($min_msg_member) ? '
  179. AND id_msg >= {int:min_msg_member} AND id_msg <= {int:max_msg_member}' : '') . '
  180. GROUP BY poster_ip',
  181. array(
  182. 'current_member' => $memID,
  183. 'min_msg_member' => !empty($min_msg_member) ? $min_msg_member : 0,
  184. 'max_msg_member' => !empty($max_msg_member) ? $max_msg_member : 0,
  185. )
  186. );
  187. $context['ips'] = array();
  188. while ($row = $smcFunc['db_fetch_assoc']($request))
  189. {
  190. $context['ips'][] = '<a href="' . $scripturl . '?action=profile;area=history;sa=ip;searchip=' . $row['poster_ip'] . ';u=' . $memID . '">' . $row['poster_ip'] . '</a>';
  191. $ips[] = $row['poster_ip'];
  192. }
  193. $smcFunc['db_free_result']($request);
  194. // Now also get the IP addresses from the error messages.
  195. $request = $smcFunc['db_query']('', '
  196. SELECT COUNT(*) AS error_count, ip
  197. FROM {db_prefix}log_errors
  198. WHERE id_member = {int:current_member}
  199. GROUP BY ip',
  200. array(
  201. 'current_member' => $memID,
  202. )
  203. );
  204. $context['error_ips'] = array();
  205. while ($row = $smcFunc['db_fetch_assoc']($request))
  206. {
  207. $context['error_ips'][] = '<a href="' . $scripturl . '?action=profile;area=history;sa=ip;searchip=' . $row['ip'] . ';u=' . $memID . '">' . $row['ip'] . '</a>';
  208. $ips[] = $row['ip'];
  209. }
  210. $smcFunc['db_free_result']($request);
  211. // Find other users that might use the same IP.
  212. $ips = array_unique($ips);
  213. $context['members_in_range'] = array();
  214. if (!empty($ips))
  215. {
  216. // Get member ID's which are in messages...
  217. $request = $smcFunc['db_query']('', '
  218. SELECT mem.id_member
  219. FROM {db_prefix}messages AS m
  220. INNER JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
  221. WHERE m.poster_ip IN ({array_string:ip_list})
  222. GROUP BY mem.id_member
  223. HAVING mem.id_member != {int:current_member}',
  224. array(
  225. 'current_member' => $memID,
  226. 'ip_list' => $ips,
  227. )
  228. );
  229. $message_members = array();
  230. while ($row = $smcFunc['db_fetch_assoc']($request))
  231. $message_members[] = $row['id_member'];
  232. $smcFunc['db_free_result']($request);
  233. // Fetch their names, cause of the GROUP BY doesn't like giving us that normally.
  234. if (!empty($message_members))
  235. {
  236. $request = $smcFunc['db_query']('', '
  237. SELECT id_member, real_name
  238. FROM {db_prefix}members
  239. WHERE id_member IN ({array_int:message_members})',
  240. array(
  241. 'message_members' => $message_members,
  242. 'ip_list' => $ips,
  243. )
  244. );
  245. while ($row = $smcFunc['db_fetch_assoc']($request))
  246. $context['members_in_range'][$row['id_member']] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>';
  247. $smcFunc['db_free_result']($request);
  248. }
  249. $request = $smcFunc['db_query']('', '
  250. SELECT id_member, real_name
  251. FROM {db_prefix}members
  252. WHERE id_member != {int:current_member}
  253. AND member_ip IN ({array_string:ip_list})',
  254. array(
  255. 'current_member' => $memID,
  256. 'ip_list' => $ips,
  257. )
  258. );
  259. while ($row = $smcFunc['db_fetch_assoc']($request))
  260. $context['members_in_range'][$row['id_member']] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>';
  261. $smcFunc['db_free_result']($request);
  262. }
  263. $context['sub_template'] = 'trackActivity';
  264. }
  265. /**
  266. * Get the number of user errors.
  267. * Callback for createList in action_trackip() and action_trackactivity()
  268. *
  269. * @param string $where
  270. * @param array $where_vars = array()
  271. * @return string number of user errors
  272. */
  273. function list_getUserErrorCount($where, $where_vars = array())
  274. {
  275. global $smcFunc;
  276. $request = $smcFunc['db_query']('', '
  277. SELECT COUNT(*) AS error_count
  278. FROM {db_prefix}log_errors
  279. WHERE ' . $where,
  280. $where_vars
  281. );
  282. list ($count) = $smcFunc['db_fetch_row']($request);
  283. $smcFunc['db_free_result']($request);
  284. // @todo cast this to an integer
  285. return $count;
  286. }
  287. /**
  288. * Callback for createList in action_trackip() and action_trackactivity()
  289. *
  290. * @param int $start
  291. * @param int $items_per_page
  292. * @param string $sort
  293. * @param string $where
  294. * @param array $where_vars
  295. * @return array error messages
  296. */
  297. function list_getUserErrors($start, $items_per_page, $sort, $where, $where_vars = array())
  298. {
  299. global $smcFunc, $txt, $scripturl;
  300. // Get a list of error messages from this ip (range).
  301. $request = $smcFunc['db_query']('', '
  302. SELECT
  303. le.log_time, le.ip, le.url, le.message, IFNULL(mem.id_member, 0) AS id_member,
  304. IFNULL(mem.real_name, {string:guest_title}) AS display_name, mem.member_name
  305. FROM {db_prefix}log_errors AS le
  306. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = le.id_member)
  307. WHERE ' . $where . '
  308. ORDER BY ' . $sort . '
  309. LIMIT ' . $start . ', ' . $items_per_page,
  310. array_merge($where_vars, array(
  311. 'guest_title' => $txt['guest_title'],
  312. ))
  313. );
  314. $error_messages = array();
  315. while ($row = $smcFunc['db_fetch_assoc']($request))
  316. $error_messages[] = array(
  317. 'ip' => $row['ip'],
  318. 'member_link' => $row['id_member'] > 0 ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['display_name'] . '</a>' : $row['display_name'],
  319. 'message' => strtr($row['message'], array('&lt;span class=&quot;remove&quot;&gt;' => '', '&lt;/span&gt;' => '')),
  320. 'url' => $row['url'],
  321. 'time' => timeformat($row['log_time']),
  322. 'timestamp' => forum_time(true, $row['log_time']),
  323. );
  324. $smcFunc['db_free_result']($request);
  325. return $error_messages;
  326. }
  327. /**
  328. * Callback for createList() in TrackIP()
  329. *
  330. * @param string $where
  331. * @param array $where_vars
  332. * @return string count of messages matching the IP
  333. */
  334. function list_getIPMessageCount($where, $where_vars = array())
  335. {
  336. global $smcFunc;
  337. $request = $smcFunc['db_query']('', '
  338. SELECT COUNT(*) AS message_count
  339. FROM {db_prefix}messages AS m
  340. INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
  341. WHERE {query_see_board} AND ' . $where,
  342. $where_vars
  343. );
  344. list ($count) = $smcFunc['db_fetch_row']($request);
  345. $smcFunc['db_free_result']($request);
  346. // @todo cast to integer
  347. return $count;
  348. }
  349. /**
  350. * Callback for createList() in TrackIP()
  351. *
  352. * @param int $start
  353. * @param int $items_per_page
  354. * @param string $sort
  355. * @param string $where
  356. * @param array $where_vars
  357. * @return array an array of messages
  358. */
  359. function list_getIPMessages($start, $items_per_page, $sort, $where, $where_vars = array())
  360. {
  361. global $smcFunc, $txt, $scripturl;
  362. // Get all the messages fitting this where clause.
  363. // @todo SLOW This query is using a filesort.
  364. $request = $smcFunc['db_query']('', '
  365. SELECT
  366. m.id_msg, m.poster_ip, IFNULL(mem.real_name, m.poster_name) AS display_name, mem.id_member,
  367. m.subject, m.poster_time, m.id_topic, m.id_board
  368. FROM {db_prefix}messages AS m
  369. INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
  370. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
  371. WHERE {query_see_board} AND ' . $where . '
  372. ORDER BY ' . $sort . '
  373. LIMIT ' . $start . ', ' . $items_per_page,
  374. array_merge($where_vars, array(
  375. ))
  376. );
  377. $messages = array();
  378. while ($row = $smcFunc['db_fetch_assoc']($request))
  379. $messages[] = array(
  380. 'ip' => $row['poster_ip'],
  381. 'member_link' => empty($row['id_member']) ? $row['display_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['display_name'] . '</a>',
  382. 'board' => array(
  383. 'id' => $row['id_board'],
  384. 'href' => $scripturl . '?board=' . $row['id_board']
  385. ),
  386. 'topic' => $row['id_topic'],
  387. 'id' => $row['id_msg'],
  388. 'subject' => $row['subject'],
  389. 'time' => timeformat($row['poster_time']),
  390. 'timestamp' => forum_time(true, $row['poster_time'])
  391. );
  392. $smcFunc['db_free_result']($request);
  393. return $messages;
  394. }
  395. /**
  396. * Track an IP address.
  397. * Accessed through ?action=trackip
  398. * and through ?action=profile;area=history;sa=ip
  399. *
  400. * @param int $memID = 0 id_member
  401. */
  402. function action_trackip($memID = 0)
  403. {
  404. global $user_profile, $scripturl, $txt, $user_info, $modSettings;
  405. global $context, $smcFunc;
  406. // Can the user do this?
  407. isAllowedTo('moderate_forum');
  408. if ($memID == 0)
  409. {
  410. $context['ip'] = $user_info['ip'];
  411. loadTemplate('Profile');
  412. loadLanguage('Profile');
  413. $context['sub_template'] = 'trackIP';
  414. $context['page_title'] = $txt['profile'];
  415. $context['base_url'] = $scripturl . '?action=trackip';
  416. }
  417. else
  418. {
  419. $context['ip'] = $user_profile[$memID]['member_ip'];
  420. $context['base_url'] = $scripturl . '?action=profile;area=history;sa=ip;u=' . $memID;
  421. }
  422. // Searching?
  423. if (isset($_REQUEST['searchip']))
  424. $context['ip'] = trim($_REQUEST['searchip']);
  425. if (preg_match('/^\d{1,3}\.(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)$/', $context['ip']) == 0 && isValidIPv6($context['ip']) === false)
  426. fatal_lang_error('invalid_tracking_ip', false);
  427. $ip_var = str_replace('*', '%', $context['ip']);
  428. $ip_string = strpos($ip_var, '%') === false ? '= {string:ip_address}' : 'LIKE {string:ip_address}';
  429. if (empty($context['history_area']))
  430. $context['page_title'] = $txt['trackIP'] . ' - ' . $context['ip'];
  431. $request = $smcFunc['db_query']('', '
  432. SELECT id_member, real_name AS display_name, member_ip
  433. FROM {db_prefix}members
  434. WHERE member_ip ' . $ip_string,
  435. array(
  436. 'ip_address' => $ip_var,
  437. )
  438. );
  439. $context['ips'] = array();
  440. while ($row = $smcFunc['db_fetch_assoc']($request))
  441. $context['ips'][$row['member_ip']][] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['display_name'] . '</a>';
  442. $smcFunc['db_free_result']($request);
  443. ksort($context['ips']);
  444. // Gonna want this for the list.
  445. require_once(SUBSDIR . '/List.subs.php');
  446. // Start with the user messages.
  447. $listOptions = array(
  448. 'id' => 'track_message_list',
  449. 'title' => $txt['messages_from_ip'] . ' ' . $context['ip'],
  450. 'start_var_name' => 'messageStart',
  451. 'items_per_page' => $modSettings['defaultMaxMessages'],
  452. 'no_items_label' => $txt['no_messages_from_ip'],
  453. 'base_href' => $context['base_url'] . ';searchip=' . $context['ip'],
  454. 'default_sort_col' => 'date',
  455. 'get_items' => array(
  456. 'function' => 'list_getIPMessages',
  457. 'params' => array(
  458. 'm.poster_ip ' . $ip_string,
  459. array('ip_address' => $ip_var),
  460. ),
  461. ),
  462. 'get_count' => array(
  463. 'function' => 'list_getIPMessageCount',
  464. 'params' => array(
  465. 'm.poster_ip ' . $ip_string,
  466. array('ip_address' => $ip_var),
  467. ),
  468. ),
  469. 'columns' => array(
  470. 'ip_address' => array(
  471. 'header' => array(
  472. 'value' => $txt['ip_address'],
  473. ),
  474. 'data' => array(
  475. 'sprintf' => array(
  476. 'format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a>',
  477. 'params' => array(
  478. 'ip' => false,
  479. ),
  480. ),
  481. ),
  482. 'sort' => array(
  483. 'default' => 'INET_ATON(m.poster_ip)',
  484. 'reverse' => 'INET_ATON(m.poster_ip) DESC',
  485. ),
  486. ),
  487. 'poster' => array(
  488. 'header' => array(
  489. 'value' => $txt['poster'],
  490. ),
  491. 'data' => array(
  492. 'db' => 'member_link',
  493. ),
  494. ),
  495. 'subject' => array(
  496. 'header' => array(
  497. 'value' => $txt['subject'],
  498. ),
  499. 'data' => array(
  500. 'sprintf' => array(
  501. 'format' => '<a href="' . $scripturl . '?topic=%1$s.msg%2$s#msg%2$s" rel="nofollow">%3$s</a>',
  502. 'params' => array(
  503. 'topic' => false,
  504. 'id' => false,
  505. 'subject' => false,
  506. ),
  507. ),
  508. ),
  509. ),
  510. 'date' => array(
  511. 'header' => array(
  512. 'value' => $txt['date'],
  513. ),
  514. 'data' => array(
  515. 'db' => 'time',
  516. ),
  517. 'sort' => array(
  518. 'default' => 'm.id_msg DESC',
  519. 'reverse' => 'm.id_msg',
  520. ),
  521. ),
  522. ),
  523. 'additional_rows' => array(
  524. array(
  525. 'position' => 'after_title',
  526. 'value' => $txt['messages_from_ip_desc'],
  527. 'class' => 'windowbg2',
  528. 'style' => 'padding: 1ex 2ex;',
  529. ),
  530. ),
  531. );
  532. // Create the messages list.
  533. createList($listOptions);
  534. // Set the options for the error lists.
  535. $listOptions = array(
  536. 'id' => 'track_user_list',
  537. 'title' => $txt['errors_from_ip'] . ' ' . $context['ip'],
  538. 'start_var_name' => 'errorStart',
  539. 'items_per_page' => $modSettings['defaultMaxMessages'],
  540. 'no_items_label' => $txt['no_errors_from_ip'],
  541. 'base_href' => $context['base_url'] . ';searchip=' . $context['ip'],
  542. 'default_sort_col' => 'date2',
  543. 'get_items' => array(
  544. 'function' => 'list_getUserErrors',
  545. 'params' => array(
  546. 'le.ip ' . $ip_string,
  547. array('ip_address' => $ip_var),
  548. ),
  549. ),
  550. 'get_count' => array(
  551. 'function' => 'list_getUserErrorCount',
  552. 'params' => array(
  553. 'ip ' . $ip_string,
  554. array('ip_address' => $ip_var),
  555. ),
  556. ),
  557. 'columns' => array(
  558. 'ip_address2' => array(
  559. 'header' => array(
  560. 'value' => $txt['ip_address'],
  561. ),
  562. 'data' => array(
  563. 'sprintf' => array(
  564. 'format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a>',
  565. 'params' => array(
  566. 'ip' => false,
  567. ),
  568. ),
  569. ),
  570. 'sort' => array(
  571. 'default' => 'INET_ATON(le.ip)',
  572. 'reverse' => 'INET_ATON(le.ip) DESC',
  573. ),
  574. ),
  575. 'display_name' => array(
  576. 'header' => array(
  577. 'value' => $txt['display_name'],
  578. ),
  579. 'data' => array(
  580. 'db' => 'member_link',
  581. ),
  582. ),
  583. 'message' => array(
  584. 'header' => array(
  585. 'value' => $txt['message'],
  586. ),
  587. 'data' => array(
  588. 'sprintf' => array(
  589. 'format' => '%1$s<br /><a href="%2$s">%2$s</a>',
  590. 'params' => array(
  591. 'message' => false,
  592. 'url' => false,
  593. ),
  594. ),
  595. ),
  596. ),
  597. 'date2' => array(
  598. 'header' => array(
  599. 'value' => $txt['date'],
  600. ),
  601. 'data' => array(
  602. 'db' => 'time',
  603. ),
  604. 'sort' => array(
  605. 'default' => 'le.id_error DESC',
  606. 'reverse' => 'le.id_error',
  607. ),
  608. ),
  609. ),
  610. 'additional_rows' => array(
  611. array(
  612. 'position' => 'after_title',
  613. 'value' => $txt['errors_from_ip_desc'],
  614. 'class' => 'windowbg2',
  615. 'style' => 'padding: 1ex 2ex;',
  616. ),
  617. ),
  618. );
  619. // Create the error list.
  620. createList($listOptions);
  621. $context['single_ip'] = strpos($context['ip'], '*') === false;
  622. if ($context['single_ip'])
  623. {
  624. $context['whois_servers'] = array(
  625. 'afrinic' => array(
  626. 'name' => $txt['whois_afrinic'],
  627. 'url' => 'http://www.afrinic.net/cgi-bin/whois?searchtext=' . $context['ip'],
  628. 'range' => array(41, 154, 196),
  629. ),
  630. 'apnic' => array(
  631. 'name' => $txt['whois_apnic'],
  632. 'url' => 'http://wq.apnic.net/apnic-bin/whois.pl?searchtext=' . $context['ip'],
  633. 'range' => array(58, 59, 60, 61, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
  634. 125, 126, 133, 150, 153, 163, 171, 202, 203, 210, 211, 218, 219, 220, 221, 222),
  635. ),
  636. 'arin' => array(
  637. 'name' => $txt['whois_arin'],
  638. 'url' => 'http://whois.arin.net/rest/ip/' . $context['ip'],
  639. 'range' => array(7, 24, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 96, 97, 98, 99,
  640. 128, 129, 130, 131, 132, 134, 135, 136, 137, 138, 139, 140, 142, 143, 144, 146, 147, 148, 149,
  641. 152, 155, 156, 157, 158, 159, 160, 161, 162, 164, 165, 166, 167, 168, 169, 170, 172, 173, 174,
  642. 192, 198, 199, 204, 205, 206, 207, 208, 209, 216),
  643. ),
  644. 'lacnic' => array(
  645. 'name' => $txt['whois_lacnic'],
  646. 'url' => 'http://lacnic.net/cgi-bin/lacnic/whois?query=' . $context['ip'],
  647. 'range' => array(186, 187, 189, 190, 191, 200, 201),
  648. ),
  649. 'ripe' => array(
  650. 'name' => $txt['whois_ripe'],
  651. 'url' => 'http://www.db.ripe.net/whois?searchtext=' . $context['ip'],
  652. 'range' => array(62, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
  653. 141, 145, 151, 188, 193, 194, 195, 212, 213, 217),
  654. ),
  655. );
  656. foreach ($context['whois_servers'] as $whois)
  657. {
  658. // Strip off the "decimal point" and anything following...
  659. if (in_array((int) $context['ip'], $whois['range']))
  660. $context['auto_whois_server'] = $whois;
  661. }
  662. }
  663. $context['sub_template'] = 'trackIP';
  664. }
  665. /**
  666. * Tracks the logins of a given user.
  667. * Accessed by ?action=trackip
  668. * and ?action=profile;area=history;sa=ip
  669. *
  670. * @param int $memID = 0 id_member
  671. */
  672. function action_tracklogin($memID = 0)
  673. {
  674. global $user_profile, $scripturl, $txt, $user_info, $modSettings;
  675. global $context, $smcFunc;
  676. // Gonna want this for the list.
  677. require_once(SUBSDIR . '/List.subs.php');
  678. if ($memID == 0)
  679. $context['base_url'] = $scripturl . '?action=trackip';
  680. else
  681. $context['base_url'] = $scripturl . '?action=profile;area=history;sa=ip;u=' . $memID;
  682. // Start with the user messages.
  683. $listOptions = array(
  684. 'id' => 'track_logins_list',
  685. 'title' => $txt['trackLogins'],
  686. 'no_items_label' => $txt['trackLogins_none_found'],
  687. 'base_href' => $context['base_url'],
  688. 'get_items' => array(
  689. 'function' => 'list_getLogins',
  690. 'params' => array(
  691. 'id_member = {int:current_member}',
  692. array('current_member' => $memID),
  693. ),
  694. ),
  695. 'get_count' => array(
  696. 'function' => 'list_getLoginCount',
  697. 'params' => array(
  698. 'id_member = {int:current_member}',
  699. array('current_member' => $memID),
  700. ),
  701. ),
  702. 'columns' => array(
  703. 'time' => array(
  704. 'header' => array(
  705. 'value' => $txt['date'],
  706. ),
  707. 'data' => array(
  708. 'db' => 'time',
  709. ),
  710. ),
  711. 'ip' => array(
  712. 'header' => array(
  713. 'value' => $txt['ip_address'],
  714. ),
  715. 'data' => array(
  716. 'sprintf' => array(
  717. 'format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a> (<a href="' . $context['base_url'] . ';searchip=%2$s">%2$s</a>) ',
  718. 'params' => array(
  719. 'ip' => false,
  720. 'ip2' => false
  721. ),
  722. ),
  723. ),
  724. ),
  725. ),
  726. 'additional_rows' => array(
  727. array(
  728. 'position' => 'after_title',
  729. 'value' => $txt['trackLogins_desc'],
  730. 'class' => 'windowbg2',
  731. 'style' => 'padding: 1ex 2ex;',
  732. ),
  733. ),
  734. );
  735. // Create the messages list.
  736. createList($listOptions);
  737. $context['sub_template'] = 'show_list';
  738. $context['default_list'] = 'track_logins_list';
  739. }
  740. /**
  741. * Callback for trackLogins for counting history.
  742. * (createList() in TrackLogins())
  743. *
  744. * @param string $where
  745. * @param array $where_vars
  746. * @return string count of messages matching the IP
  747. */
  748. function list_getLoginCount($where, $where_vars = array())
  749. {
  750. global $smcFunc;
  751. $request = $smcFunc['db_query']('', '
  752. SELECT COUNT(*) AS message_count
  753. FROM {db_prefix}member_logins
  754. WHERE id_member = {int:id_member}',
  755. array(
  756. 'id_member' => $where_vars['current_member'],
  757. )
  758. );
  759. list ($count) = $smcFunc['db_fetch_row']($request);
  760. $smcFunc['db_free_result']($request);
  761. // @todo cast to integer
  762. return $count;
  763. }
  764. /**
  765. * Callback for trackLogins data.
  766. *
  767. * @param int $start
  768. * @param int $items_per_page
  769. * @param string $sort
  770. * @param string $where
  771. * @param array $where_vars
  772. * @return array an array of messages
  773. */
  774. function list_getLogins($start, $items_per_page, $sort, $where, $where_vars = array())
  775. {
  776. global $smcFunc, $txt, $scripturl;
  777. $request = $smcFunc['db_query']('', '
  778. SELECT time, ip, ip2
  779. FROM {db_prefix}member_logins
  780. WHERE {int:id_member}
  781. ORDER BY time DESC',
  782. array(
  783. 'id_member' => $where_vars['current_member'],
  784. )
  785. );
  786. $logins = array();
  787. while ($row = $smcFunc['db_fetch_assoc']($request))
  788. $logins[] = array(
  789. 'time' => timeformat($row['time']),
  790. 'ip' => $row['ip'],
  791. 'ip2' => $row['ip2'],
  792. );
  793. $smcFunc['db_free_result']($request);
  794. return $logins;
  795. }
  796. /**
  797. * Logs edits to a members profile.
  798. *
  799. * @param int $memID id_member
  800. */
  801. function action_trackedits($memID)
  802. {
  803. global $scripturl, $txt, $modSettings, $context, $smcFunc;
  804. require_once(SUBSDIR . '/List.subs.php');
  805. // Get the names of any custom fields.
  806. $request = $smcFunc['db_query']('', '
  807. SELECT col_name, field_name, bbc
  808. FROM {db_prefix}custom_fields',
  809. array(
  810. )
  811. );
  812. $context['custom_field_titles'] = array();
  813. while ($row = $smcFunc['db_fetch_assoc']($request))
  814. $context['custom_field_titles']['customfield_' . $row['col_name']] = array(
  815. 'title' => $row['field_name'],
  816. 'parse_bbc' => $row['bbc'],
  817. );
  818. $smcFunc['db_free_result']($request);
  819. // Set the options for the error lists.
  820. $listOptions = array(
  821. 'id' => 'edit_list',
  822. 'title' => $txt['trackEdits'],
  823. 'items_per_page' => $modSettings['defaultMaxMessages'],
  824. 'no_items_label' => $txt['trackEdit_no_edits'],
  825. 'base_href' => $scripturl . '?action=profile;area=history;sa=edits;u=' . $memID,
  826. 'default_sort_col' => 'time',
  827. 'get_items' => array(
  828. 'function' => 'list_getProfileEdits',
  829. 'params' => array(
  830. $memID,
  831. ),
  832. ),
  833. 'get_count' => array(
  834. 'function' => 'list_getProfileEditCount',
  835. 'params' => array(
  836. $memID,
  837. ),
  838. ),
  839. 'columns' => array(
  840. 'action' => array(
  841. 'header' => array(
  842. 'value' => $txt['trackEdit_action'],
  843. ),
  844. 'data' => array(
  845. 'db' => 'action_text',
  846. ),
  847. ),
  848. 'before' => array(
  849. 'header' => array(
  850. 'value' => $txt['trackEdit_before'],
  851. ),
  852. 'data' => array(
  853. 'db' => 'before',
  854. ),
  855. ),
  856. 'after' => array(
  857. 'header' => array(
  858. 'value' => $txt['trackEdit_after'],
  859. ),
  860. 'data' => array(
  861. 'db' => 'after',
  862. ),
  863. ),
  864. 'time' => array(
  865. 'header' => array(
  866. 'value' => $txt['date'],
  867. ),
  868. 'data' => array(
  869. 'db' => 'time',
  870. ),
  871. 'sort' => array(
  872. 'default' => 'id_action DESC',
  873. 'reverse' => 'id_action',
  874. ),
  875. ),
  876. 'applicator' => array(
  877. 'header' => array(
  878. 'value' => $txt['trackEdit_applicator'],
  879. ),
  880. 'data' => array(
  881. 'db' => 'member_link',
  882. ),
  883. ),
  884. ),
  885. );
  886. // Create the error list.
  887. createList($listOptions);
  888. $context['sub_template'] = 'show_list';
  889. $context['default_list'] = 'edit_list';
  890. }
  891. /**
  892. * How many edits?
  893. *
  894. * @param int $memID id_member
  895. * @return string number of profile edits
  896. */
  897. function list_getProfileEditCount($memID)
  898. {
  899. global $smcFunc;
  900. $request = $smcFunc['db_query']('', '
  901. SELECT COUNT(*) AS edit_count
  902. FROM {db_prefix}log_actions
  903. WHERE id_log = {int:log_type}
  904. AND id_member = {int:owner}',
  905. array(
  906. 'log_type' => 2,
  907. 'owner' => $memID,
  908. )
  909. );
  910. list ($edit_count) = $smcFunc['db_fetch_row']($request);
  911. $smcFunc['db_free_result']($request);
  912. // @todo cast to integer
  913. return $edit_count;
  914. }
  915. /**
  916. * Callback function for createList in trackEdits().
  917. *
  918. * @param int $start
  919. * @param int $items_per_page
  920. * @param string $sort
  921. * @param int $memID
  922. * @return array
  923. */
  924. function list_getProfileEdits($start, $items_per_page, $sort, $memID)
  925. {
  926. global $smcFunc, $txt, $scripturl, $context;
  927. // Get a list of error messages from this ip (range).
  928. $request = $smcFunc['db_query']('', '
  929. SELECT
  930. id_action, id_member, ip, log_time, action, extra
  931. FROM {db_prefix}log_actions
  932. WHERE id_log = {int:log_type}
  933. AND id_member = {int:owner}
  934. ORDER BY ' . $sort . '
  935. LIMIT ' . $start . ', ' . $items_per_page,
  936. array(
  937. 'log_type' => 2,
  938. 'owner' => $memID,
  939. )
  940. );
  941. $edits = array();
  942. $members = array();
  943. while ($row = $smcFunc['db_fetch_assoc']($request))
  944. {
  945. $extra = @unserialize($row['extra']);
  946. if (!empty($extra['applicator']))
  947. $members[] = $extra['applicator'];
  948. // Work out what the name of the action is.
  949. if (isset($txt['trackEdit_action_' . $row['action']]))
  950. $action_text = $txt['trackEdit_action_' . $row['action']];
  951. elseif (isset($txt[$row['action']]))
  952. $action_text = $txt[$row['action']];
  953. // Custom field?
  954. elseif (isset($context['custom_field_titles'][$row['action']]))
  955. $action_text = $context['custom_field_titles'][$row['action']]['title'];
  956. else
  957. $action_text = $row['action'];
  958. // Parse BBC?
  959. $parse_bbc = isset($context['custom_field_titles'][$row['action']]) && $context['custom_field_titles'][$row['action']]['parse_bbc'] ? true : false;
  960. $edits[] = array(
  961. 'id' => $row['id_action'],
  962. 'ip' => $row['ip'],
  963. 'id_member' => !empty($extra['applicator']) ? $extra['applicator'] : 0,
  964. 'member_link' => $txt['trackEdit_deleted_member'],
  965. 'action' => $row['action'],
  966. 'action_text' => $action_text,
  967. 'before' => !empty($extra['previous']) ? ($parse_bbc ? parse_bbc($extra['previous']) : $extra['previous']) : '',
  968. 'after' => !empty($extra['new']) ? ($parse_bbc ? parse_bbc($extra['new']) : $extra['new']) : '',
  969. 'time' => timeformat($row['log_time']),
  970. );
  971. }
  972. $smcFunc['db_free_result']($request);
  973. // Get any member names.
  974. if (!empty($members))
  975. {
  976. $request = $smcFunc['db_query']('', '
  977. SELECT
  978. id_member, real_name
  979. FROM {db_prefix}members
  980. WHERE id_member IN ({array_int:members})',
  981. array(
  982. 'members' => $members,
  983. )
  984. );
  985. $members = array();
  986. while ($row = $smcFunc['db_fetch_assoc']($request))
  987. $members[$row['id_member']] = $row['real_name'];
  988. $smcFunc['db_free_result']($request);
  989. foreach ($edits as $key => $value)
  990. if (isset($members[$value['id_member']]))
  991. $edits[$key]['member_link'] = '<a href="' . $scripturl . '?action=profile;u=' . $value['id_member'] . '">' . $members[$value['id_member']] . '</a>';
  992. }
  993. return $edits;
  994. }