PageRenderTime 55ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/phpBB/includes/functions_admin.php

http://github.com/phpbb/phpbb3
PHP | 3231 lines | 2264 code | 485 blank | 482 comment | 371 complexity | 51c14e5d35764e3bc063a166d32489c6 MD5 | raw file
Possible License(s): AGPL-1.0

Large files files are truncated, but you can click here to view the full 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. /**
  14. * @ignore
  15. */
  16. if (!defined('IN_PHPBB'))
  17. {
  18. exit;
  19. }
  20. /**
  21. * Recalculate Nested Sets
  22. *
  23. * @param int $new_id first left_id (should start with 1)
  24. * @param string $pkey primary key-column (containing the id for the parent_id of the children)
  25. * @param string $table constant or fullname of the table
  26. * @param int $parent_id parent_id of the current set (default = 0)
  27. * @param array $where contains strings to compare closer on the where statement (additional)
  28. */
  29. function recalc_nested_sets(&$new_id, $pkey, $table, $parent_id = 0, $where = array())
  30. {
  31. global $db;
  32. $sql = 'SELECT *
  33. FROM ' . $table . '
  34. WHERE parent_id = ' . (int) $parent_id .
  35. ((!empty($where)) ? ' AND ' . implode(' AND ', $where) : '') . '
  36. ORDER BY left_id ASC';
  37. $result = $db->sql_query($sql);
  38. while ($row = $db->sql_fetchrow($result))
  39. {
  40. // First we update the left_id for this module
  41. if ($row['left_id'] != $new_id)
  42. {
  43. $db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('left_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}");
  44. }
  45. $new_id++;
  46. // Then we go through any children and update their left/right id's
  47. recalc_nested_sets($new_id, $pkey, $table, $row[$pkey], $where);
  48. // Then we come back and update the right_id for this module
  49. if ($row['right_id'] != $new_id)
  50. {
  51. $db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('right_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}");
  52. }
  53. $new_id++;
  54. }
  55. $db->sql_freeresult($result);
  56. }
  57. /**
  58. * Simple version of jumpbox, just lists authed forums
  59. */
  60. function make_forum_select($select_id = false, $ignore_id = false, $ignore_acl = false, $ignore_nonpost = false, $ignore_emptycat = true, $only_acl_post = false, $return_array = false)
  61. {
  62. global $db, $auth, $phpbb_dispatcher;
  63. // This query is identical to the jumpbox one
  64. $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, forum_flags, forum_options, left_id, right_id
  65. FROM ' . FORUMS_TABLE . '
  66. ORDER BY left_id ASC';
  67. $result = $db->sql_query($sql, 600);
  68. $rowset = array();
  69. while ($row = $db->sql_fetchrow($result))
  70. {
  71. $rowset[(int) $row['forum_id']] = $row;
  72. }
  73. $db->sql_freeresult($result);
  74. $right = 0;
  75. $padding_store = array('0' => '');
  76. $padding = '';
  77. $forum_list = ($return_array) ? array() : '';
  78. /**
  79. * Modify the forum list data
  80. *
  81. * @event core.make_forum_select_modify_forum_list
  82. * @var array rowset Array with the forums list data
  83. * @since 3.1.10-RC1
  84. */
  85. $vars = array('rowset');
  86. extract($phpbb_dispatcher->trigger_event('core.make_forum_select_modify_forum_list', compact($vars)));
  87. // Sometimes it could happen that forums will be displayed here not be displayed within the index page
  88. // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
  89. // If this happens, the padding could be "broken"
  90. foreach ($rowset as $row)
  91. {
  92. if ($row['left_id'] < $right)
  93. {
  94. $padding .= '&nbsp; &nbsp;';
  95. $padding_store[$row['parent_id']] = $padding;
  96. }
  97. else if ($row['left_id'] > $right + 1)
  98. {
  99. $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : '';
  100. }
  101. $right = $row['right_id'];
  102. $disabled = false;
  103. if (!$ignore_acl && $auth->acl_gets(array('f_list', 'a_forum', 'a_forumadd', 'a_forumdel'), $row['forum_id']))
  104. {
  105. if ($only_acl_post && !$auth->acl_get('f_post', $row['forum_id']) || (!$auth->acl_get('m_approve', $row['forum_id']) && !$auth->acl_get('f_noapprove', $row['forum_id'])))
  106. {
  107. $disabled = true;
  108. }
  109. }
  110. else if (!$ignore_acl)
  111. {
  112. continue;
  113. }
  114. if (
  115. ((is_array($ignore_id) && in_array($row['forum_id'], $ignore_id)) || $row['forum_id'] == $ignore_id)
  116. ||
  117. // Non-postable forum with no subforums, don't display
  118. ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']) && $ignore_emptycat)
  119. ||
  120. ($row['forum_type'] != FORUM_POST && $ignore_nonpost)
  121. )
  122. {
  123. $disabled = true;
  124. }
  125. if ($return_array)
  126. {
  127. // Include some more information...
  128. $selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? true : false) : (($row['forum_id'] == $select_id) ? true : false);
  129. $forum_list[$row['forum_id']] = array_merge(array('padding' => $padding, 'selected' => ($selected && !$disabled), 'disabled' => $disabled), $row);
  130. }
  131. else
  132. {
  133. $selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? ' selected="selected"' : '') : (($row['forum_id'] == $select_id) ? ' selected="selected"' : '');
  134. $forum_list .= '<option value="' . $row['forum_id'] . '"' . (($disabled) ? ' disabled="disabled" class="disabled-option"' : $selected) . '>' . $padding . $row['forum_name'] . '</option>';
  135. }
  136. }
  137. unset($padding_store, $rowset);
  138. return $forum_list;
  139. }
  140. /**
  141. * Generate size select options
  142. */
  143. function size_select_options($size_compare)
  144. {
  145. global $user;
  146. $size_types_text = array($user->lang['BYTES'], $user->lang['KIB'], $user->lang['MIB']);
  147. $size_types = array('b', 'kb', 'mb');
  148. $s_size_options = '';
  149. for ($i = 0, $size = count($size_types_text); $i < $size; $i++)
  150. {
  151. $selected = ($size_compare == $size_types[$i]) ? ' selected="selected"' : '';
  152. $s_size_options .= '<option value="' . $size_types[$i] . '"' . $selected . '>' . $size_types_text[$i] . '</option>';
  153. }
  154. return $s_size_options;
  155. }
  156. /**
  157. * Generate list of groups (option fields without select)
  158. *
  159. * @param int $group_id The default group id to mark as selected
  160. * @param array $exclude_ids The group ids to exclude from the list, false (default) if you whish to exclude no id
  161. * @param int $manage_founder If set to false (default) all groups are returned, if 0 only those groups returned not being managed by founders only, if 1 only those groups returned managed by founders only.
  162. *
  163. * @return string The list of options.
  164. */
  165. function group_select_options($group_id, $exclude_ids = false, $manage_founder = false)
  166. {
  167. global $db, $config, $phpbb_container;
  168. /** @var \phpbb\group\helper $group_helper */
  169. $group_helper = $phpbb_container->get('group_helper');
  170. $exclude_sql = ($exclude_ids !== false && count($exclude_ids)) ? 'WHERE ' . $db->sql_in_set('group_id', array_map('intval', $exclude_ids), true) : '';
  171. $sql_and = (!$config['coppa_enable']) ? (($exclude_sql) ? ' AND ' : ' WHERE ') . "group_name <> 'REGISTERED_COPPA'" : '';
  172. $sql_founder = ($manage_founder !== false) ? (($exclude_sql || $sql_and) ? ' AND ' : ' WHERE ') . 'group_founder_manage = ' . (int) $manage_founder : '';
  173. $sql = 'SELECT group_id, group_name, group_type
  174. FROM ' . GROUPS_TABLE . "
  175. $exclude_sql
  176. $sql_and
  177. $sql_founder
  178. ORDER BY group_type DESC, group_name ASC";
  179. $result = $db->sql_query($sql);
  180. $s_group_options = '';
  181. while ($row = $db->sql_fetchrow($result))
  182. {
  183. $selected = ($row['group_id'] == $group_id) ? ' selected="selected"' : '';
  184. $s_group_options .= '<option' . (($row['group_type'] == GROUP_SPECIAL) ? ' class="sep"' : '') . ' value="' . $row['group_id'] . '"' . $selected . '>' . $group_helper->get_name($row['group_name']) . '</option>';
  185. }
  186. $db->sql_freeresult($result);
  187. return $s_group_options;
  188. }
  189. /**
  190. * Obtain authed forums list
  191. */
  192. function get_forum_list($acl_list = 'f_list', $id_only = true, $postable_only = false, $no_cache = false)
  193. {
  194. global $db, $auth, $phpbb_dispatcher;
  195. static $forum_rows;
  196. if (!isset($forum_rows))
  197. {
  198. // This query is identical to the jumpbox one
  199. $expire_time = ($no_cache) ? 0 : 600;
  200. $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id
  201. FROM ' . FORUMS_TABLE . '
  202. ORDER BY left_id ASC';
  203. $result = $db->sql_query($sql, $expire_time);
  204. $forum_rows = array();
  205. $right = $padding = 0;
  206. $padding_store = array('0' => 0);
  207. while ($row = $db->sql_fetchrow($result))
  208. {
  209. if ($row['left_id'] < $right)
  210. {
  211. $padding++;
  212. $padding_store[$row['parent_id']] = $padding;
  213. }
  214. else if ($row['left_id'] > $right + 1)
  215. {
  216. // Ok, if the $padding_store for this parent is empty there is something wrong. For now we will skip over it.
  217. // @todo digging deep to find out "how" this can happen.
  218. $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : $padding;
  219. }
  220. $right = $row['right_id'];
  221. $row['padding'] = $padding;
  222. $forum_rows[] = $row;
  223. }
  224. $db->sql_freeresult($result);
  225. unset($padding_store);
  226. }
  227. $rowset = array();
  228. foreach ($forum_rows as $row)
  229. {
  230. if ($postable_only && $row['forum_type'] != FORUM_POST)
  231. {
  232. continue;
  233. }
  234. if ($acl_list == '' || ($acl_list != '' && $auth->acl_gets($acl_list, $row['forum_id'])))
  235. {
  236. $rowset[] = ($id_only) ? (int) $row['forum_id'] : $row;
  237. }
  238. }
  239. /**
  240. * Modify the forum list data
  241. *
  242. * @event core.get_forum_list_modify_data
  243. * @var array rowset Array with the forum list data
  244. * @since 3.1.10-RC1
  245. */
  246. $vars = array('rowset');
  247. extract($phpbb_dispatcher->trigger_event('core.get_forum_list_modify_data', compact($vars)));
  248. return $rowset;
  249. }
  250. /**
  251. * Get forum branch
  252. */
  253. function get_forum_branch($forum_id, $type = 'all', $order = 'descending', $include_forum = true)
  254. {
  255. global $db;
  256. switch ($type)
  257. {
  258. case 'parents':
  259. $condition = 'f1.left_id BETWEEN f2.left_id AND f2.right_id';
  260. break;
  261. case 'children':
  262. $condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id';
  263. break;
  264. default:
  265. $condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id OR f1.left_id BETWEEN f2.left_id AND f2.right_id';
  266. break;
  267. }
  268. $rows = array();
  269. $sql = 'SELECT f2.*
  270. FROM ' . FORUMS_TABLE . ' f1
  271. LEFT JOIN ' . FORUMS_TABLE . " f2 ON ($condition)
  272. WHERE f1.forum_id = $forum_id
  273. ORDER BY f2.left_id " . (($order == 'descending') ? 'ASC' : 'DESC');
  274. $result = $db->sql_query($sql);
  275. while ($row = $db->sql_fetchrow($result))
  276. {
  277. if (!$include_forum && $row['forum_id'] == $forum_id)
  278. {
  279. continue;
  280. }
  281. $rows[] = $row;
  282. }
  283. $db->sql_freeresult($result);
  284. return $rows;
  285. }
  286. /**
  287. * Copies permissions from one forum to others
  288. *
  289. * @param int $src_forum_id The source forum we want to copy permissions from
  290. * @param array $dest_forum_ids The destination forum(s) we want to copy to
  291. * @param bool $clear_dest_perms True if destination permissions should be deleted
  292. * @param bool $add_log True if log entry should be added
  293. *
  294. * @return bool False on error
  295. */
  296. function copy_forum_permissions($src_forum_id, $dest_forum_ids, $clear_dest_perms = true, $add_log = true)
  297. {
  298. global $db, $user, $phpbb_log;
  299. // Only one forum id specified
  300. if (!is_array($dest_forum_ids))
  301. {
  302. $dest_forum_ids = array($dest_forum_ids);
  303. }
  304. // Make sure forum ids are integers
  305. $src_forum_id = (int) $src_forum_id;
  306. $dest_forum_ids = array_map('intval', $dest_forum_ids);
  307. // No source forum or no destination forums specified
  308. if (empty($src_forum_id) || empty($dest_forum_ids))
  309. {
  310. return false;
  311. }
  312. // Check if source forum exists
  313. $sql = 'SELECT forum_name
  314. FROM ' . FORUMS_TABLE . '
  315. WHERE forum_id = ' . $src_forum_id;
  316. $result = $db->sql_query($sql);
  317. $src_forum_name = $db->sql_fetchfield('forum_name');
  318. $db->sql_freeresult($result);
  319. // Source forum doesn't exist
  320. if (empty($src_forum_name))
  321. {
  322. return false;
  323. }
  324. // Check if destination forums exists
  325. $sql = 'SELECT forum_id, forum_name
  326. FROM ' . FORUMS_TABLE . '
  327. WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
  328. $result = $db->sql_query($sql);
  329. $dest_forum_ids = $dest_forum_names = array();
  330. while ($row = $db->sql_fetchrow($result))
  331. {
  332. $dest_forum_ids[] = (int) $row['forum_id'];
  333. $dest_forum_names[] = $row['forum_name'];
  334. }
  335. $db->sql_freeresult($result);
  336. // No destination forum exists
  337. if (empty($dest_forum_ids))
  338. {
  339. return false;
  340. }
  341. // From the mysql documentation:
  342. // Prior to MySQL 4.0.14, the target table of the INSERT statement cannot appear
  343. // in the FROM clause of the SELECT part of the query. This limitation is lifted in 4.0.14.
  344. // Due to this we stay on the safe side if we do the insertion "the manual way"
  345. // Rowsets we're going to insert
  346. $users_sql_ary = $groups_sql_ary = array();
  347. // Query acl users table for source forum data
  348. $sql = 'SELECT user_id, auth_option_id, auth_role_id, auth_setting
  349. FROM ' . ACL_USERS_TABLE . '
  350. WHERE forum_id = ' . $src_forum_id;
  351. $result = $db->sql_query($sql);
  352. while ($row = $db->sql_fetchrow($result))
  353. {
  354. $row = array(
  355. 'user_id' => (int) $row['user_id'],
  356. 'auth_option_id' => (int) $row['auth_option_id'],
  357. 'auth_role_id' => (int) $row['auth_role_id'],
  358. 'auth_setting' => (int) $row['auth_setting'],
  359. );
  360. foreach ($dest_forum_ids as $dest_forum_id)
  361. {
  362. $users_sql_ary[] = $row + array('forum_id' => $dest_forum_id);
  363. }
  364. }
  365. $db->sql_freeresult($result);
  366. // Query acl groups table for source forum data
  367. $sql = 'SELECT group_id, auth_option_id, auth_role_id, auth_setting
  368. FROM ' . ACL_GROUPS_TABLE . '
  369. WHERE forum_id = ' . $src_forum_id;
  370. $result = $db->sql_query($sql);
  371. while ($row = $db->sql_fetchrow($result))
  372. {
  373. $row = array(
  374. 'group_id' => (int) $row['group_id'],
  375. 'auth_option_id' => (int) $row['auth_option_id'],
  376. 'auth_role_id' => (int) $row['auth_role_id'],
  377. 'auth_setting' => (int) $row['auth_setting'],
  378. );
  379. foreach ($dest_forum_ids as $dest_forum_id)
  380. {
  381. $groups_sql_ary[] = $row + array('forum_id' => $dest_forum_id);
  382. }
  383. }
  384. $db->sql_freeresult($result);
  385. $db->sql_transaction('begin');
  386. // Clear current permissions of destination forums
  387. if ($clear_dest_perms)
  388. {
  389. $sql = 'DELETE FROM ' . ACL_USERS_TABLE . '
  390. WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
  391. $db->sql_query($sql);
  392. $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
  393. WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
  394. $db->sql_query($sql);
  395. }
  396. $db->sql_multi_insert(ACL_USERS_TABLE, $users_sql_ary);
  397. $db->sql_multi_insert(ACL_GROUPS_TABLE, $groups_sql_ary);
  398. if ($add_log)
  399. {
  400. $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_FORUM_COPIED_PERMISSIONS', false, array($src_forum_name, implode(', ', $dest_forum_names)));
  401. }
  402. $db->sql_transaction('commit');
  403. return true;
  404. }
  405. /**
  406. * Get physical file listing
  407. */
  408. function filelist($rootdir, $dir = '', $type = 'gif|jpg|jpeg|png')
  409. {
  410. $matches = array($dir => array());
  411. // Remove initial / if present
  412. $rootdir = (substr($rootdir, 0, 1) == '/') ? substr($rootdir, 1) : $rootdir;
  413. // Add closing / if not present
  414. $rootdir = ($rootdir && substr($rootdir, -1) != '/') ? $rootdir . '/' : $rootdir;
  415. // Remove initial / if present
  416. $dir = (substr($dir, 0, 1) == '/') ? substr($dir, 1) : $dir;
  417. // Add closing / if not present
  418. $dir = ($dir && substr($dir, -1) != '/') ? $dir . '/' : $dir;
  419. if (!is_dir($rootdir . $dir))
  420. {
  421. return $matches;
  422. }
  423. $dh = @opendir($rootdir . $dir);
  424. if (!$dh)
  425. {
  426. return $matches;
  427. }
  428. while (($fname = readdir($dh)) !== false)
  429. {
  430. if (is_file("$rootdir$dir$fname"))
  431. {
  432. if (filesize("$rootdir$dir$fname") && preg_match('#\.' . $type . '$#i', $fname))
  433. {
  434. $matches[$dir][] = $fname;
  435. }
  436. }
  437. else if ($fname[0] != '.' && is_dir("$rootdir$dir$fname"))
  438. {
  439. $matches += filelist($rootdir, "$dir$fname", $type);
  440. }
  441. }
  442. closedir($dh);
  443. return $matches;
  444. }
  445. /**
  446. * Move topic(s)
  447. */
  448. function move_topics($topic_ids, $forum_id, $auto_sync = true)
  449. {
  450. global $db, $phpbb_dispatcher;
  451. if (empty($topic_ids))
  452. {
  453. return;
  454. }
  455. $forum_ids = array($forum_id);
  456. if (!is_array($topic_ids))
  457. {
  458. $topic_ids = array($topic_ids);
  459. }
  460. /**
  461. * Perform additional actions before topics move
  462. *
  463. * @event core.move_topics_before
  464. * @var array topic_ids Array of the moved topic ids
  465. * @var string forum_id The forum id from where the topics are moved
  466. * @since 3.2.9-RC1
  467. */
  468. $vars = array(
  469. 'topic_ids',
  470. 'forum_id',
  471. );
  472. extract($phpbb_dispatcher->trigger_event('core.move_topics_before', compact($vars)));
  473. $sql = 'DELETE FROM ' . TOPICS_TABLE . '
  474. WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids) . '
  475. AND forum_id = ' . $forum_id;
  476. $db->sql_query($sql);
  477. if ($auto_sync)
  478. {
  479. $sql = 'SELECT DISTINCT forum_id
  480. FROM ' . TOPICS_TABLE . '
  481. WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
  482. $result = $db->sql_query($sql);
  483. while ($row = $db->sql_fetchrow($result))
  484. {
  485. $forum_ids[] = $row['forum_id'];
  486. }
  487. $db->sql_freeresult($result);
  488. }
  489. $table_ary = array(TOPICS_TABLE, POSTS_TABLE, LOG_TABLE, DRAFTS_TABLE, TOPICS_TRACK_TABLE);
  490. /**
  491. * Perform additional actions before topics move
  492. *
  493. * @event core.move_topics_before_query
  494. * @var array table_ary Array of tables from which forum_id will be updated for all rows that hold the moved topics
  495. * @var array topic_ids Array of the moved topic ids
  496. * @var string forum_id The forum id from where the topics are moved
  497. * @var array forum_ids Array of the forums where the topics are moving (includes also forum_id)
  498. * @var bool auto_sync Whether or not to perform auto sync
  499. * @since 3.1.5-RC1
  500. */
  501. $vars = array(
  502. 'table_ary',
  503. 'topic_ids',
  504. 'forum_id',
  505. 'forum_ids',
  506. 'auto_sync',
  507. );
  508. extract($phpbb_dispatcher->trigger_event('core.move_topics_before_query', compact($vars)));
  509. foreach ($table_ary as $table)
  510. {
  511. $sql = "UPDATE $table
  512. SET forum_id = $forum_id
  513. WHERE " . $db->sql_in_set('topic_id', $topic_ids);
  514. $db->sql_query($sql);
  515. }
  516. unset($table_ary);
  517. /**
  518. * Perform additional actions after topics move
  519. *
  520. * @event core.move_topics_after
  521. * @var array topic_ids Array of the moved topic ids
  522. * @var string forum_id The forum id from where the topics were moved
  523. * @var array forum_ids Array of the forums where the topics were moved (includes also forum_id)
  524. * @since 3.2.9-RC1
  525. */
  526. $vars = array(
  527. 'topic_ids',
  528. 'forum_id',
  529. 'forum_ids',
  530. );
  531. extract($phpbb_dispatcher->trigger_event('core.move_topics_after', compact($vars)));
  532. if ($auto_sync)
  533. {
  534. sync('forum', 'forum_id', $forum_ids, true, true);
  535. unset($forum_ids);
  536. }
  537. }
  538. /**
  539. * Move post(s)
  540. */
  541. function move_posts($post_ids, $topic_id, $auto_sync = true)
  542. {
  543. global $db, $phpbb_dispatcher;
  544. if (!is_array($post_ids))
  545. {
  546. $post_ids = array($post_ids);
  547. }
  548. $forum_ids = array();
  549. $topic_ids = array($topic_id);
  550. $sql = 'SELECT DISTINCT topic_id, forum_id
  551. FROM ' . POSTS_TABLE . '
  552. WHERE ' . $db->sql_in_set('post_id', $post_ids);
  553. $result = $db->sql_query($sql);
  554. while ($row = $db->sql_fetchrow($result))
  555. {
  556. $forum_ids[] = (int) $row['forum_id'];
  557. $topic_ids[] = (int) $row['topic_id'];
  558. }
  559. $db->sql_freeresult($result);
  560. $sql = 'SELECT forum_id
  561. FROM ' . TOPICS_TABLE . '
  562. WHERE topic_id = ' . $topic_id;
  563. $result = $db->sql_query($sql);
  564. $forum_row = $db->sql_fetchrow($result);
  565. $db->sql_freeresult($result);
  566. if (!$forum_row)
  567. {
  568. trigger_error('NO_TOPIC');
  569. }
  570. /**
  571. * Perform additional actions before moving posts
  572. *
  573. * @event core.move_posts_before
  574. * @var array post_ids Array of post ids to move
  575. * @var int topic_id The topic id the posts are moved to
  576. * @var bool auto_sync Whether or not to perform auto sync
  577. * @var array forum_ids Array of the forum ids the posts are moved from
  578. * @var array topic_ids Array of the topic ids the posts are moved from
  579. * @var array forum_row Array with the forum id of the topic the posts are moved to
  580. * @since 3.1.7-RC1
  581. */
  582. $vars = array(
  583. 'post_ids',
  584. 'topic_id',
  585. 'auto_sync',
  586. 'forum_ids',
  587. 'topic_ids',
  588. 'forum_row',
  589. );
  590. extract($phpbb_dispatcher->trigger_event('core.move_posts_before', compact($vars)));
  591. $sql = 'UPDATE ' . POSTS_TABLE . '
  592. SET forum_id = ' . (int) $forum_row['forum_id'] . ", topic_id = $topic_id
  593. WHERE " . $db->sql_in_set('post_id', $post_ids);
  594. $db->sql_query($sql);
  595. $sql = 'UPDATE ' . ATTACHMENTS_TABLE . "
  596. SET topic_id = $topic_id, in_message = 0
  597. WHERE " . $db->sql_in_set('post_msg_id', $post_ids);
  598. $db->sql_query($sql);
  599. /**
  600. * Perform additional actions after moving posts
  601. *
  602. * @event core.move_posts_after
  603. * @var array post_ids Array of the moved post ids
  604. * @var int topic_id The topic id the posts are moved to
  605. * @var bool auto_sync Whether or not to perform auto sync
  606. * @var array forum_ids Array of the forum ids the posts are moved from
  607. * @var array topic_ids Array of the topic ids the posts are moved from
  608. * @var array forum_row Array with the forum id of the topic the posts are moved to
  609. * @since 3.1.7-RC1
  610. */
  611. $vars = array(
  612. 'post_ids',
  613. 'topic_id',
  614. 'auto_sync',
  615. 'forum_ids',
  616. 'topic_ids',
  617. 'forum_row',
  618. );
  619. extract($phpbb_dispatcher->trigger_event('core.move_posts_after', compact($vars)));
  620. if ($auto_sync)
  621. {
  622. $forum_ids[] = (int) $forum_row['forum_id'];
  623. sync('topic_reported', 'topic_id', $topic_ids);
  624. sync('topic_attachment', 'topic_id', $topic_ids);
  625. sync('topic', 'topic_id', $topic_ids, true);
  626. sync('forum', 'forum_id', $forum_ids, true, true);
  627. /**
  628. * Perform additional actions after move post sync
  629. *
  630. * @event core.move_posts_sync_after
  631. * @var array post_ids Array of the moved post ids
  632. * @var int topic_id The topic id the posts are moved to
  633. * @var bool auto_sync Whether or not to perform auto sync
  634. * @var array forum_ids Array of the forum ids the posts are moved from
  635. * @var array topic_ids Array of the topic ids the posts are moved from
  636. * @var array forum_row Array with the forum id of the topic the posts are moved to
  637. * @since 3.1.11-RC1
  638. */
  639. $vars = array(
  640. 'post_ids',
  641. 'topic_id',
  642. 'auto_sync',
  643. 'forum_ids',
  644. 'topic_ids',
  645. 'forum_row',
  646. );
  647. extract($phpbb_dispatcher->trigger_event('core.move_posts_sync_after', compact($vars)));
  648. }
  649. // Update posted information
  650. update_posted_info($topic_ids);
  651. }
  652. /**
  653. * Remove topic(s)
  654. */
  655. function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_sync = true, $call_delete_posts = true)
  656. {
  657. global $db, $config, $phpbb_container, $phpbb_dispatcher;
  658. $approved_topics = 0;
  659. $forum_ids = $topic_ids = array();
  660. if ($where_type === 'range')
  661. {
  662. $where_clause = $where_ids;
  663. }
  664. else
  665. {
  666. $where_ids = (is_array($where_ids)) ? array_unique($where_ids) : array($where_ids);
  667. if (!count($where_ids))
  668. {
  669. return array('topics' => 0, 'posts' => 0);
  670. }
  671. $where_clause = $db->sql_in_set($where_type, $where_ids);
  672. }
  673. // Making sure that delete_posts does not call delete_topics again...
  674. $return = array(
  675. 'posts' => ($call_delete_posts) ? delete_posts($where_type, $where_ids, false, true, $post_count_sync, false) : 0,
  676. );
  677. $sql = 'SELECT topic_id, forum_id, topic_visibility, topic_moved_id
  678. FROM ' . TOPICS_TABLE . '
  679. WHERE ' . $where_clause;
  680. $result = $db->sql_query($sql);
  681. while ($row = $db->sql_fetchrow($result))
  682. {
  683. $forum_ids[] = $row['forum_id'];
  684. $topic_ids[] = $row['topic_id'];
  685. if ($row['topic_visibility'] == ITEM_APPROVED && !$row['topic_moved_id'])
  686. {
  687. $approved_topics++;
  688. }
  689. }
  690. $db->sql_freeresult($result);
  691. $return['topics'] = count($topic_ids);
  692. if (!count($topic_ids))
  693. {
  694. return $return;
  695. }
  696. $db->sql_transaction('begin');
  697. $table_ary = array(BOOKMARKS_TABLE, TOPICS_TRACK_TABLE, TOPICS_POSTED_TABLE, POLL_VOTES_TABLE, POLL_OPTIONS_TABLE, TOPICS_WATCH_TABLE, TOPICS_TABLE);
  698. /**
  699. * Perform additional actions before topic(s) deletion
  700. *
  701. * @event core.delete_topics_before_query
  702. * @var array table_ary Array of tables from which all rows will be deleted that hold a topic_id occuring in topic_ids
  703. * @var array topic_ids Array of topic ids to delete
  704. * @since 3.1.4-RC1
  705. */
  706. $vars = array(
  707. 'table_ary',
  708. 'topic_ids',
  709. );
  710. extract($phpbb_dispatcher->trigger_event('core.delete_topics_before_query', compact($vars)));
  711. foreach ($table_ary as $table)
  712. {
  713. $sql = "DELETE FROM $table
  714. WHERE " . $db->sql_in_set('topic_id', $topic_ids);
  715. $db->sql_query($sql);
  716. }
  717. unset($table_ary);
  718. /**
  719. * Perform additional actions after topic(s) deletion
  720. *
  721. * @event core.delete_topics_after_query
  722. * @var array topic_ids Array of topic ids that were deleted
  723. * @since 3.1.4-RC1
  724. */
  725. $vars = array(
  726. 'topic_ids',
  727. );
  728. extract($phpbb_dispatcher->trigger_event('core.delete_topics_after_query', compact($vars)));
  729. $moved_topic_ids = array();
  730. // update the other forums
  731. $sql = 'SELECT topic_id, forum_id
  732. FROM ' . TOPICS_TABLE . '
  733. WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids);
  734. $result = $db->sql_query($sql);
  735. while ($row = $db->sql_fetchrow($result))
  736. {
  737. $forum_ids[] = $row['forum_id'];
  738. $moved_topic_ids[] = $row['topic_id'];
  739. }
  740. $db->sql_freeresult($result);
  741. if (count($moved_topic_ids))
  742. {
  743. $sql = 'DELETE FROM ' . TOPICS_TABLE . '
  744. WHERE ' . $db->sql_in_set('topic_id', $moved_topic_ids);
  745. $db->sql_query($sql);
  746. }
  747. $db->sql_transaction('commit');
  748. if ($auto_sync)
  749. {
  750. sync('forum', 'forum_id', array_unique($forum_ids), true, true);
  751. sync('topic_reported', $where_type, $where_ids);
  752. }
  753. if ($approved_topics)
  754. {
  755. $config->increment('num_topics', $approved_topics * (-1), false);
  756. }
  757. /* @var $phpbb_notifications \phpbb\notification\manager */
  758. $phpbb_notifications = $phpbb_container->get('notification_manager');
  759. $phpbb_notifications->delete_notifications(array(
  760. 'notification.type.topic',
  761. 'notification.type.approve_topic',
  762. 'notification.type.topic_in_queue',
  763. ), $topic_ids);
  764. return $return;
  765. }
  766. /**
  767. * Remove post(s)
  768. */
  769. function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync = true, $post_count_sync = true, $call_delete_topics = true)
  770. {
  771. global $db, $config, $phpbb_root_path, $phpEx, $auth, $user, $phpbb_container, $phpbb_dispatcher;
  772. // Notifications types to delete
  773. $delete_notifications_types = array(
  774. 'notification.type.quote',
  775. 'notification.type.approve_post',
  776. 'notification.type.post_in_queue',
  777. 'notification.type.report_post',
  778. );
  779. /**
  780. * Perform additional actions before post(s) deletion
  781. *
  782. * @event core.delete_posts_before
  783. * @var string where_type Variable containing posts deletion mode
  784. * @var mixed where_ids Array or comma separated list of posts ids to delete
  785. * @var bool auto_sync Flag indicating if topics/forums should be synchronized
  786. * @var bool posted_sync Flag indicating if topics_posted table should be resynchronized
  787. * @var bool post_count_sync Flag indicating if posts count should be resynchronized
  788. * @var bool call_delete_topics Flag indicating if topics having no posts should be deleted
  789. * @var array delete_notifications_types Array with notifications types to delete
  790. * @since 3.1.0-a4
  791. */
  792. $vars = array(
  793. 'where_type',
  794. 'where_ids',
  795. 'auto_sync',
  796. 'posted_sync',
  797. 'post_count_sync',
  798. 'call_delete_topics',
  799. 'delete_notifications_types',
  800. );
  801. extract($phpbb_dispatcher->trigger_event('core.delete_posts_before', compact($vars)));
  802. if ($where_type === 'range')
  803. {
  804. $where_clause = $where_ids;
  805. }
  806. else
  807. {
  808. if (is_array($where_ids))
  809. {
  810. $where_ids = array_unique($where_ids);
  811. }
  812. else
  813. {
  814. $where_ids = array($where_ids);
  815. }
  816. if (!count($where_ids))
  817. {
  818. return false;
  819. }
  820. $where_ids = array_map('intval', $where_ids);
  821. /* Possible code for splitting post deletion
  822. if (count($where_ids) >= 1001)
  823. {
  824. // Split into chunks of 1000
  825. $chunks = array_chunk($where_ids, 1000);
  826. foreach ($chunks as $_where_ids)
  827. {
  828. delete_posts($where_type, $_where_ids, $auto_sync, $posted_sync, $post_count_sync, $call_delete_topics);
  829. }
  830. return;
  831. }*/
  832. $where_clause = $db->sql_in_set($where_type, $where_ids);
  833. }
  834. $approved_posts = 0;
  835. $post_ids = $topic_ids = $forum_ids = $post_counts = $remove_topics = array();
  836. $sql = 'SELECT post_id, poster_id, post_visibility, post_postcount, topic_id, forum_id
  837. FROM ' . POSTS_TABLE . '
  838. WHERE ' . $where_clause;
  839. $result = $db->sql_query($sql);
  840. while ($row = $db->sql_fetchrow($result))
  841. {
  842. $post_ids[] = (int) $row['post_id'];
  843. $poster_ids[] = (int) $row['poster_id'];
  844. $topic_ids[] = (int) $row['topic_id'];
  845. $forum_ids[] = (int) $row['forum_id'];
  846. if ($row['post_postcount'] && $post_count_sync && $row['post_visibility'] == ITEM_APPROVED)
  847. {
  848. $post_counts[$row['poster_id']] = (!empty($post_counts[$row['poster_id']])) ? $post_counts[$row['poster_id']] + 1 : 1;
  849. }
  850. if ($row['post_visibility'] == ITEM_APPROVED)
  851. {
  852. $approved_posts++;
  853. }
  854. }
  855. $db->sql_freeresult($result);
  856. if (!count($post_ids))
  857. {
  858. return false;
  859. }
  860. $db->sql_transaction('begin');
  861. $table_ary = array(POSTS_TABLE, REPORTS_TABLE);
  862. /**
  863. * Perform additional actions during post(s) deletion before running the queries
  864. *
  865. * @event core.delete_posts_in_transaction_before
  866. * @var array post_ids Array with deleted posts' ids
  867. * @var array poster_ids Array with deleted posts' author ids
  868. * @var array topic_ids Array with deleted posts' topic ids
  869. * @var array forum_ids Array with deleted posts' forum ids
  870. * @var string where_type Variable containing posts deletion mode
  871. * @var mixed where_ids Array or comma separated list of post ids to delete
  872. * @var array delete_notifications_types Array with notifications types to delete
  873. * @var array table_ary Array with table names to delete data from
  874. * @since 3.1.7-RC1
  875. */
  876. $vars = array(
  877. 'post_ids',
  878. 'poster_ids',
  879. 'topic_ids',
  880. 'forum_ids',
  881. 'where_type',
  882. 'where_ids',
  883. 'delete_notifications_types',
  884. 'table_ary',
  885. );
  886. extract($phpbb_dispatcher->trigger_event('core.delete_posts_in_transaction_before', compact($vars)));
  887. foreach ($table_ary as $table)
  888. {
  889. $sql = "DELETE FROM $table
  890. WHERE " . $db->sql_in_set('post_id', $post_ids);
  891. $db->sql_query($sql);
  892. }
  893. unset($table_ary);
  894. // Adjust users post counts
  895. if (count($post_counts) && $post_count_sync)
  896. {
  897. foreach ($post_counts as $poster_id => $substract)
  898. {
  899. $sql = 'UPDATE ' . USERS_TABLE . '
  900. SET user_posts = 0
  901. WHERE user_id = ' . $poster_id . '
  902. AND user_posts < ' . $substract;
  903. $db->sql_query($sql);
  904. $sql = 'UPDATE ' . USERS_TABLE . '
  905. SET user_posts = user_posts - ' . $substract . '
  906. WHERE user_id = ' . $poster_id . '
  907. AND user_posts >= ' . $substract;
  908. $db->sql_query($sql);
  909. }
  910. }
  911. // Remove topics now having no posts?
  912. if (count($topic_ids))
  913. {
  914. $sql = 'SELECT topic_id
  915. FROM ' . POSTS_TABLE . '
  916. WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
  917. GROUP BY topic_id';
  918. $result = $db->sql_query($sql);
  919. while ($row = $db->sql_fetchrow($result))
  920. {
  921. $remove_topics[] = $row['topic_id'];
  922. }
  923. $db->sql_freeresult($result);
  924. // Actually, those not within remove_topics should be removed. ;)
  925. $remove_topics = array_diff($topic_ids, $remove_topics);
  926. }
  927. // Remove the message from the search index
  928. $search_type = $config['search_type'];
  929. if (!class_exists($search_type))
  930. {
  931. trigger_error('NO_SUCH_SEARCH_MODULE');
  932. }
  933. $error = false;
  934. $search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
  935. if ($error)
  936. {
  937. trigger_error($error);
  938. }
  939. $search->index_remove($post_ids, $poster_ids, $forum_ids);
  940. /** @var \phpbb\attachment\manager $attachment_manager */
  941. $attachment_manager = $phpbb_container->get('attachment.manager');
  942. $attachment_manager->delete('post', $post_ids, false);
  943. unset($attachment_manager);
  944. /**
  945. * Perform additional actions during post(s) deletion
  946. *
  947. * @event core.delete_posts_in_transaction
  948. * @var array post_ids Array with deleted posts' ids
  949. * @var array poster_ids Array with deleted posts' author ids
  950. * @var array topic_ids Array with deleted posts' topic ids
  951. * @var array forum_ids Array with deleted posts' forum ids
  952. * @var string where_type Variable containing posts deletion mode
  953. * @var mixed where_ids Array or comma separated list of posts ids to delete
  954. * @var array delete_notifications_types Array with notifications types to delete
  955. * @since 3.1.0-a4
  956. */
  957. $vars = array(
  958. 'post_ids',
  959. 'poster_ids',
  960. 'topic_ids',
  961. 'forum_ids',
  962. 'where_type',
  963. 'where_ids',
  964. 'delete_notifications_types',
  965. );
  966. extract($phpbb_dispatcher->trigger_event('core.delete_posts_in_transaction', compact($vars)));
  967. $db->sql_transaction('commit');
  968. /**
  969. * Perform additional actions after post(s) deletion
  970. *
  971. * @event core.delete_posts_after
  972. * @var array post_ids Array with deleted posts' ids
  973. * @var array poster_ids Array with deleted posts' author ids
  974. * @var array topic_ids Array with deleted posts' topic ids
  975. * @var array forum_ids Array with deleted posts' forum ids
  976. * @var string where_type Variable containing posts deletion mode
  977. * @var mixed where_ids Array or comma separated list of posts ids to delete
  978. * @var array delete_notifications_types Array with notifications types to delete
  979. * @since 3.1.0-a4
  980. */
  981. $vars = array(
  982. 'post_ids',
  983. 'poster_ids',
  984. 'topic_ids',
  985. 'forum_ids',
  986. 'where_type',
  987. 'where_ids',
  988. 'delete_notifications_types',
  989. );
  990. extract($phpbb_dispatcher->trigger_event('core.delete_posts_after', compact($vars)));
  991. // Resync topics_posted table
  992. if ($posted_sync)
  993. {
  994. update_posted_info($topic_ids);
  995. }
  996. if ($auto_sync)
  997. {
  998. sync('topic_reported', 'topic_id', $topic_ids);
  999. sync('topic', 'topic_id', $topic_ids, true);
  1000. sync('forum', 'forum_id', $forum_ids, true, true);
  1001. }
  1002. if ($approved_posts && $post_count_sync)
  1003. {
  1004. $config->increment('num_posts', $approved_posts * (-1), false);
  1005. }
  1006. // We actually remove topics now to not be inconsistent (the delete_topics function calls this function too)
  1007. if (count($remove_topics) && $call_delete_topics)
  1008. {
  1009. delete_topics('topic_id', $remove_topics, $auto_sync, $post_count_sync, false);
  1010. }
  1011. /* @var $phpbb_notifications \phpbb\notification\manager */
  1012. $phpbb_notifications = $phpbb_container->get('notification_manager');
  1013. $phpbb_notifications->delete_notifications($delete_notifications_types, $post_ids);
  1014. return count($post_ids);
  1015. }
  1016. /**
  1017. * Deletes shadow topics pointing to a specified forum.
  1018. *
  1019. * @param int $forum_id The forum id
  1020. * @param string $sql_more Additional WHERE statement, e.g. t.topic_time < (time() - 1234)
  1021. * @param bool $auto_sync Will call sync() if this is true
  1022. *
  1023. * @return array Array with affected forums
  1024. */
  1025. function delete_topic_shadows($forum_id, $sql_more = '', $auto_sync = true)
  1026. {
  1027. global $db;
  1028. if (!$forum_id)
  1029. {
  1030. // Nothing to do.
  1031. return;
  1032. }
  1033. // Set of affected forums we have to resync
  1034. $sync_forum_ids = array();
  1035. // Amount of topics we select and delete at once.
  1036. $batch_size = 500;
  1037. do
  1038. {
  1039. $sql = 'SELECT t2.forum_id, t2.topic_id
  1040. FROM ' . TOPICS_TABLE . ' t2, ' . TOPICS_TABLE . ' t
  1041. WHERE t2.topic_moved_id = t.topic_id
  1042. AND t.forum_id = ' . (int) $forum_id . '
  1043. ' . (($sql_more) ? 'AND ' . $sql_more : '');
  1044. $result = $db->sql_query_limit($sql, $batch_size);
  1045. $topic_ids = array();
  1046. while ($row = $db->sql_fetchrow($result))
  1047. {
  1048. $topic_ids[] = (int) $row['topic_id'];
  1049. $sync_forum_ids[(int) $row['forum_id']] = (int) $row['forum_id'];
  1050. }
  1051. $db->sql_freeresult($result);
  1052. if (!empty($topic_ids))
  1053. {
  1054. $sql = 'DELETE FROM ' . TOPICS_TABLE . '
  1055. WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
  1056. $db->sql_query($sql);
  1057. }
  1058. }
  1059. while (count($topic_ids) == $batch_size);
  1060. if ($auto_sync)
  1061. {
  1062. sync('forum', 'forum_id', $sync_forum_ids, true, true);
  1063. }
  1064. return $sync_forum_ids;
  1065. }
  1066. /**
  1067. * Update/Sync posted information for topics
  1068. */
  1069. function update_posted_info(&$topic_ids)
  1070. {
  1071. global $db, $config;
  1072. if (empty($topic_ids) || !$config['load_db_track'])
  1073. {
  1074. return;
  1075. }
  1076. // First of all, let us remove any posted information for these topics
  1077. $sql = 'DELETE FROM ' . TOPICS_POSTED_TABLE . '
  1078. WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
  1079. $db->sql_query($sql);
  1080. // Now, let us collect the user/topic combos for rebuilding the information
  1081. $sql = 'SELECT poster_id, topic_id
  1082. FROM ' . POSTS_TABLE . '
  1083. WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
  1084. AND poster_id <> ' . ANONYMOUS . '
  1085. GROUP BY poster_id, topic_id';
  1086. $result = $db->sql_query($sql);
  1087. $posted = array();
  1088. while ($row = $db->sql_fetchrow($result))
  1089. {
  1090. // Add as key to make them unique (grouping by) and circumvent empty keys on array_unique
  1091. $posted[$row['poster_id']][] = $row['topic_id'];
  1092. }
  1093. $db->sql_freeresult($result);
  1094. // Now add the information...
  1095. $sql_ary = array();
  1096. foreach ($posted as $user_id => $topic_row)
  1097. {
  1098. foreach ($topic_row as $topic_id)
  1099. {
  1100. $sql_ary[] = array(
  1101. 'user_id' => (int) $user_id,
  1102. 'topic_id' => (int) $topic_id,
  1103. 'topic_posted' => 1,
  1104. );
  1105. }
  1106. }
  1107. unset($posted);
  1108. $db->sql_multi_insert(TOPICS_POSTED_TABLE, $sql_ary);
  1109. }
  1110. /**
  1111. * All-encompasing sync function
  1112. *
  1113. * Exaples:
  1114. * <code>
  1115. * sync('topic', 'topic_id', 123); // resync topic #123
  1116. * sync('topic', 'forum_id', array(2, 3)); // resync topics from forum #2 and #3
  1117. * sync('topic'); // resync all topics
  1118. * sync('topic', 'range', 'topic_id BETWEEN 1 AND 60'); // resync a range of topics/forums (only available for 'topic' and 'forum' modes)
  1119. * </code>
  1120. *
  1121. * Modes:
  1122. * - forum Resync complete forum
  1123. * - topic Resync topics
  1124. * - topic_moved Removes topic shadows that would be in the same forum as the topic they link to
  1125. * - topic_visibility Resyncs the topic_visibility flag according to the status of the first post
  1126. * - post_reported Resyncs the post_reported flag, relying on actual reports
  1127. * - topic_reported Resyncs the topic_reported flag, relying on post_reported flags
  1128. * - post_attachement Same as post_reported, but with attachment flags
  1129. * - topic_attachement Same as topic_reported, but with attachment flags
  1130. */
  1131. function sync($mode, $where_type = '', $where_ids = '', $resync_parents = false, $sync_extra = false)
  1132. {
  1133. global $db;
  1134. if (is_array($where_ids))
  1135. {
  1136. $where_ids = array_unique($where_ids);
  1137. $where_ids = array_map('intval', $where_ids);
  1138. }
  1139. else if ($where_type != 'range')
  1140. {
  1141. $where_ids = ($where_ids) ? array((int) $where_ids) : array();
  1142. }
  1143. if ($mode == 'forum' || $mode == 'topic' || $mode == 'topic_visibility' || $mode == 'topic_reported' || $mode == 'post_reported')
  1144. {
  1145. if (!$where_type)
  1146. {
  1147. $where_sql = '';
  1148. $where_sql_and = 'WHERE';
  1149. }
  1150. else if ($where_type == 'range')
  1151. {
  1152. // Only check a range of topics/forums. For instance: 'topic_id BETWEEN 1 AND 60'
  1153. $where_sql = 'WHERE (' . $mode[0] . ".$where_ids)";
  1154. $where_sql_and = $where_sql . "\n\tAND";
  1155. }
  1156. else
  1157. {
  1158. // Do not sync the "global forum"
  1159. $where_ids = array_diff($where_ids, array(0));
  1160. if (!count($where_ids))
  1161. {
  1162. // Empty array with IDs. This means that we don't have any work to do. Just return.
  1163. return;
  1164. }
  1165. // Limit the topics/forums we are syncing, use specific topic/forum IDs.
  1166. // $where_type contains the field for the where clause (forum_id, topic_id)
  1167. $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids);
  1168. $where_sql_and = $where_sql . "\n\tAND";
  1169. }
  1170. }
  1171. else
  1172. {
  1173. if (!count($where_ids))
  1174. {
  1175. return;
  1176. }
  1177. // $where_type contains the field for the where clause (forum_id, topic_id)
  1178. $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids);
  1179. $where_sql_and = $where_sql . "\n\tAND";
  1180. }
  1181. switch ($mode)
  1182. {
  1183. case 'topic_moved':
  1184. $db->sql_transaction('begin');
  1185. switch ($db->get_sql_layer())
  1186. {
  1187. case 'mysqli':
  1188. $sql = 'DELETE FROM ' . TOPICS_TABLE . '
  1189. USING ' . TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2
  1190. WHERE t1.topic_moved_id = t2.topic_id
  1191. AND t1.forum_id = t2.forum_id";
  1192. $db->sql_query($sql);
  1193. break;
  1194. default:
  1195. $sql = 'SELECT t1.topic_id
  1196. FROM ' .TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2
  1197. WHERE t1.topic_moved_id = t2.topic_id
  1198. AND t1.forum_id = t2.forum_id";
  1199. $result = $db->sql_query($sql);
  1200. $topic_id_ary = array();
  1201. while ($row = $db->sql_fetchrow($result))
  1202. {
  1203. $topic_id_ary[] = $row['topic_id'];
  1204. }
  1205. $db->sql_freeresult($result);
  1206. if (!count($topic_id_ary))
  1207. {
  1208. return;
  1209. }
  1210. $sql = 'DELETE FROM ' . TOPICS_TABLE . '
  1211. WHERE ' . $db->sql_in_set('topic_id', $topic_id_ary);
  1212. $db->sql_query($sql);
  1213. break;
  1214. }
  1215. $db->sql_transaction('commit');
  1216. break;
  1217. case 'topic_visibility':
  1218. $db->sql_transaction('begin');
  1219. $sql = 'SELECT t.topic_id, p.post_visibility
  1220. FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
  1221. $where_sql_and p.topic_id = t.topic_id
  1222. AND p.post_visibility = " . ITEM_APPROVED;
  1223. $result = $db->sql_query($sql);
  1224. $topics_approved = array();
  1225. while ($row = $db->sql_fetchrow($result))
  1226. {
  1227. $topics_approved[] = (int) $row['topic_id'];
  1228. }
  1229. $db->sql_freeresult($result);
  1230. $sql = 'SELECT t.topic_id, p.post_visibility
  1231. FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
  1232. $where_sql_and " . $db->sql_in_set('t.topic_id', $topics_approved, true, true) . '
  1233. AND p.topic_id = t.topic_id
  1234. AND p.post_visibility = ' . ITEM_DELETED;
  1235. $result = $db->sql_query($sql);
  1236. $topics_softdeleted = array();
  1237. while ($row = $db->sql_fetchrow($result))
  1238. {
  1239. $topics_softdeleted[] = (int) $row['topic_id'];
  1240. }
  1241. $db->sql_freeresult($result);
  1242. $topics_softdeleted = array_diff($topics_softdeleted, $topics_approved);
  1243. $topics_not_unapproved = array_merge($topics_softdeleted, $topics_approved);
  1244. $update_ary = array(
  1245. ITEM_UNAPPROVED => (!empty($topics_not_unapproved)) ? $where_sql_and . ' ' . $db->sql_in_set('topic_id', $topics_not_unapproved, true) : '',
  1246. ITEM_APPROVED => (!empty($topics_approved)) ? ' WHERE ' . $db->sql_in_set('topic_id', $topics_approved) : '',
  1247. ITEM_DELETED => (!empty($topics_softdeleted)) ? ' WHERE ' . $db->sql_in_set('topic_id', $topics_softdeleted) : '',
  1248. );
  1249. foreach ($update_ary as $visibility => $sql_where)
  1250. {
  1251. if ($sql_where)
  1252. {
  1253. $sql = 'UPDATE ' . TOPICS_TABLE . '
  1254. SET topic_visibility = ' . $visibility . '
  1255. ' . $sql_where;
  1256. $db->sql_query($sql);
  1257. }
  1258. }
  1259. $db->sql_transaction('commit');
  1260. break;
  1261. case 'post_reported':
  1262. $post_ids = $post_reported = array();
  1263. $db->sql_transaction('begin');
  1264. $sql = 'SELECT p.post_id, p.post_reported
  1265. FROM ' . POSTS_TABLE . " p
  1266. $where_sql
  1267. GROUP BY p.post_id, p.post_reported";
  1268. $result = $db->sql_query($sql);
  1269. while ($row = $db->sql_fetchrow($result))
  1270. {
  1271. $post_ids[$row['post_id']] = $row['post_id'];
  1272. if ($row['post_reported'])
  1273. {
  1274. $post_reported[$row['post_id']] = 1;
  1275. }
  1276. }
  1277. $db->sql_freeresult($result);
  1278. $sql = 'SELECT DISTINCT(post_id)
  1279. FROM ' . REPORTS_TABLE . '
  1280. WHERE ' . $db->sql_in_set('post_id', $post_ids) . '
  1281. AND report_closed = 0';
  1282. $result = $db->sql_query($sql);
  1283. $post_ids = array();
  1284. while ($row = $db->sql_fetchrow($result))
  1285. {
  1286. if (!isset($post_reported[$row['post_id']]))
  1287. {
  1288. $post_ids[] = $row['post_id'];
  1289. }
  1290. else
  1291. {
  1292. unset($post_reported[$row['post_id']]);
  1293. }
  1294. }
  1295. $db->sql_freeresult($result);
  1296. // $post_reported should be empty by now, if it's not it contains
  1297. // posts that are falsely flagged as reported
  1298. foreach ($post_reported as $post_id => $void)
  1299. {
  1300. $post_ids[] = $post_id;
  1301. }
  1302. if (count($post_ids))
  1303. {
  1304. $sql = 'UPDATE ' . POSTS_TABLE . '
  1305. SET post_reported = 1 - post_reported
  1306. WHERE ' . $db->sql_in_set('post_id', $post_ids);
  1307. $db->sql_query($sql);
  1308. }
  1309. $db->sql_transaction('commit');
  1310. break;
  1311. case 'topic_reported':
  1312. if ($sync_extra)
  1313. {
  1314. sync('post_reported', $where_type, $where_ids);
  1315. }
  1316. $topic_ids = $topic_reported = array();
  1317. $db->sql_transaction('begin');
  1318. $sql = 'SELECT DISTINCT(t.topic_id)
  1319. FROM ' . POSTS_TABLE . " t
  1320. $where_sql_and t.post_reported = 1";
  1321. $result = $db->sql_query($sql);
  1322. while ($row = $db->sql_fetchrow($result))
  1323. {
  1324. $topic_reported[$row['topic_id']] = 1;
  1325. }
  1326. $db->sql_freeresult($result);
  1327. $sql = 'SELECT t.topic_id, t.topic_reported
  1328. FROM ' . TOPICS_TABLE . " t
  1329. $where_sql";
  1330. $result = $db->sql_query($sql);
  1331. while ($row = $db->sql_fetchrow($result))
  1332. {
  1333. if ($row['topic_reported'] ^ isset($topic_reported[$row['topic_id']]))
  1334. {
  1335. $topic_ids[] = $row['topic_id'];
  1336. }
  1337. }
  1338. $db->sql_freeresult($result);
  1339. if (count($topic_ids))
  1340. {
  1341. $sql = 'UPDATE ' . TOPICS_TABLE . '
  1342. SET topic_reported = 1 - topic_reported
  1343. WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
  1344. $db->sql_query($sql);
  1345. }
  1346. $db->sql_transaction('commit');
  1347. break;
  1348. case 'post_attachment':
  1349. $post_ids = $post_attachment = array();
  1350. $db->sql_transaction('begin');
  1351. $sql = 'SELECT p.post_id, p.post_attachment
  1352. FROM ' . POSTS_TABLE . " p
  1353. $where_sql
  1354. GROUP BY p.post_id, p.post_attachment";
  1355. $result = $db->sql_query($sql);
  1356. while ($row = $db->sql_fetchrow($result))
  1357. {
  1358. $post_ids[$row['post_id']] = $row['post_id'];
  1359. if ($row['post_attachment'])
  1360. {
  1361. $post_attachment[$row['post_id']] = 1;
  1362. }
  1363. }
  1364. $db->sql_freeresult($result);
  1365. $sql = 'SELECT DISTINCT(post_msg_id)
  1366. FROM ' . ATTACHMENTS_TABLE . '
  1367. WHERE ' . $db->sql_in_set('post_msg_id', $post_ids) . '
  1368. AND in_message = 0';
  1369. $result = $db->sql_query($sql);
  1370. $post_ids = array();
  1371. while ($row = $db->sql_fetchrow($result))
  1372. {
  1373. if (!isset($post_attachment[$row['post_msg_id']]))
  1374. {
  1375. $post_ids[] = $row['post_msg_id'];
  1376. }
  1377. else
  1378. {
  1379. unset($post_attachment[$row['post_msg_id']]);
  1380. }
  1381. }
  1382. $db->sql_freeresult($result);
  1383. // $post_attachment should be empty by now, if it's not it contains
  1384. // posts that are falsely flagged as having attachments
  1385. foreach ($post_attachment as $post_id => $void)
  1386. {
  1387. $post_ids[] = $post_id;
  1388. }
  1389. if (count($post_ids))
  1390. {
  1391. $sql = 'UPDATE ' . POSTS_TABLE . '
  1392. SET post_attachment = 1 - post_attachment
  1393. WHERE ' . $db->sql_in_set('post_id', $post_ids);
  1394. $db->sql_query($sql);
  1395. }
  1396. $db->sql_transaction('commit');
  1397. break;
  1398. case 'topic_attachment':
  1399. if ($sync_extra)
  1400. {
  1401. sync('post_attachment', $where_type, $where_ids);
  1402. }
  1403. $topic_ids = $topic_attachment = array();
  1404. $db->sql_transaction('begin');
  1405. $sql = 'SELECT DISTINCT(t.topic_id)
  1406. FROM ' . POSTS_TABLE . " t
  1407. $where_sql_and t.post_attachment = 1";
  1408. $result = $db->sql_query($sql);
  1409. while ($row = $db->sql_fetchrow($result))
  1410. {
  1411. $topic_attachment[$row['topic_id']] = 1;
  1412. }
  1413. $db->sql_freeresult($result);
  1414. $sql = 'SELECT t.topic_id, t.topic_attachment
  1415. FROM ' . TOPICS_TABLE . " t
  1416. $where_sql";
  1417. $result = $db->sql_query($sql);
  1418. while ($row = $db->sql_fetchrow($result))
  1419. {
  1420. if ($row['topic_attachment'] ^ isset($topic_attachment[$row['topic_id']]))
  1421. {
  1422. $topic_ids[] = $row['topic_id'];
  1423. }
  1424. }
  1425. $db->sql_freeresult($result);
  1426. if (count($topic_ids))
  1427. {
  1428. $sql = 'UPDATE ' . TOPICS_TABLE . '
  1429. SET topic_attachment = 1 - topic_attachment
  1430. WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
  1431. $db->sql_query($sql);
  1432. }
  1433. $db->sql_transaction('commit');
  1434. break;
  1435. case 'forum':
  1436. $db->sql_transaction('begin');
  1437. // 1: Get the list of all forums
  1438. $sql = 'SELECT f.*
  1439. FROM ' . FORUMS_TABLE . " f
  1440. $where_sql";
  1441. $result = $db->sql_query($sql);
  1442. $forum_data = $forum_ids = $post_ids = $last_post_id = $post_info = array();
  1443. while ($row = $db->sql_fetchrow($result))
  1444. {
  1445. if ($row['forum_type'] == FORUM_LINK)
  1446. {
  1447. continue;
  1448. }
  1449. $forum_id = (int) $row['forum_id'];
  1450. $forum_ids[$forum_id] = $forum_id;
  1451. $forum_data[$forum_id] = $row;
  1452. if ($sync_extra)
  1453. {
  1454. $forum_data[$forum_id]['posts_approved'] = 0;
  1455. $forum_data[$forum_id]['posts_unapproved'] = 0;
  1456. $forum_data[$forum_id]['posts_softdeleted'] = 0;
  1457. $forum_data[$forum_id]['topics_approved'] = 0;
  1458. $forum_data[$forum_id]['topics_unapproved'] = 0;
  1459. $forum_data[$forum_id]['topics_softdeleted'] = 0;
  1460. }
  1461. $forum_data[$forum_id]['last_post_id'] = 0;
  1462. $forum_data[$forum_id]['last_post_subject'] = '';
  1463. $forum_data[$forum_id]['last_post_time'] = 0;
  1464. $forum_data[$forum_id]['last_poster_id'] = 0;
  1465. $forum_data[$forum_id]['last_poster_name'] = '';
  1466. $forum_data[$forum_id]['last_poster_colour'] = '';
  1467. }
  1468. $db->sql_freeresult($result);
  1469. if (!count($forum_ids))
  1470. {
  1471. break;
  1472. }
  1473. $forum_ids = array_values($forum_ids);
  1474. // 2: Get topic counts for each forum (optional)
  1475. if ($sync_extra)
  1476. {
  1477. $sql = 'SELECT forum_id, topic_visibility, COUNT(topic_id) AS total_topics
  1478. FROM ' . TOPICS_TABLE . '
  1479. WHERE ' . $db->sql_in_set('forum_id', $forum_ids) . '
  1480. GROUP BY forum_id, topic_visibility';
  1481. $result = $db->sql_query($sql);
  1482. while ($row = $db->sql_fetchrow($result))
  1483. {
  1484. $forum_id = (int) $row['forum_id'];
  1485. if ($row['topic_visibility'] == ITEM_APPROVED)
  1486. {
  1487. $forum_data[$forum_id]['topics_approved'] = $row['total_topics'];
  1488. }
  1489. else if ($row['topic_visibility'] == ITEM_UNAPPROVED || $row['topic_visibility'] == ITEM_REAPPROVE)
  1490. {
  1491. $forum_data[$forum_id]['topics_unapproved'] = $row['total_topics'];
  1492. }
  1493. else if ($row['topic_visibility'] == ITEM_DELETED)
  1494. {
  1495. $forum_data[$forum_id]['topics_softdeleted'] = $row['total_topics'];
  1496. }
  1497. }
  1498. $db->sql_freeresult($result);
  1499. }
  1500. // 3: Get post count for each forum (optional)
  1501. if ($sync_extra)
  1502. {
  1503. if (count($forum_ids) == 1)
  1504. {
  1505. $sql = 'SELECT SUM(t.topic_posts_approved) AS forum_posts_approved, SUM(t.topic_posts_unapproved) AS forum_posts_unapproved, SUM(t.topic_posts_softdeleted) AS forum_posts_softdeleted
  1506. FROM ' . TOPICS_TABLE . ' t
  1507. WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
  1508. AND t.topic_status <> ' . ITEM_MOVED;
  1509. }
  1510. else
  1511. {
  1512. $sql = 'SELECT t.forum_id, SUM(t.topic_posts_approved) AS forum_posts_approved, SUM(t.topic_posts_unapproved) AS forum_posts_unapproved, SUM(t.topic_posts_softdeleted) AS forum_posts_softdeleted
  1513. FROM ' . TOPICS_TABLE . ' t
  1514. WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
  1515. AND t.topic_status <> ' . ITEM_MOVED . '
  1516. GROUP BY t.forum_id';
  1517. }
  1518. $result = $db->sql_query($sql);
  1519. while ($row = $db->sql_fetchrow($result))
  1520. {
  1521. $forum_id = (count($fo

Large files files are truncated, but you can click here to view the full file