PageRenderTime 60ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/forum/phpbb/content_visibility.php

https://github.com/AJenbo/ubuntudanmark.dk
PHP | 863 lines | 492 code | 83 blank | 288 comment | 95 complexity | d29bb59529c013e342609c54df6f6849 MD5 | raw file
  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;
  14. /**
  15. * phpbb_visibility
  16. * Handle fetching and setting the visibility for topics and posts
  17. */
  18. class content_visibility
  19. {
  20. /**
  21. * Database object
  22. * @var \phpbb\db\driver\driver_interface
  23. */
  24. protected $db;
  25. /**
  26. * User object
  27. * @var \phpbb\user
  28. */
  29. protected $user;
  30. /**
  31. * Auth object
  32. * @var \phpbb\auth\auth
  33. */
  34. protected $auth;
  35. /**
  36. * config object
  37. * @var \phpbb\config\config
  38. */
  39. protected $config;
  40. /**
  41. * Event dispatcher object
  42. * @var \phpbb\event\dispatcher_interface
  43. */
  44. protected $phpbb_dispatcher;
  45. /**
  46. * phpBB root path
  47. * @var string
  48. */
  49. protected $phpbb_root_path;
  50. /**
  51. * PHP Extension
  52. * @var string
  53. */
  54. protected $php_ext;
  55. /**
  56. * Constructor
  57. *
  58. * @param \phpbb\auth\auth $auth Auth object
  59. * @param \phpbb\config\config $config Config object
  60. * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object
  61. * @param \phpbb\db\driver\driver_interface $db Database object
  62. * @param \phpbb\user $user User object
  63. * @param string $phpbb_root_path Root path
  64. * @param string $php_ext PHP Extension
  65. * @param string $forums_table Forums table name
  66. * @param string $posts_table Posts table name
  67. * @param string $topics_table Topics table name
  68. * @param string $users_table Users table name
  69. */
  70. public function __construct(\phpbb\auth\auth $auth, \phpbb\config\config $config, \phpbb\event\dispatcher_interface $phpbb_dispatcher, \phpbb\db\driver\driver_interface $db, \phpbb\user $user, $phpbb_root_path, $php_ext, $forums_table, $posts_table, $topics_table, $users_table)
  71. {
  72. $this->auth = $auth;
  73. $this->config = $config;
  74. $this->phpbb_dispatcher = $phpbb_dispatcher;
  75. $this->db = $db;
  76. $this->user = $user;
  77. $this->phpbb_root_path = $phpbb_root_path;
  78. $this->php_ext = $php_ext;
  79. $this->forums_table = $forums_table;
  80. $this->posts_table = $posts_table;
  81. $this->topics_table = $topics_table;
  82. $this->users_table = $users_table;
  83. }
  84. /**
  85. * Can the current logged-in user soft-delete posts?
  86. *
  87. * @param $forum_id int Forum ID whose permissions to check
  88. * @param $poster_id int Poster ID of the post in question
  89. * @param $post_locked bool Is the post locked?
  90. * @return bool
  91. */
  92. public function can_soft_delete($forum_id, $poster_id, $post_locked)
  93. {
  94. if ($this->auth->acl_get('m_softdelete', $forum_id))
  95. {
  96. return true;
  97. }
  98. else if ($this->auth->acl_get('f_softdelete', $forum_id) && $poster_id == $this->user->data['user_id'] && !$post_locked)
  99. {
  100. return true;
  101. }
  102. return false;
  103. }
  104. /**
  105. * Get the topics post count or the forums post/topic count based on permissions
  106. *
  107. * @param $mode string One of topic_posts, forum_posts or forum_topics
  108. * @param $data array Array with the topic/forum data to calculate from
  109. * @param $forum_id int The forum id is used for permission checks
  110. * @return int Number of posts/topics the user can see in the topic/forum
  111. */
  112. public function get_count($mode, $data, $forum_id)
  113. {
  114. if (!$this->auth->acl_get('m_approve', $forum_id))
  115. {
  116. return (int) $data[$mode . '_approved'];
  117. }
  118. return (int) $data[$mode . '_approved'] + (int) $data[$mode . '_unapproved'] + (int) $data[$mode . '_softdeleted'];
  119. }
  120. /**
  121. * Create topic/post visibility SQL for a given forum ID
  122. *
  123. * Note: Read permissions are not checked.
  124. *
  125. * @param $mode string Either "topic" or "post"
  126. * @param $forum_id int The forum id is used for permission checks
  127. * @param $table_alias string Table alias to prefix in SQL queries
  128. * @return string The appropriate combination SQL logic for topic/post_visibility
  129. */
  130. public function get_visibility_sql($mode, $forum_id, $table_alias = '')
  131. {
  132. $where_sql = '';
  133. $get_visibility_sql_overwrite = false;
  134. /**
  135. * Allow changing the result of calling get_visibility_sql
  136. *
  137. * @event core.phpbb_content_visibility_get_visibility_sql_before
  138. * @var string where_sql Extra visibility conditions. It must end with either an SQL "AND" or an "OR"
  139. * @var string mode Either "topic" or "post" depending on the query this is being used in
  140. * @var array forum_id The forum id in which the search is made.
  141. * @var string table_alias Table alias to prefix in SQL queries
  142. * @var mixed get_visibility_sql_overwrite If a string, forces the function to return get_forums_visibility_sql_overwrite after executing the event
  143. * If false, get_visibility_sql continues normally
  144. * It must be either boolean or string
  145. * @since 3.1.4-RC1
  146. */
  147. $vars = array(
  148. 'where_sql',
  149. 'mode',
  150. 'forum_id',
  151. 'table_alias',
  152. 'get_visibility_sql_overwrite',
  153. );
  154. extract($this->phpbb_dispatcher->trigger_event('core.phpbb_content_visibility_get_visibility_sql_before', compact($vars)));
  155. if ($get_visibility_sql_overwrite !== false)
  156. {
  157. return $get_visibility_sql_overwrite;
  158. }
  159. if ($this->auth->acl_get('m_approve', $forum_id))
  160. {
  161. return $where_sql . '1 = 1';
  162. }
  163. return $where_sql . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED;
  164. }
  165. /**
  166. * Create topic/post visibility SQL for a set of forums
  167. *
  168. * Note: Read permissions are not checked. Forums without read permissions
  169. * should not be in $forum_ids
  170. *
  171. * @param $mode string Either "topic" or "post"
  172. * @param $forum_ids array Array of forum ids which the posts/topics are limited to
  173. * @param $table_alias string Table alias to prefix in SQL queries
  174. * @return string The appropriate combination SQL logic for topic/post_visibility
  175. */
  176. public function get_forums_visibility_sql($mode, $forum_ids = array(), $table_alias = '')
  177. {
  178. $where_sql = '(';
  179. $approve_forums = array_intersect($forum_ids, array_keys($this->auth->acl_getf('m_approve', true)));
  180. $get_forums_visibility_sql_overwrite = false;
  181. /**
  182. * Allow changing the result of calling get_forums_visibility_sql
  183. *
  184. * @event core.phpbb_content_visibility_get_forums_visibility_before
  185. * @var string where_sql The action the user tried to execute
  186. * @var string mode Either "topic" or "post" depending on the query this is being used in
  187. * @var array forum_ids Array of forum ids which the posts/topics are limited to
  188. * @var string table_alias Table alias to prefix in SQL queries
  189. * @var array approve_forums Array of forums where the user has m_approve permissions
  190. * @var mixed get_forums_visibility_sql_overwrite If a string, forces the function to return get_forums_visibility_sql_overwrite after executing the event
  191. * If false, get_forums_visibility_sql continues normally
  192. * It must be either boolean or string
  193. * @since 3.1.3-RC1
  194. */
  195. $vars = array(
  196. 'where_sql',
  197. 'mode',
  198. 'forum_ids',
  199. 'table_alias',
  200. 'approve_forums',
  201. 'get_forums_visibility_sql_overwrite',
  202. );
  203. extract($this->phpbb_dispatcher->trigger_event('core.phpbb_content_visibility_get_forums_visibility_before', compact($vars)));
  204. if ($get_forums_visibility_sql_overwrite !== false)
  205. {
  206. return $get_forums_visibility_sql_overwrite;
  207. }
  208. if (sizeof($approve_forums))
  209. {
  210. // Remove moderator forums from the rest
  211. $forum_ids = array_diff($forum_ids, $approve_forums);
  212. if (!sizeof($forum_ids))
  213. {
  214. // The user can see all posts/topics in all specified forums
  215. return $where_sql . $this->db->sql_in_set($table_alias . 'forum_id', $approve_forums) . ')';
  216. }
  217. else
  218. {
  219. // Moderator can view all posts/topics in some forums
  220. $where_sql .= $this->db->sql_in_set($table_alias . 'forum_id', $approve_forums) . ' OR ';
  221. }
  222. }
  223. else
  224. {
  225. // The user is just a normal user
  226. return $where_sql . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . '
  227. AND ' . $this->db->sql_in_set($table_alias . 'forum_id', $forum_ids, false, true) . ')';
  228. }
  229. $where_sql .= '(' . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . '
  230. AND ' . $this->db->sql_in_set($table_alias . 'forum_id', $forum_ids) . '))';
  231. return $where_sql;
  232. }
  233. /**
  234. * Create topic/post visibility SQL for all forums on the board
  235. *
  236. * Note: Read permissions are not checked. Forums without read permissions
  237. * should be in $exclude_forum_ids
  238. *
  239. * @param $mode string Either "topic" or "post"
  240. * @param $exclude_forum_ids array Array of forum ids which are excluded
  241. * @param $table_alias string Table alias to prefix in SQL queries
  242. * @return string The appropriate combination SQL logic for topic/post_visibility
  243. */
  244. public function get_global_visibility_sql($mode, $exclude_forum_ids = array(), $table_alias = '')
  245. {
  246. $where_sqls = array();
  247. $approve_forums = array_diff(array_keys($this->auth->acl_getf('m_approve', true)), $exclude_forum_ids);
  248. $visibility_sql_overwrite = null;
  249. /**
  250. * Allow changing the result of calling get_global_visibility_sql
  251. *
  252. * @event core.phpbb_content_visibility_get_global_visibility_before
  253. * @var array where_sqls The action the user tried to execute
  254. * @var string mode Either "topic" or "post" depending on the query this is being used in
  255. * @var array exclude_forum_ids Array of forum ids the current user doesn't have access to
  256. * @var string table_alias Table alias to prefix in SQL queries
  257. * @var array approve_forums Array of forums where the user has m_approve permissions
  258. * @var string visibility_sql_overwrite Forces the function to return an implosion of where_sqls (joined by "OR")
  259. * @since 3.1.3-RC1
  260. */
  261. $vars = array(
  262. 'where_sqls',
  263. 'mode',
  264. 'exclude_forum_ids',
  265. 'table_alias',
  266. 'approve_forums',
  267. 'visibility_sql_overwrite',
  268. );
  269. extract($this->phpbb_dispatcher->trigger_event('core.phpbb_content_visibility_get_global_visibility_before', compact($vars)));
  270. if ($visibility_sql_overwrite)
  271. {
  272. return $visibility_sql_overwrite;
  273. }
  274. if (sizeof($exclude_forum_ids))
  275. {
  276. $where_sqls[] = '(' . $this->db->sql_in_set($table_alias . 'forum_id', $exclude_forum_ids, true) . '
  277. AND ' . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . ')';
  278. }
  279. else
  280. {
  281. $where_sqls[] = $table_alias . $mode . '_visibility = ' . ITEM_APPROVED;
  282. }
  283. if (sizeof($approve_forums))
  284. {
  285. $where_sqls[] = $this->db->sql_in_set($table_alias . 'forum_id', $approve_forums);
  286. return '(' . implode(' OR ', $where_sqls) . ')';
  287. }
  288. // There is only one element, so we just return that one
  289. return $where_sqls[0];
  290. }
  291. /**
  292. * Change visibility status of one post or all posts of a topic
  293. *
  294. * @param $visibility int Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
  295. * @param $post_id mixed Post ID or array of post IDs to act on,
  296. * if it is empty, all posts of topic_id will be modified
  297. * @param $topic_id int Topic where $post_id is found
  298. * @param $forum_id int Forum where $topic_id is found
  299. * @param $user_id int User performing the action
  300. * @param $time int Timestamp when the action is performed
  301. * @param $reason string Reason why the visibility was changed.
  302. * @param $is_starter bool Is this the first post of the topic changed?
  303. * @param $is_latest bool Is this the last post of the topic changed?
  304. * @param $limit_visibility mixed Limit updating per topic_id to a certain visibility
  305. * @param $limit_delete_time mixed Limit updating per topic_id to a certain deletion time
  306. * @return array Changed post data, empty array if an error occurred.
  307. */
  308. public function set_post_visibility($visibility, $post_id, $topic_id, $forum_id, $user_id, $time, $reason, $is_starter, $is_latest, $limit_visibility = false, $limit_delete_time = false)
  309. {
  310. if (!in_array($visibility, array(ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE)))
  311. {
  312. return array();
  313. }
  314. if ($post_id)
  315. {
  316. if (is_array($post_id))
  317. {
  318. $where_sql = $this->db->sql_in_set('post_id', array_map('intval', $post_id));
  319. }
  320. else
  321. {
  322. $where_sql = 'post_id = ' . (int) $post_id;
  323. }
  324. $where_sql .= ' AND topic_id = ' . (int) $topic_id;
  325. }
  326. else
  327. {
  328. $where_sql = 'topic_id = ' . (int) $topic_id;
  329. // Limit the posts to a certain visibility and deletion time
  330. // This allows us to only restore posts, that were approved
  331. // when the topic got soft deleted. So previous soft deleted
  332. // and unapproved posts are still soft deleted/unapproved
  333. if ($limit_visibility !== false)
  334. {
  335. $where_sql .= ' AND post_visibility = ' . (int) $limit_visibility;
  336. }
  337. if ($limit_delete_time !== false)
  338. {
  339. $where_sql .= ' AND post_delete_time = ' . (int) $limit_delete_time;
  340. }
  341. }
  342. $sql = 'SELECT poster_id, post_id, post_postcount, post_visibility
  343. FROM ' . $this->posts_table . '
  344. WHERE ' . $where_sql;
  345. $result = $this->db->sql_query($sql);
  346. $post_ids = $poster_postcounts = $postcounts = $postcount_visibility = array();
  347. while ($row = $this->db->sql_fetchrow($result))
  348. {
  349. $post_ids[] = (int) $row['post_id'];
  350. if ($row['post_visibility'] != $visibility)
  351. {
  352. if ($row['post_postcount'] && !isset($poster_postcounts[(int) $row['poster_id']]))
  353. {
  354. $poster_postcounts[(int) $row['poster_id']] = 1;
  355. }
  356. else if ($row['post_postcount'])
  357. {
  358. $poster_postcounts[(int) $row['poster_id']]++;
  359. }
  360. if (!isset($postcount_visibility[$row['post_visibility']]))
  361. {
  362. $postcount_visibility[$row['post_visibility']] = 1;
  363. }
  364. else
  365. {
  366. $postcount_visibility[$row['post_visibility']]++;
  367. }
  368. }
  369. }
  370. $this->db->sql_freeresult($result);
  371. if (empty($post_ids))
  372. {
  373. return array();
  374. }
  375. if (!function_exists('truncate_string'))
  376. {
  377. include($this->phpbb_root_path . 'includes/functions_content.' . $this->php_ext);
  378. }
  379. $data = array(
  380. 'post_visibility' => (int) $visibility,
  381. 'post_delete_user' => (int) $user_id,
  382. 'post_delete_time' => ((int) $time) ?: time(),
  383. 'post_delete_reason' => truncate_string($reason, 255, 255, false),
  384. );
  385. /**
  386. * Perform actions right before the query to change post visibility
  387. *
  388. * @event core.set_post_visibility_before_sql
  389. * @var int visibility Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
  390. * @var array post_id Array containing all post IDs to be modified. If blank, all posts within the topic are modified.
  391. * @var int topic_id Topic of the post IDs to be modified.
  392. * @var int forum_id Forum ID that the topic_id resides in.
  393. * @var int user_id User ID doing this action.
  394. * @var int timestamp Timestamp of this action.
  395. * @var string reason Reason specified by the user for this change.
  396. * @var bool is_starter Are we changing the topic's starter?
  397. * @var bool is_latest Are we changing the topic's latest post?
  398. * @var array data The data array for this action.
  399. * @since 3.1.10-RC1
  400. */
  401. $vars = array(
  402. 'visibility',
  403. 'post_id',
  404. 'topic_id',
  405. 'forum_id',
  406. 'user_id',
  407. 'timestamp',
  408. 'reason',
  409. 'is_starter',
  410. 'is_latest',
  411. 'data',
  412. );
  413. extract($this->phpbb_dispatcher->trigger_event('core.set_post_visibility_before_sql', compact($vars)));
  414. $sql = 'UPDATE ' . $this->posts_table . '
  415. SET ' . $this->db->sql_build_array('UPDATE', $data) . '
  416. WHERE ' . $this->db->sql_in_set('post_id', $post_ids);
  417. $this->db->sql_query($sql);
  418. // Group the authors by post count, to reduce the number of queries
  419. foreach ($poster_postcounts as $poster_id => $num_posts)
  420. {
  421. $postcounts[$num_posts][] = $poster_id;
  422. }
  423. // Update users postcounts
  424. foreach ($postcounts as $num_posts => $poster_ids)
  425. {
  426. if (in_array($visibility, array(ITEM_REAPPROVE, ITEM_DELETED)))
  427. {
  428. $sql = 'UPDATE ' . $this->users_table . '
  429. SET user_posts = 0
  430. WHERE ' . $this->db->sql_in_set('user_id', $poster_ids) . '
  431. AND user_posts < ' . $num_posts;
  432. $this->db->sql_query($sql);
  433. $sql = 'UPDATE ' . $this->users_table . '
  434. SET user_posts = user_posts - ' . $num_posts . '
  435. WHERE ' . $this->db->sql_in_set('user_id', $poster_ids) . '
  436. AND user_posts >= ' . $num_posts;
  437. $this->db->sql_query($sql);
  438. }
  439. else
  440. {
  441. $sql = 'UPDATE ' . $this->users_table . '
  442. SET user_posts = user_posts + ' . $num_posts . '
  443. WHERE ' . $this->db->sql_in_set('user_id', $poster_ids);
  444. $this->db->sql_query($sql);
  445. }
  446. }
  447. $update_topic_postcount = true;
  448. // Sync the first/last topic information if needed
  449. if (!$is_starter && $is_latest)
  450. {
  451. if (!function_exists('update_post_information'))
  452. {
  453. include($this->phpbb_root_path . 'includes/functions_posting.' . $this->php_ext);
  454. }
  455. // update_post_information can only update the last post info ...
  456. if ($topic_id)
  457. {
  458. update_post_information('topic', $topic_id, false);
  459. }
  460. if ($forum_id)
  461. {
  462. update_post_information('forum', $forum_id, false);
  463. }
  464. }
  465. else if ($is_starter && $topic_id)
  466. {
  467. if (!function_exists('sync'))
  468. {
  469. include($this->phpbb_root_path . 'includes/functions_admin.' . $this->php_ext);
  470. }
  471. // ... so we need to use sync, if the first post is changed.
  472. // The forum is resynced recursive by sync() itself.
  473. sync('topic', 'topic_id', $topic_id, true);
  474. // sync recalculates the topic replies and forum posts by itself, so we don't do that.
  475. $update_topic_postcount = false;
  476. }
  477. $topic_update_array = array();
  478. // Update the topic's reply count and the forum's post count
  479. if ($update_topic_postcount)
  480. {
  481. $field_alias = array(
  482. ITEM_APPROVED => 'posts_approved',
  483. ITEM_UNAPPROVED => 'posts_unapproved',
  484. ITEM_DELETED => 'posts_softdeleted',
  485. ITEM_REAPPROVE => 'posts_unapproved',
  486. );
  487. $cur_posts = array_fill_keys($field_alias, 0);
  488. foreach ($postcount_visibility as $post_visibility => $visibility_posts)
  489. {
  490. $cur_posts[$field_alias[(int) $post_visibility]] += $visibility_posts;
  491. }
  492. $sql_ary = array();
  493. $recipient_field = $field_alias[$visibility];
  494. foreach ($cur_posts as $field => $count)
  495. {
  496. // Decrease the count for the old statuses.
  497. if ($count && $field != $recipient_field)
  498. {
  499. $sql_ary[$field] = " - $count";
  500. }
  501. }
  502. // Add up the count from all statuses excluding the recipient status.
  503. $count_increase = array_sum(array_diff($cur_posts, array($recipient_field)));
  504. if ($count_increase)
  505. {
  506. $sql_ary[$recipient_field] = " + $count_increase";
  507. }
  508. if (sizeof($sql_ary))
  509. {
  510. $forum_sql = array();
  511. foreach ($sql_ary as $field => $value_change)
  512. {
  513. $topic_update_array[] = 'topic_' . $field . ' = topic_' . $field . $value_change;
  514. $forum_sql[] = 'forum_' . $field . ' = forum_' . $field . $value_change;
  515. }
  516. $sql = 'UPDATE ' . $this->forums_table . '
  517. SET ' . implode(', ', $forum_sql) . '
  518. WHERE forum_id = ' . (int) $forum_id;
  519. $this->db->sql_query($sql);
  520. }
  521. }
  522. if ($post_id)
  523. {
  524. $sql = 'SELECT 1 AS has_attachments
  525. FROM ' . POSTS_TABLE . '
  526. WHERE topic_id = ' . (int) $topic_id . '
  527. AND post_attachment = 1
  528. AND post_visibility = ' . ITEM_APPROVED . '
  529. AND ' . $this->db->sql_in_set('post_id', $post_id, true);
  530. $result = $this->db->sql_query_limit($sql, 1);
  531. $has_attachment = (bool) $this->db->sql_fetchfield('has_attachments');
  532. $this->db->sql_freeresult($result);
  533. if ($has_attachment && $visibility == ITEM_APPROVED)
  534. {
  535. $topic_update_array[] = 'topic_attachment = 1';
  536. }
  537. else if (!$has_attachment && $visibility != ITEM_APPROVED)
  538. {
  539. $topic_update_array[] = 'topic_attachment = 0';
  540. }
  541. }
  542. if (!empty($topic_update_array))
  543. {
  544. // Update the number for replies and posts, and update the attachments flag
  545. $sql = 'UPDATE ' . $this->topics_table . '
  546. SET ' . implode(', ', $topic_update_array) . '
  547. WHERE topic_id = ' . (int) $topic_id;
  548. $this->db->sql_query($sql);
  549. }
  550. /**
  551. * Perform actions after all steps to changing post visibility
  552. *
  553. * @event core.set_post_visibility_after
  554. * @var int visibility Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
  555. * @var array post_id Array containing all post IDs to be modified. If blank, all posts within the topic are modified.
  556. * @var int topic_id Topic of the post IDs to be modified.
  557. * @var int forum_id Forum ID that the topic_id resides in.
  558. * @var int user_id User ID doing this action.
  559. * @var int timestamp Timestamp of this action.
  560. * @var string reason Reason specified by the user for this change.
  561. * @var bool is_starter Are we changing the topic's starter?
  562. * @var bool is_latest Are we changing the topic's latest post?
  563. * @var array data The data array for this action.
  564. * @since 3.1.10-RC1
  565. */
  566. $vars = array(
  567. 'visibility',
  568. 'post_id',
  569. 'topic_id',
  570. 'forum_id',
  571. 'user_id',
  572. 'timestamp',
  573. 'reason',
  574. 'is_starter',
  575. 'is_latest',
  576. 'data',
  577. );
  578. extract($this->phpbb_dispatcher->trigger_event('core.set_post_visibility_after', compact($vars)));
  579. return $data;
  580. }
  581. /**
  582. * Set topic visibility
  583. *
  584. * Allows approving (which is akin to undeleting/restore) or soft deleting an entire topic.
  585. * Calls set_post_visibility as needed.
  586. *
  587. * Note: By default, when a soft deleted topic is restored. Only posts that
  588. * were approved at the time of soft deleting, are being restored.
  589. * Same applies to soft deleting. Only approved posts will be marked
  590. * as soft deleted.
  591. * If you want to update all posts, use the force option.
  592. *
  593. * @param $visibility int Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
  594. * @param $topic_id mixed Topic ID to act on
  595. * @param $forum_id int Forum where $topic_id is found
  596. * @param $user_id int User performing the action
  597. * @param $time int Timestamp when the action is performed
  598. * @param $reason string Reason why the visibilty was changed.
  599. * @param $force_update_all bool Force to update all posts within the topic
  600. * @return array Changed topic data, empty array if an error occured.
  601. */
  602. public function set_topic_visibility($visibility, $topic_id, $forum_id, $user_id, $time, $reason, $force_update_all = false)
  603. {
  604. if (!in_array($visibility, array(ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE)))
  605. {
  606. return array();
  607. }
  608. if (!$force_update_all)
  609. {
  610. $sql = 'SELECT topic_visibility, topic_delete_time
  611. FROM ' . $this->topics_table . '
  612. WHERE topic_id = ' . (int) $topic_id;
  613. $result = $this->db->sql_query($sql);
  614. $original_topic_data = $this->db->sql_fetchrow($result);
  615. $this->db->sql_freeresult($result);
  616. if (!$original_topic_data)
  617. {
  618. // The topic does not exist...
  619. return array();
  620. }
  621. }
  622. if (!function_exists('truncate_string'))
  623. {
  624. include($this->phpbb_root_path . 'includes/functions_content.' . $this->php_ext);
  625. }
  626. // Note, we do not set a reason for the posts, just for the topic
  627. $data = array(
  628. 'topic_visibility' => (int) $visibility,
  629. 'topic_delete_user' => (int) $user_id,
  630. 'topic_delete_time' => ((int) $time) ?: time(),
  631. 'topic_delete_reason' => truncate_string($reason, 255, 255, false),
  632. );
  633. /**
  634. * Perform actions right before the query to change topic visibility
  635. *
  636. * @event core.set_topic_visibility_before_sql
  637. * @var int visibility Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
  638. * @var int topic_id Topic of the post IDs to be modified.
  639. * @var int forum_id Forum ID that the topic_id resides in.
  640. * @var int user_id User ID doing this action.
  641. * @var int timestamp Timestamp of this action.
  642. * @var string reason Reason specified by the user for this change.
  643. * @var bool force_update_all Force an update on all posts within the topic, regardless of their current approval state.
  644. * @var array data The data array for this action.
  645. * @since 3.1.10-RC1
  646. */
  647. $vars = array(
  648. 'visibility',
  649. 'topic_id',
  650. 'forum_id',
  651. 'user_id',
  652. 'timestamp',
  653. 'reason',
  654. 'force_update_all',
  655. 'data',
  656. );
  657. extract($this->phpbb_dispatcher->trigger_event('core.set_topic_visibility_before_sql', compact($vars)));
  658. $sql = 'UPDATE ' . $this->topics_table . '
  659. SET ' . $this->db->sql_build_array('UPDATE', $data) . '
  660. WHERE topic_id = ' . (int) $topic_id;
  661. $this->db->sql_query($sql);
  662. if (!$this->db->sql_affectedrows())
  663. {
  664. return array();
  665. }
  666. if (!$force_update_all && $original_topic_data['topic_delete_time'] && $original_topic_data['topic_visibility'] == ITEM_DELETED && $visibility == ITEM_APPROVED)
  667. {
  668. // If we're restoring a topic we only restore posts, that were soft deleted through the topic soft deletion.
  669. $this->set_post_visibility($visibility, false, $topic_id, $forum_id, $user_id, $time, '', true, true, $original_topic_data['topic_visibility'], $original_topic_data['topic_delete_time']);
  670. }
  671. else if (!$force_update_all && $original_topic_data['topic_visibility'] == ITEM_APPROVED && $visibility == ITEM_DELETED)
  672. {
  673. // If we're soft deleting a topic we only mark approved posts as soft deleted.
  674. $this->set_post_visibility($visibility, false, $topic_id, $forum_id, $user_id, $time, '', true, true, $original_topic_data['topic_visibility']);
  675. }
  676. else
  677. {
  678. $this->set_post_visibility($visibility, false, $topic_id, $forum_id, $user_id, $time, '', true, true);
  679. }
  680. /**
  681. * Perform actions after all steps to changing topic visibility
  682. *
  683. * @event core.set_topic_visibility_after
  684. * @var int visibility Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
  685. * @var int topic_id Topic of the post IDs to be modified.
  686. * @var int forum_id Forum ID that the topic_id resides in.
  687. * @var int user_id User ID doing this action.
  688. * @var int timestamp Timestamp of this action.
  689. * @var string reason Reason specified by the user for this change.
  690. * @var bool force_update_all Force an update on all posts within the topic, regardless of their current approval state.
  691. * @var array data The data array for this action.
  692. * @since 3.1.10-RC1
  693. */
  694. $vars = array(
  695. 'visibility',
  696. 'topic_id',
  697. 'forum_id',
  698. 'user_id',
  699. 'timestamp',
  700. 'reason',
  701. 'force_update_all',
  702. 'data',
  703. );
  704. extract($this->phpbb_dispatcher->trigger_event('core.set_topic_visibility_after', compact($vars)));
  705. return $data;
  706. }
  707. /**
  708. * Add post to topic and forum statistics
  709. *
  710. * @param $data array Contains information from the topics table about given topic
  711. * @param &$sql_data array Populated with the SQL changes, may be empty at call time
  712. * @return null
  713. */
  714. public function add_post_to_statistic($data, &$sql_data)
  715. {
  716. $sql_data[$this->topics_table] = (($sql_data[$this->topics_table]) ? $sql_data[$this->topics_table] . ', ' : '') . 'topic_posts_approved = topic_posts_approved + 1';
  717. $sql_data[$this->forums_table] = (($sql_data[$this->forums_table]) ? $sql_data[$this->forums_table] . ', ' : '') . 'forum_posts_approved = forum_posts_approved + 1';
  718. if ($data['post_postcount'])
  719. {
  720. $sql_data[$this->users_table] = (($sql_data[$this->users_table]) ? $sql_data[$this->users_table] . ', ' : '') . 'user_posts = user_posts + 1';
  721. }
  722. $this->config->increment('num_posts', 1, false);
  723. }
  724. /**
  725. * Remove post from topic and forum statistics
  726. *
  727. * @param $data array Contains information from the topics table about given topic
  728. * @param &$sql_data array Populated with the SQL changes, may be empty at call time
  729. * @return null
  730. */
  731. public function remove_post_from_statistic($data, &$sql_data)
  732. {
  733. if ($data['post_visibility'] == ITEM_APPROVED)
  734. {
  735. $sql_data[$this->topics_table] = ((!empty($sql_data[$this->topics_table])) ? $sql_data[$this->topics_table] . ', ' : '') . 'topic_posts_approved = topic_posts_approved - 1';
  736. $sql_data[$this->forums_table] = ((!empty($sql_data[$this->forums_table])) ? $sql_data[$this->forums_table] . ', ' : '') . 'forum_posts_approved = forum_posts_approved - 1';
  737. if ($data['post_postcount'])
  738. {
  739. $sql_data[$this->users_table] = ((!empty($sql_data[$this->users_table])) ? $sql_data[$this->users_table] . ', ' : '') . 'user_posts = user_posts - 1';
  740. }
  741. $this->config->increment('num_posts', -1, false);
  742. }
  743. else if ($data['post_visibility'] == ITEM_UNAPPROVED || $data['post_visibility'] == ITEM_REAPPROVE)
  744. {
  745. $sql_data[FORUMS_TABLE] = (($sql_data[FORUMS_TABLE]) ? $sql_data[FORUMS_TABLE] . ', ' : '') . 'forum_posts_unapproved = forum_posts_unapproved - 1';
  746. $sql_data[TOPICS_TABLE] = (($sql_data[TOPICS_TABLE]) ? $sql_data[TOPICS_TABLE] . ', ' : '') . 'topic_posts_unapproved = topic_posts_unapproved - 1';
  747. }
  748. else if ($data['post_visibility'] == ITEM_DELETED)
  749. {
  750. $sql_data[FORUMS_TABLE] = (($sql_data[FORUMS_TABLE]) ? $sql_data[FORUMS_TABLE] . ', ' : '') . 'forum_posts_softdeleted = forum_posts_softdeleted - 1';
  751. $sql_data[TOPICS_TABLE] = (($sql_data[TOPICS_TABLE]) ? $sql_data[TOPICS_TABLE] . ', ' : '') . 'topic_posts_softdeleted = topic_posts_softdeleted - 1';
  752. }
  753. }
  754. /**
  755. * Remove topic from forum statistics
  756. *
  757. * @param $data array Post and topic data
  758. * @param &$sql_data array Populated with the SQL changes, may be empty at call time
  759. * @return null
  760. */
  761. public function remove_topic_from_statistic($data, &$sql_data)
  762. {
  763. if ($data['topic_visibility'] == ITEM_APPROVED)
  764. {
  765. $sql_data[FORUMS_TABLE] .= 'forum_posts_approved = forum_posts_approved - 1, forum_topics_approved = forum_topics_approved - 1';
  766. if ($data['post_postcount'])
  767. {
  768. $sql_data[$this->users_table] = ((!empty($sql_data[$this->users_table])) ? $sql_data[$this->users_table] . ', ' : '') . 'user_posts = user_posts - 1';
  769. }
  770. }
  771. else if ($data['topic_visibility'] == ITEM_UNAPPROVED || $data['post_visibility'] == ITEM_REAPPROVE)
  772. {
  773. $sql_data[FORUMS_TABLE] .= 'forum_posts_unapproved = forum_posts_unapproved - 1, forum_topics_unapproved = forum_topics_unapproved - 1';
  774. }
  775. else if ($data['topic_visibility'] == ITEM_DELETED)
  776. {
  777. $sql_data[FORUMS_TABLE] .= 'forum_posts_softdeleted = forum_posts_softdeleted - 1, forum_topics_softdeleted = forum_topics_softdeleted - 1';
  778. }
  779. }
  780. }