PageRenderTime 75ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 1ms

/phpBB/phpbb/log/log.php

http://github.com/phpbb/phpbb
PHP | 1001 lines | 568 code | 119 blank | 314 comment | 73 complexity | d943ac4b43e44d377f8cfab694eb3d41 MD5 | raw file
Possible License(s): GPL-3.0, AGPL-1.0
  1. <?php
  2. /**
  3. *
  4. * This file is part of the phpBB Forum Software package.
  5. *
  6. * @copyright (c) phpBB Limited <https://www.phpbb.com>
  7. * @license GNU General Public License, version 2 (GPL-2.0)
  8. *
  9. * For full copyright and license information, please see
  10. * the docs/CREDITS.txt file.
  11. *
  12. */
  13. namespace phpbb\log;
  14. /**
  15. * This class is used to add entries into the log table.
  16. */
  17. class log implements \phpbb\log\log_interface
  18. {
  19. /**
  20. * If set, administrative user profile links will be returned and messages
  21. * will not be censored.
  22. * @var bool
  23. */
  24. protected $is_in_admin;
  25. /**
  26. * An array with the disabled log types. Logs of such types will not be
  27. * added when add() is called.
  28. * @var array
  29. */
  30. protected $disabled_types;
  31. /**
  32. * Keeps the total log count of the last call to get_logs()
  33. * @var int
  34. */
  35. protected $entry_count;
  36. /**
  37. * Keeps the offset of the last valid page of the last call to get_logs()
  38. * @var int
  39. */
  40. protected $last_page_offset;
  41. /**
  42. * The table we use to store our logs.
  43. * @var string
  44. */
  45. protected $log_table;
  46. /**
  47. * Database object
  48. * @var \phpbb\db\driver\driver
  49. */
  50. protected $db;
  51. /**
  52. * User object
  53. * @var \phpbb\user
  54. */
  55. protected $user;
  56. /**
  57. * Auth object
  58. * @var \phpbb\auth\auth
  59. */
  60. protected $auth;
  61. /**
  62. * Event dispatcher object
  63. * @var \phpbb\event\dispatcher_interface
  64. */
  65. protected $dispatcher;
  66. /**
  67. * phpBB root path
  68. * @var string
  69. */
  70. protected $phpbb_root_path;
  71. /**
  72. * Admin root path
  73. * @var string
  74. */
  75. protected $phpbb_admin_path;
  76. /**
  77. * PHP Extension
  78. * @var string
  79. */
  80. protected $php_ext;
  81. /**
  82. * Constructor
  83. *
  84. * @param \phpbb\db\driver\driver_interface $db Database object
  85. * @param \phpbb\user $user User object
  86. * @param \phpbb\auth\auth $auth Auth object
  87. * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher
  88. * @param string $phpbb_root_path Root path
  89. * @param string $relative_admin_path Relative admin root path
  90. * @param string $php_ext PHP Extension
  91. * @param string $log_table Name of the table we use to store our logs
  92. */
  93. public function __construct($db, $user, $auth, $phpbb_dispatcher, $phpbb_root_path, $relative_admin_path, $php_ext, $log_table)
  94. {
  95. $this->db = $db;
  96. $this->user = $user;
  97. $this->auth = $auth;
  98. $this->dispatcher = $phpbb_dispatcher;
  99. $this->phpbb_root_path = $phpbb_root_path;
  100. $this->phpbb_admin_path = $this->phpbb_root_path . $relative_admin_path;
  101. $this->php_ext = $php_ext;
  102. $this->log_table = $log_table;
  103. /*
  104. * IN_ADMIN is set after the session is created,
  105. * so we need to take ADMIN_START into account as well, otherwise
  106. * it will not work for the \phpbb\log\log object we create in common.php
  107. */
  108. $this->set_is_admin((defined('ADMIN_START') && ADMIN_START) || (defined('IN_ADMIN') && IN_ADMIN));
  109. $this->enable();
  110. }
  111. /**
  112. * Set is_in_admin in order to return administrative user profile links
  113. * in get_logs()
  114. *
  115. * @param bool $is_in_admin Are we called from within the acp?
  116. * @return null
  117. */
  118. public function set_is_admin($is_in_admin)
  119. {
  120. $this->is_in_admin = (bool) $is_in_admin;
  121. }
  122. /**
  123. * Returns the is_in_admin option
  124. *
  125. * @return bool
  126. */
  127. public function get_is_admin()
  128. {
  129. return $this->is_in_admin;
  130. }
  131. /**
  132. * Set table name
  133. *
  134. * @param string $log_table Can overwrite the table to use for the logs
  135. * @return null
  136. */
  137. public function set_log_table($log_table)
  138. {
  139. $this->log_table = $log_table;
  140. }
  141. /**
  142. * {@inheritDoc}
  143. */
  144. public function is_enabled($type = '')
  145. {
  146. if ($type == '' || $type == 'all')
  147. {
  148. return !isset($this->disabled_types['all']);
  149. }
  150. return !isset($this->disabled_types[$type]) && !isset($this->disabled_types['all']);
  151. }
  152. /**
  153. * {@inheritDoc}
  154. */
  155. public function disable($type = '')
  156. {
  157. if (is_array($type))
  158. {
  159. foreach ($type as $disable_type)
  160. {
  161. $this->disable($disable_type);
  162. }
  163. return;
  164. }
  165. // Empty string is an equivalent for all types.
  166. if ($type == '')
  167. {
  168. $type = 'all';
  169. }
  170. $this->disabled_types[$type] = true;
  171. }
  172. /**
  173. * {@inheritDoc}
  174. */
  175. public function enable($type = '')
  176. {
  177. if (is_array($type))
  178. {
  179. foreach ($type as $enable_type)
  180. {
  181. $this->enable($enable_type);
  182. }
  183. return;
  184. }
  185. if ($type == '' || $type == 'all')
  186. {
  187. $this->disabled_types = array();
  188. return;
  189. }
  190. unset($this->disabled_types[$type]);
  191. }
  192. /**
  193. * {@inheritDoc}
  194. */
  195. public function add($mode, $user_id, $log_ip, $log_operation, $log_time = false, $additional_data = array())
  196. {
  197. if (!$this->is_enabled($mode))
  198. {
  199. return false;
  200. }
  201. if ($log_time === false)
  202. {
  203. $log_time = time();
  204. }
  205. $sql_ary = array(
  206. 'user_id' => !empty($user_id) ? $user_id : ANONYMOUS,
  207. 'log_ip' => !empty($log_ip) ? $log_ip : '',
  208. 'log_time' => $log_time,
  209. 'log_operation' => $log_operation,
  210. );
  211. switch ($mode)
  212. {
  213. case 'admin':
  214. $sql_ary += array(
  215. 'log_type' => LOG_ADMIN,
  216. 'log_data' => (!empty($additional_data)) ? serialize($additional_data) : '',
  217. );
  218. break;
  219. case 'mod':
  220. $forum_id = isset($additional_data['forum_id']) ? (int) $additional_data['forum_id'] : 0;
  221. unset($additional_data['forum_id']);
  222. $topic_id = isset($additional_data['topic_id']) ? (int) $additional_data['topic_id'] : 0;
  223. unset($additional_data['topic_id']);
  224. $post_id = isset($additional_data['post_id']) ? (int) $additional_data['post_id'] : 0;
  225. unset($additional_data['post_id']);
  226. $sql_ary += array(
  227. 'log_type' => LOG_MOD,
  228. 'forum_id' => $forum_id,
  229. 'topic_id' => $topic_id,
  230. 'post_id' => $post_id,
  231. 'log_data' => (!empty($additional_data)) ? serialize($additional_data) : '',
  232. );
  233. break;
  234. case 'user':
  235. $reportee_id = (int) $additional_data['reportee_id'];
  236. unset($additional_data['reportee_id']);
  237. $sql_ary += array(
  238. 'log_type' => LOG_USERS,
  239. 'reportee_id' => $reportee_id,
  240. 'log_data' => (!empty($additional_data)) ? serialize($additional_data) : '',
  241. );
  242. break;
  243. case 'critical':
  244. $sql_ary += array(
  245. 'log_type' => LOG_CRITICAL,
  246. 'log_data' => (!empty($additional_data)) ? serialize($additional_data) : '',
  247. );
  248. break;
  249. }
  250. /**
  251. * Allows to modify log data before we add it to the database
  252. *
  253. * NOTE: if sql_ary does not contain a log_type value, the entry will
  254. * not be stored in the database. So ensure to set it, if needed.
  255. *
  256. * @event core.add_log
  257. * @var string mode Mode of the entry we log
  258. * @var int user_id ID of the user who triggered the log
  259. * @var string log_ip IP of the user who triggered the log
  260. * @var string log_operation Language key of the log operation
  261. * @var int log_time Timestamp, when the log was added
  262. * @var array additional_data Array with additional log data
  263. * @var array sql_ary Array with log data we insert into the
  264. * database. If sql_ary[log_type] is not set,
  265. * we won't add the entry to the database.
  266. * @since 3.1.0-a1
  267. */
  268. $vars = array(
  269. 'mode',
  270. 'user_id',
  271. 'log_ip',
  272. 'log_operation',
  273. 'log_time',
  274. 'additional_data',
  275. 'sql_ary',
  276. );
  277. extract($this->dispatcher->trigger_event('core.add_log', compact($vars)));
  278. // We didn't find a log_type, so we don't save it in the database.
  279. if (!isset($sql_ary['log_type']))
  280. {
  281. return false;
  282. }
  283. $this->db->sql_query('INSERT INTO ' . $this->log_table . ' ' . $this->db->sql_build_array('INSERT', $sql_ary));
  284. return $this->db->sql_nextid();
  285. }
  286. /**
  287. * {@inheritDoc}
  288. */
  289. public function delete($mode, $conditions = array())
  290. {
  291. switch ($mode)
  292. {
  293. case 'admin':
  294. $log_type = LOG_ADMIN;
  295. break;
  296. case 'mod':
  297. $log_type = LOG_MOD;
  298. break;
  299. case 'user':
  300. $log_type = LOG_USERS;
  301. break;
  302. case 'users':
  303. $log_type = LOG_USERS;
  304. break;
  305. case 'critical':
  306. $log_type = LOG_CRITICAL;
  307. break;
  308. default:
  309. $log_type = false;
  310. }
  311. /**
  312. * Allows to modify log data before we delete it from the database
  313. *
  314. * NOTE: if sql_ary does not contain a log_type value, the entry will
  315. * not be deleted in the database. So ensure to set it, if needed.
  316. *
  317. * @event core.delete_log
  318. * @var string mode Mode of the entry we log
  319. * @var string log_type Type ID of the log (should be different than false)
  320. * @var array conditions An array of conditions, 3 different forms are accepted
  321. * 1) <key> => <value> transformed into 'AND <key> = <value>' (value should be an integer)
  322. * 2) <key> => array(<operator>, <value>) transformed into 'AND <key> <operator> <value>' (values can't be an array)
  323. * 3) <key> => array('IN' => array(<values>)) transformed into 'AND <key> IN <values>'
  324. * A special field, keywords, can also be defined. In this case only the log entries that have the keywords in log_operation or log_data will be deleted.
  325. * @since 3.1.0-b4
  326. */
  327. $vars = array(
  328. 'mode',
  329. 'log_type',
  330. 'conditions',
  331. );
  332. extract($this->dispatcher->trigger_event('core.delete_log', compact($vars)));
  333. if ($log_type === false)
  334. {
  335. return;
  336. }
  337. $sql_where = 'WHERE log_type = ' . $log_type;
  338. if (isset($conditions['keywords']))
  339. {
  340. $sql_where .= $this->generate_sql_keyword($conditions['keywords'], '');
  341. unset($conditions['keywords']);
  342. }
  343. foreach ($conditions as $field => $field_value)
  344. {
  345. $sql_where .= ' AND ';
  346. if (is_array($field_value) && count($field_value) == 2 && !is_array($field_value[1]))
  347. {
  348. $sql_where .= $field . ' ' . $field_value[0] . ' ' . $field_value[1];
  349. }
  350. else if (is_array($field_value) && isset($field_value['IN']) && is_array($field_value['IN']))
  351. {
  352. $sql_where .= $this->db->sql_in_set($field, $field_value['IN']);
  353. }
  354. else
  355. {
  356. $sql_where .= $field . ' = ' . $field_value;
  357. }
  358. }
  359. $sql = 'DELETE FROM ' . $this->log_table . "
  360. $sql_where";
  361. $this->db->sql_query($sql);
  362. $this->add('admin', $this->user->data['user_id'], $this->user->ip, 'LOG_CLEAR_' . strtoupper($mode));
  363. }
  364. /**
  365. * {@inheritDoc}
  366. */
  367. public function get_logs($mode, $count_logs = true, $limit = 0, $offset = 0, $forum_id = 0, $topic_id = 0, $user_id = 0, $log_time = 0, $sort_by = 'l.log_time DESC', $keywords = '')
  368. {
  369. $this->entry_count = 0;
  370. $this->last_page_offset = $offset;
  371. $topic_id_list = $reportee_id_list = array();
  372. $profile_url = ($this->get_is_admin() && $this->phpbb_admin_path) ? append_sid("{$this->phpbb_admin_path}index.{$this->php_ext}", 'i=users&amp;mode=overview') : append_sid("{$this->phpbb_root_path}memberlist.{$this->php_ext}", 'mode=viewprofile');
  373. switch ($mode)
  374. {
  375. case 'admin':
  376. $log_type = LOG_ADMIN;
  377. $sql_additional = '';
  378. break;
  379. case 'mod':
  380. $log_type = LOG_MOD;
  381. $sql_additional = '';
  382. if ($topic_id)
  383. {
  384. $sql_additional = 'AND l.topic_id = ' . (int) $topic_id;
  385. }
  386. else if (is_array($forum_id))
  387. {
  388. $sql_additional = 'AND ' . $this->db->sql_in_set('l.forum_id', array_map('intval', $forum_id));
  389. }
  390. else if ($forum_id)
  391. {
  392. $sql_additional = 'AND l.forum_id = ' . (int) $forum_id;
  393. }
  394. break;
  395. case 'user':
  396. $log_type = LOG_USERS;
  397. $sql_additional = 'AND l.reportee_id = ' . (int) $user_id;
  398. break;
  399. case 'users':
  400. $log_type = LOG_USERS;
  401. $sql_additional = '';
  402. break;
  403. case 'critical':
  404. $log_type = LOG_CRITICAL;
  405. $sql_additional = '';
  406. break;
  407. default:
  408. $log_type = false;
  409. $sql_additional = '';
  410. }
  411. /**
  412. * Overwrite log type and limitations before we count and get the logs
  413. *
  414. * NOTE: if log_type is false, no entries will be returned.
  415. *
  416. * @event core.get_logs_modify_type
  417. * @var string mode Mode of the entries we display
  418. * @var bool count_logs Do we count all matching entries?
  419. * @var int limit Limit the number of entries
  420. * @var int offset Offset when fetching the entries
  421. * @var mixed forum_id Limit entries to the forum_id,
  422. * can also be an array of forum_ids
  423. * @var int topic_id Limit entries to the topic_id
  424. * @var int user_id Limit entries to the user_id
  425. * @var int log_time Limit maximum age of log entries
  426. * @var string sort_by SQL order option
  427. * @var string keywords Will only return entries that have the
  428. * keywords in log_operation or log_data
  429. * @var string profile_url URL to the users profile
  430. * @var int log_type Limit logs to a certain type. If log_type
  431. * is false, no entries will be returned.
  432. * @var string sql_additional Additional conditions for the entries,
  433. * e.g.: 'AND l.forum_id = 1'
  434. * @since 3.1.0-a1
  435. */
  436. $vars = array(
  437. 'mode',
  438. 'count_logs',
  439. 'limit',
  440. 'offset',
  441. 'forum_id',
  442. 'topic_id',
  443. 'user_id',
  444. 'log_time',
  445. 'sort_by',
  446. 'keywords',
  447. 'profile_url',
  448. 'log_type',
  449. 'sql_additional',
  450. );
  451. extract($this->dispatcher->trigger_event('core.get_logs_modify_type', compact($vars)));
  452. if ($log_type === false)
  453. {
  454. $this->last_page_offset = 0;
  455. return array();
  456. }
  457. $sql_keywords = '';
  458. if (!empty($keywords))
  459. {
  460. // Get the SQL condition for our keywords
  461. $sql_keywords = $this->generate_sql_keyword($keywords);
  462. }
  463. $get_logs_sql_ary = array(
  464. 'SELECT' => 'l.*, u.username, u.username_clean, u.user_colour',
  465. 'FROM' => array(
  466. $this->log_table => 'l',
  467. USERS_TABLE => 'u',
  468. ),
  469. 'WHERE' => 'l.log_type = ' . (int) $log_type . "
  470. AND l.user_id = u.user_id
  471. $sql_keywords
  472. $sql_additional",
  473. 'ORDER_BY' => $sort_by,
  474. );
  475. if ($log_time)
  476. {
  477. $get_logs_sql_ary['WHERE'] = 'l.log_time >= ' . (int) $log_time . '
  478. AND ' . $get_logs_sql_ary['WHERE'];
  479. }
  480. /**
  481. * Modify the query to obtain the logs data
  482. *
  483. * @event core.get_logs_main_query_before
  484. * @var array get_logs_sql_ary The array in the format of the query builder with the query
  485. * to get the log count and the log list
  486. * @var string mode Mode of the entries we display
  487. * @var bool count_logs Do we count all matching entries?
  488. * @var int limit Limit the number of entries
  489. * @var int offset Offset when fetching the entries
  490. * @var mixed forum_id Limit entries to the forum_id,
  491. * can also be an array of forum_ids
  492. * @var int topic_id Limit entries to the topic_id
  493. * @var int user_id Limit entries to the user_id
  494. * @var int log_time Limit maximum age of log entries
  495. * @var string sort_by SQL order option
  496. * @var string keywords Will only return entries that have the
  497. * keywords in log_operation or log_data
  498. * @var string profile_url URL to the users profile
  499. * @var int log_type Limit logs to a certain type. If log_type
  500. * is false, no entries will be returned.
  501. * @var string sql_additional Additional conditions for the entries,
  502. * e.g.: 'AND l.forum_id = 1'
  503. * @since 3.1.5-RC1
  504. */
  505. $vars = array(
  506. 'get_logs_sql_ary',
  507. 'mode',
  508. 'count_logs',
  509. 'limit',
  510. 'offset',
  511. 'forum_id',
  512. 'topic_id',
  513. 'user_id',
  514. 'log_time',
  515. 'sort_by',
  516. 'keywords',
  517. 'profile_url',
  518. 'log_type',
  519. 'sql_additional',
  520. );
  521. extract($this->dispatcher->trigger_event('core.get_logs_main_query_before', compact($vars)));
  522. if ($count_logs)
  523. {
  524. $count_logs_sql_ary = $get_logs_sql_ary;
  525. $count_logs_sql_ary['SELECT'] = 'COUNT(l.log_id) AS total_entries';
  526. unset($count_logs_sql_ary['ORDER_BY']);
  527. $sql = $this->db->sql_build_query('SELECT', $count_logs_sql_ary);
  528. $result = $this->db->sql_query($sql);
  529. $this->entry_count = (int) $this->db->sql_fetchfield('total_entries');
  530. $this->db->sql_freeresult($result);
  531. if ($this->entry_count == 0)
  532. {
  533. // Save the queries, because there are no logs to display
  534. $this->last_page_offset = 0;
  535. return array();
  536. }
  537. // Return the user to the last page that is valid
  538. while ($this->last_page_offset >= $this->entry_count)
  539. {
  540. $this->last_page_offset = max(0, $this->last_page_offset - $limit);
  541. }
  542. }
  543. $sql = $this->db->sql_build_query('SELECT', $get_logs_sql_ary);
  544. $result = $this->db->sql_query_limit($sql, $limit, $this->last_page_offset);
  545. $i = 0;
  546. $log = array();
  547. while ($row = $this->db->sql_fetchrow($result))
  548. {
  549. $row['forum_id'] = (int) $row['forum_id'];
  550. if ($row['topic_id'])
  551. {
  552. $topic_id_list[] = (int) $row['topic_id'];
  553. }
  554. if ($row['reportee_id'])
  555. {
  556. $reportee_id_list[] = (int) $row['reportee_id'];
  557. }
  558. $log_entry_data = array(
  559. 'id' => (int) $row['log_id'],
  560. 'reportee_id' => (int) $row['reportee_id'],
  561. 'reportee_username' => '',
  562. 'reportee_username_full'=> '',
  563. 'user_id' => (int) $row['user_id'],
  564. 'username' => $row['username'],
  565. 'username_full' => get_username_string('full', $row['user_id'], $row['username'], $row['user_colour'], false, $profile_url),
  566. 'ip' => $row['log_ip'],
  567. 'time' => (int) $row['log_time'],
  568. 'forum_id' => (int) $row['forum_id'],
  569. 'topic_id' => (int) $row['topic_id'],
  570. 'post_id' => (int) $row['post_id'],
  571. 'viewforum' => ($row['forum_id'] && $this->auth->acl_get('f_read', $row['forum_id'])) ? append_sid("{$this->phpbb_root_path}viewforum.{$this->php_ext}", 'f=' . $row['forum_id']) : false,
  572. 'action' => (isset($this->user->lang[$row['log_operation']])) ? $row['log_operation'] : '{' . ucfirst(str_replace('_', ' ', $row['log_operation'])) . '}',
  573. );
  574. /**
  575. * Modify the entry's data before it is returned
  576. *
  577. * @event core.get_logs_modify_entry_data
  578. * @var array row Entry data from the database
  579. * @var array log_entry_data Entry's data which is returned
  580. * @since 3.1.0-a1
  581. */
  582. $vars = array('row', 'log_entry_data');
  583. extract($this->dispatcher->trigger_event('core.get_logs_modify_entry_data', compact($vars)));
  584. $log[$i] = $log_entry_data;
  585. if (!empty($row['log_data']))
  586. {
  587. $log_data_ary = unserialize($row['log_data']);
  588. $log_data_ary = ($log_data_ary !== false) ? $log_data_ary : array();
  589. if (isset($this->user->lang[$row['log_operation']]))
  590. {
  591. // Check if there are more occurrences of % than
  592. // arguments, if there are we fill out the arguments
  593. // array. It doesn't matter if we add more arguments than
  594. // placeholders.
  595. $num_args = 0;
  596. if (!is_array($this->user->lang[$row['log_operation']]))
  597. {
  598. $num_args = substr_count($this->user->lang[$row['log_operation']], '%');
  599. }
  600. else
  601. {
  602. foreach ($this->user->lang[$row['log_operation']] as $case => $plural_string)
  603. {
  604. $num_args = max($num_args, substr_count($plural_string, '%'));
  605. }
  606. }
  607. if (($num_args - count($log_data_ary)) > 0)
  608. {
  609. $log_data_ary = array_merge($log_data_ary, array_fill(0, $num_args - count($log_data_ary), ''));
  610. }
  611. $lang_arguments = array_merge(array($log[$i]['action']), $log_data_ary);
  612. $log[$i]['action'] = call_user_func_array(array($this->user, 'lang'), $lang_arguments);
  613. // If within the admin panel we do not censor text out
  614. if ($this->get_is_admin())
  615. {
  616. $log[$i]['action'] = bbcode_nl2br($log[$i]['action']);
  617. }
  618. else
  619. {
  620. $log[$i]['action'] = bbcode_nl2br(censor_text($log[$i]['action']));
  621. }
  622. }
  623. else if (!empty($log_data_ary))
  624. {
  625. $log[$i]['action'] .= '<br />' . implode('', $log_data_ary);
  626. }
  627. /* Apply make_clickable... has to be seen if it is for good. :/
  628. // Seems to be not for the moment, reconsider later...
  629. $log[$i]['action'] = make_clickable($log[$i]['action']);
  630. */
  631. }
  632. else
  633. {
  634. $log[$i]['action'] = $this->user->lang($log[$i]['action']);
  635. }
  636. $i++;
  637. }
  638. $this->db->sql_freeresult($result);
  639. /**
  640. * Get some additional data after we got all log entries
  641. *
  642. * @event core.get_logs_get_additional_data
  643. * @var array log Array with all our log entries
  644. * @var array topic_id_list Array of topic ids, for which we
  645. * get the permission data
  646. * @var array reportee_id_list Array of additional user IDs we
  647. * get the username strings for
  648. * @since 3.1.0-a1
  649. */
  650. $vars = array('log', 'topic_id_list', 'reportee_id_list');
  651. extract($this->dispatcher->trigger_event('core.get_logs_get_additional_data', compact($vars)));
  652. if (count($topic_id_list))
  653. {
  654. $topic_auth = $this->get_topic_auth($topic_id_list);
  655. foreach ($log as $key => $row)
  656. {
  657. $log[$key]['viewtopic'] = (isset($topic_auth['f_read'][$row['topic_id']])) ? append_sid("{$this->phpbb_root_path}viewtopic.{$this->php_ext}", 'f=' . $topic_auth['f_read'][$row['topic_id']] . '&amp;t=' . $row['topic_id']) : false;
  658. $log[$key]['viewpost'] = (isset($topic_auth['f_read'][$row['topic_id']]) && $row['post_id']) ? append_sid("{$this->phpbb_root_path}viewtopic.{$this->php_ext}", 'f=' . $topic_auth['f_read'][$row['topic_id']] . '&amp;t=' . $row['topic_id'] . '&amp;p=' . $row['post_id'] . '#p' . $row['post_id']) : false;
  659. $log[$key]['viewlogs'] = (isset($topic_auth['m_'][$row['topic_id']])) ? append_sid("{$this->phpbb_root_path}mcp.{$this->php_ext}", 'i=logs&amp;mode=topic_logs&amp;t=' . $row['topic_id'], true, $this->user->session_id) : false;
  660. }
  661. }
  662. if (count($reportee_id_list))
  663. {
  664. $reportee_data_list = $this->get_reportee_data($reportee_id_list);
  665. foreach ($log as $key => $row)
  666. {
  667. if (!isset($reportee_data_list[$row['reportee_id']]))
  668. {
  669. continue;
  670. }
  671. $log[$key]['reportee_username'] = $reportee_data_list[$row['reportee_id']]['username'];
  672. $log[$key]['reportee_username_full'] = get_username_string('full', $row['reportee_id'], $reportee_data_list[$row['reportee_id']]['username'], $reportee_data_list[$row['reportee_id']]['user_colour'], false, $profile_url);
  673. }
  674. }
  675. /**
  676. * Allow modifying or execute extra final filter on log entries
  677. *
  678. * @event core.get_logs_after
  679. * @var array log Array with all our log entries
  680. * @var array topic_id_list Array of topic ids, for which we
  681. * get the permission data
  682. * @var array reportee_id_list Array of additional user IDs we
  683. * get the username strings for
  684. * @var string mode Mode of the entries we display
  685. * @var bool count_logs Do we count all matching entries?
  686. * @var int limit Limit the number of entries
  687. * @var int offset Offset when fetching the entries
  688. * @var mixed forum_id Limit entries to the forum_id,
  689. * can also be an array of forum_ids
  690. * @var int topic_id Limit entries to the topic_id
  691. * @var int user_id Limit entries to the user_id
  692. * @var int log_time Limit maximum age of log entries
  693. * @var string sort_by SQL order option
  694. * @var string keywords Will only return entries that have the
  695. * keywords in log_operation or log_data
  696. * @var string profile_url URL to the users profile
  697. * @var int log_type The type of logs it was filtered
  698. * @since 3.1.3-RC1
  699. */
  700. $vars = array(
  701. 'log',
  702. 'topic_id_list',
  703. 'reportee_id_list',
  704. 'mode',
  705. 'count_logs',
  706. 'limit',
  707. 'offset',
  708. 'forum_id',
  709. 'topic_id',
  710. 'user_id',
  711. 'log_time',
  712. 'sort_by',
  713. 'keywords',
  714. 'profile_url',
  715. 'log_type',
  716. );
  717. extract($this->dispatcher->trigger_event('core.get_logs_after', compact($vars)));
  718. return $log;
  719. }
  720. /**
  721. * Generates a sql condition for the specified keywords
  722. *
  723. * @param string $keywords The keywords the user specified to search for
  724. * @param string $table_alias The alias of the logs' table ('l.' by default)
  725. * @param string $statement_operator The operator used to prefix the statement ('AND' by default)
  726. *
  727. * @return string Returns the SQL condition searching for the keywords
  728. */
  729. protected function generate_sql_keyword($keywords, $table_alias = 'l.', $statement_operator = 'AND')
  730. {
  731. // Use no preg_quote for $keywords because this would lead to sole
  732. // backslashes being added. We also use an OR connection here for
  733. // spaces and the | string. Currently, regex is not supported for
  734. // searching (but may come later).
  735. $keywords = preg_split('#[\s|]+#u', utf8_strtolower($keywords), 0, PREG_SPLIT_NO_EMPTY);
  736. $sql_keywords = '';
  737. if (!empty($keywords))
  738. {
  739. $keywords_pattern = array();
  740. // Build pattern and keywords...
  741. for ($i = 0, $num_keywords = count($keywords); $i < $num_keywords; $i++)
  742. {
  743. $keywords_pattern[] = preg_quote($keywords[$i], '#');
  744. $keywords[$i] = $this->db->sql_like_expression($this->db->get_any_char() . $keywords[$i] . $this->db->get_any_char());
  745. }
  746. $keywords_pattern = '#' . implode('|', $keywords_pattern) . '#ui';
  747. $operations = array();
  748. foreach ($this->user->lang as $key => $value)
  749. {
  750. if (substr($key, 0, 4) == 'LOG_')
  751. {
  752. if (is_array($value))
  753. {
  754. foreach ($value as $plural_value)
  755. {
  756. if (preg_match($keywords_pattern, $plural_value))
  757. {
  758. $operations[] = $key;
  759. break;
  760. }
  761. }
  762. }
  763. else if (preg_match($keywords_pattern, $value))
  764. {
  765. $operations[] = $key;
  766. }
  767. }
  768. }
  769. $sql_keywords = ' ' . $statement_operator . ' (';
  770. if (!empty($operations))
  771. {
  772. $sql_keywords .= $this->db->sql_in_set($table_alias . 'log_operation', $operations) . ' OR ';
  773. }
  774. $sql_lower = $this->db->sql_lower_text($table_alias . 'log_data');
  775. $sql_keywords .= " $sql_lower " . implode(" OR $sql_lower ", $keywords) . ')';
  776. }
  777. return $sql_keywords;
  778. }
  779. /**
  780. * Determine whether the user is allowed to read and/or moderate the forum of the topic
  781. *
  782. * @param array $topic_ids Array with the topic ids
  783. *
  784. * @return array Returns an array with two keys 'm_' and 'read_f' which are also an array of topic_id => forum_id sets when the permissions are given. Sample:
  785. * array(
  786. * 'permission' => array(
  787. * topic_id => forum_id
  788. * ),
  789. * ),
  790. */
  791. protected function get_topic_auth(array $topic_ids)
  792. {
  793. $forum_auth = array('f_read' => array(), 'm_' => array());
  794. $topic_ids = array_unique($topic_ids);
  795. $sql_ary = array(
  796. 'SELECT' => 'topic_id, forum_id',
  797. 'FROM' => array(
  798. TOPICS_TABLE => 't',
  799. ),
  800. 'WHERE' => $this->db->sql_in_set('topic_id', array_map('intval', $topic_ids)),
  801. );
  802. /**
  803. * Allow modifying SQL query before topic data is retrieved.
  804. *
  805. * @event core.phpbb_log_get_topic_auth_sql_before
  806. * @var array topic_ids Array with unique topic IDs
  807. * @var array sql_ary SQL array
  808. * @since 3.1.11-RC1
  809. */
  810. $vars = array(
  811. 'topic_ids',
  812. 'sql_ary',
  813. );
  814. extract($this->dispatcher->trigger_event('core.phpbb_log_get_topic_auth_sql_before', compact($vars)));
  815. $sql = $this->db->sql_build_query('SELECT', $sql_ary);
  816. $result = $this->db->sql_query($sql);
  817. while ($row = $this->db->sql_fetchrow($result))
  818. {
  819. $row['topic_id'] = (int) $row['topic_id'];
  820. $row['forum_id'] = (int) $row['forum_id'];
  821. if ($this->auth->acl_get('f_read', $row['forum_id']))
  822. {
  823. $forum_auth['f_read'][$row['topic_id']] = $row['forum_id'];
  824. }
  825. /**
  826. * Allow modifying SQL query after topic data is retrieved (inside loop).
  827. *
  828. * @event core.phpbb_log_get_topic_auth_sql_after
  829. * @var array forum_auth Forum permissions
  830. * @var array row One row of data from SQL query
  831. * @since 3.2.2-RC1
  832. */
  833. $vars = array(
  834. 'forum_auth',
  835. 'row',
  836. );
  837. extract($this->dispatcher->trigger_event('core.phpbb_log_get_topic_auth_sql_after', compact($vars)));
  838. if ($this->auth->acl_gets('a_', 'm_', $row['forum_id']))
  839. {
  840. $forum_auth['m_'][$row['topic_id']] = $row['forum_id'];
  841. }
  842. }
  843. $this->db->sql_freeresult($result);
  844. return $forum_auth;
  845. }
  846. /**
  847. * Get the data for all reportee from the database
  848. *
  849. * @param array $reportee_ids Array with the user ids of the reportees
  850. *
  851. * @return array Returns an array with the reportee data
  852. */
  853. protected function get_reportee_data(array $reportee_ids)
  854. {
  855. $reportee_ids = array_unique($reportee_ids);
  856. $reportee_data_list = array();
  857. $sql = 'SELECT user_id, username, user_colour
  858. FROM ' . USERS_TABLE . '
  859. WHERE ' . $this->db->sql_in_set('user_id', $reportee_ids);
  860. $result = $this->db->sql_query($sql);
  861. while ($row = $this->db->sql_fetchrow($result))
  862. {
  863. $reportee_data_list[$row['user_id']] = $row;
  864. }
  865. $this->db->sql_freeresult($result);
  866. return $reportee_data_list;
  867. }
  868. /**
  869. * {@inheritDoc}
  870. */
  871. public function get_log_count()
  872. {
  873. return ($this->entry_count) ? $this->entry_count : 0;
  874. }
  875. /**
  876. * {@inheritDoc}
  877. */
  878. public function get_valid_offset()
  879. {
  880. return ($this->last_page_offset) ? $this->last_page_offset : 0;
  881. }
  882. }