Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
functions_admin.php
0001 <?php
0002 /**
0003 *
0004 * This file is part of the phpBB Forum Software package.
0005 *
0006 * @copyright (c) phpBB Limited <https://www.phpbb.com>
0007 * @license GNU General Public License, version 2 (GPL-2.0)
0008 *
0009 * For full copyright and license information, please see
0010 * the docs/CREDITS.txt file.
0011 *
0012 */
0013
0014 /**
0015 * @ignore
0016 */
0017 if (!defined('IN_PHPBB'))
0018 {
0019 exit;
0020 }
0021
0022 /**
0023 * Recalculate Nested Sets
0024 *
0025 * @param int $new_id first left_id (should start with 1)
0026 * @param string $pkey primary key-column (containing the id for the parent_id of the children)
0027 * @param string $table constant or fullname of the table
0028 * @param int $parent_id parent_id of the current set (default = 0)
0029 * @param array $where contains strings to compare closer on the where statement (additional)
0030 */
0031 function recalc_nested_sets(&$new_id, $pkey, $table, $parent_id = 0, $where = array())
0032 {
0033 global $db;
0034
0035 $sql = 'SELECT *
0036 FROM ' . $table . '
0037 WHERE parent_id = ' . (int) $parent_id .
0038 ((!empty($where)) ? ' AND ' . implode(' AND ', $where) : '') . '
0039 ORDER BY left_id ASC';
0040 $result = $db->sql_query($sql);
0041 while ($row = $db->sql_fetchrow($result))
0042 {
0043 // First we update the left_id for this module
0044 if ($row['left_id'] != $new_id)
0045 {
0046 $db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('left_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}");
0047 }
0048 $new_id++;
0049
0050 // Then we go through any children and update their left/right id's
0051 recalc_nested_sets($new_id, $pkey, $table, $row[$pkey], $where);
0052
0053 // Then we come back and update the right_id for this module
0054 if ($row['right_id'] != $new_id)
0055 {
0056 $db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('right_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}");
0057 }
0058 $new_id++;
0059 }
0060 $db->sql_freeresult($result);
0061 }
0062
0063 /**
0064 * Simple version of jumpbox, just lists authed forums
0065 */
0066 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)
0067 {
0068 global $db, $auth, $phpbb_dispatcher;
0069
0070 // This query is identical to the jumpbox one
0071 $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, forum_flags, forum_options, left_id, right_id
0072 FROM ' . FORUMS_TABLE . '
0073 ORDER BY left_id ASC';
0074 $result = $db->sql_query($sql, 600);
0075
0076 $rowset = array();
0077 while ($row = $db->sql_fetchrow($result))
0078 {
0079 $rowset[(int) $row['forum_id']] = $row;
0080 }
0081 $db->sql_freeresult($result);
0082
0083 $right = 0;
0084 $padding_store = array('0' => '');
0085 $padding = '';
0086 $forum_list = ($return_array) ? array() : '';
0087
0088 /**
0089 * Modify the forum list data
0090 *
0091 * @event core.make_forum_select_modify_forum_list
0092 * @var array rowset Array with the forums list data
0093 * @since 3.1.10-RC1
0094 */
0095 $vars = array('rowset');
0096 extract($phpbb_dispatcher->trigger_event('core.make_forum_select_modify_forum_list', compact($vars)));
0097
0098 // Sometimes it could happen that forums will be displayed here not be displayed within the index page
0099 // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
0100 // If this happens, the padding could be "broken"
0101
0102 foreach ($rowset as $row)
0103 {
0104 if ($row['left_id'] < $right)
0105 {
0106 $padding .= ' ';
0107 $padding_store[$row['parent_id']] = $padding;
0108 }
0109 else if ($row['left_id'] > $right + 1)
0110 {
0111 $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : '';
0112 }
0113
0114 $right = $row['right_id'];
0115 $disabled = false;
0116
0117 if (!$ignore_acl && $auth->acl_gets(array('f_list', 'a_forum', 'a_forumadd', 'a_forumdel'), $row['forum_id']))
0118 {
0119 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'])))
0120 {
0121 $disabled = true;
0122 }
0123 }
0124 else if (!$ignore_acl)
0125 {
0126 continue;
0127 }
0128
0129 if (
0130 ((is_array($ignore_id) && in_array($row['forum_id'], $ignore_id)) || $row['forum_id'] == $ignore_id)
0131 ||
0132 // Non-postable forum with no subforums, don't display
0133 ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']) && $ignore_emptycat)
0134 ||
0135 ($row['forum_type'] != FORUM_POST && $ignore_nonpost)
0136 )
0137 {
0138 $disabled = true;
0139 }
0140
0141 if ($return_array)
0142 {
0143 // Include some more information...
0144 $selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? true : false) : (($row['forum_id'] == $select_id) ? true : false);
0145 $forum_list[$row['forum_id']] = array_merge(array('padding' => $padding, 'selected' => ($selected && !$disabled), 'disabled' => $disabled), $row);
0146 }
0147 else
0148 {
0149 $selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? ' selected="selected"' : '') : (($row['forum_id'] == $select_id) ? ' selected="selected"' : '');
0150 $forum_list .= '<option value="' . $row['forum_id'] . '"' . (($disabled) ? ' disabled="disabled" class="disabled-option"' : $selected) . '>' . $padding . $row['forum_name'] . '</option>';
0151 }
0152 }
0153 unset($padding_store, $rowset);
0154
0155 return $forum_list;
0156 }
0157
0158 /**
0159 * Generate size select options
0160 */
0161 function size_select_options($size_compare)
0162 {
0163 global $user;
0164
0165 $size_types_text = array($user->lang['BYTES'], $user->lang['KIB'], $user->lang['MIB']);
0166 $size_types = array('b', 'kb', 'mb');
0167
0168 $s_size_options = '';
0169
0170 for ($i = 0, $size = count($size_types_text); $i < $size; $i++)
0171 {
0172 $selected = ($size_compare == $size_types[$i]) ? ' selected="selected"' : '';
0173 $s_size_options .= '<option value="' . $size_types[$i] . '"' . $selected . '>' . $size_types_text[$i] . '</option>';
0174 }
0175
0176 return $s_size_options;
0177 }
0178
0179 /**
0180 * Generate list of groups (option fields without select)
0181 *
0182 * @param int $group_id The default group id to mark as selected
0183 * @param array $exclude_ids The group ids to exclude from the list, false (default) if you whish to exclude no id
0184 * @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.
0185 *
0186 * @return string The list of options.
0187 */
0188 function group_select_options($group_id, $exclude_ids = false, $manage_founder = false)
0189 {
0190 global $db, $config, $phpbb_container;
0191
0192 /** @var \phpbb\group\helper $group_helper */
0193 $group_helper = $phpbb_container->get('group_helper');
0194
0195 $exclude_sql = ($exclude_ids !== false && count($exclude_ids)) ? 'WHERE ' . $db->sql_in_set('group_id', array_map('intval', $exclude_ids), true) : '';
0196 $sql_and = (!$config['coppa_enable']) ? (($exclude_sql) ? ' AND ' : ' WHERE ') . "group_name <> 'REGISTERED_COPPA'" : '';
0197 $sql_founder = ($manage_founder !== false) ? (($exclude_sql || $sql_and) ? ' AND ' : ' WHERE ') . 'group_founder_manage = ' . (int) $manage_founder : '';
0198
0199 $sql = 'SELECT group_id, group_name, group_type
0200 FROM ' . GROUPS_TABLE . "
0201 $exclude_sql
0202 $sql_and
0203 $sql_founder
0204 ORDER BY group_type DESC, group_name ASC";
0205 $result = $db->sql_query($sql);
0206
0207 $s_group_options = '';
0208 while ($row = $db->sql_fetchrow($result))
0209 {
0210 $selected = ($row['group_id'] == $group_id) ? ' selected="selected"' : '';
0211 $s_group_options .= '<option' . (($row['group_type'] == GROUP_SPECIAL) ? ' class="sep"' : '') . ' value="' . $row['group_id'] . '"' . $selected . '>' . $group_helper->get_name($row['group_name']) . '</option>';
0212 }
0213 $db->sql_freeresult($result);
0214
0215 return $s_group_options;
0216 }
0217
0218 /**
0219 * Obtain authed forums list
0220 */
0221 function get_forum_list($acl_list = 'f_list', $id_only = true, $postable_only = false, $no_cache = false)
0222 {
0223 global $db, $auth, $phpbb_dispatcher;
0224 static $forum_rows;
0225
0226 if (!isset($forum_rows))
0227 {
0228 // This query is identical to the jumpbox one
0229 $expire_time = ($no_cache) ? 0 : 600;
0230
0231 $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id
0232 FROM ' . FORUMS_TABLE . '
0233 ORDER BY left_id ASC';
0234 $result = $db->sql_query($sql, $expire_time);
0235
0236 $forum_rows = array();
0237
0238 $right = $padding = 0;
0239 $padding_store = array('0' => 0);
0240
0241 while ($row = $db->sql_fetchrow($result))
0242 {
0243 if ($row['left_id'] < $right)
0244 {
0245 $padding++;
0246 $padding_store[$row['parent_id']] = $padding;
0247 }
0248 else if ($row['left_id'] > $right + 1)
0249 {
0250 // Ok, if the $padding_store for this parent is empty there is something wrong. For now we will skip over it.
0251 // @todo digging deep to find out "how" this can happen.
0252 $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : $padding;
0253 }
0254
0255 $right = $row['right_id'];
0256 $row['padding'] = $padding;
0257
0258 $forum_rows[] = $row;
0259 }
0260 $db->sql_freeresult($result);
0261 unset($padding_store);
0262 }
0263
0264 $rowset = array();
0265 foreach ($forum_rows as $row)
0266 {
0267 if ($postable_only && $row['forum_type'] != FORUM_POST)
0268 {
0269 continue;
0270 }
0271
0272 if ($acl_list == '' || ($acl_list != '' && $auth->acl_gets($acl_list, $row['forum_id'])))
0273 {
0274 $rowset[] = ($id_only) ? (int) $row['forum_id'] : $row;
0275 }
0276 }
0277
0278 /**
0279 * Modify the forum list data
0280 *
0281 * @event core.get_forum_list_modify_data
0282 * @var array rowset Array with the forum list data
0283 * @since 3.1.10-RC1
0284 */
0285 $vars = array('rowset');
0286 extract($phpbb_dispatcher->trigger_event('core.get_forum_list_modify_data', compact($vars)));
0287
0288 return $rowset;
0289 }
0290
0291 /**
0292 * Get forum branch
0293 */
0294 function get_forum_branch($forum_id, $type = 'all', $order = 'descending', $include_forum = true)
0295 {
0296 global $db;
0297
0298 switch ($type)
0299 {
0300 case 'parents':
0301 $condition = 'f1.left_id BETWEEN f2.left_id AND f2.right_id';
0302 break;
0303
0304 case 'children':
0305 $condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id';
0306 break;
0307
0308 default:
0309 $condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id OR f1.left_id BETWEEN f2.left_id AND f2.right_id';
0310 break;
0311 }
0312
0313 $rows = array();
0314
0315 $sql = 'SELECT f2.*
0316 FROM ' . FORUMS_TABLE . ' f1
0317 LEFT JOIN ' . FORUMS_TABLE . " f2 ON ($condition)
0318 WHERE f1.forum_id = $forum_id
0319 ORDER BY f2.left_id " . (($order == 'descending') ? 'ASC' : 'DESC');
0320 $result = $db->sql_query($sql);
0321
0322 while ($row = $db->sql_fetchrow($result))
0323 {
0324 if (!$include_forum && $row['forum_id'] == $forum_id)
0325 {
0326 continue;
0327 }
0328
0329 $rows[] = $row;
0330 }
0331 $db->sql_freeresult($result);
0332
0333 return $rows;
0334 }
0335
0336 /**
0337 * Copies permissions from one forum to others
0338 *
0339 * @param int $src_forum_id The source forum we want to copy permissions from
0340 * @param array $dest_forum_ids The destination forum(s) we want to copy to
0341 * @param bool $clear_dest_perms True if destination permissions should be deleted
0342 * @param bool $add_log True if log entry should be added
0343 *
0344 * @return bool False on error
0345 */
0346 function copy_forum_permissions($src_forum_id, $dest_forum_ids, $clear_dest_perms = true, $add_log = true)
0347 {
0348 global $db, $user, $phpbb_log;
0349
0350 // Only one forum id specified
0351 if (!is_array($dest_forum_ids))
0352 {
0353 $dest_forum_ids = array($dest_forum_ids);
0354 }
0355
0356 // Make sure forum ids are integers
0357 $src_forum_id = (int) $src_forum_id;
0358 $dest_forum_ids = array_map('intval', $dest_forum_ids);
0359
0360 // No source forum or no destination forums specified
0361 if (empty($src_forum_id) || empty($dest_forum_ids))
0362 {
0363 return false;
0364 }
0365
0366 // Check if source forum exists
0367 $sql = 'SELECT forum_name
0368 FROM ' . FORUMS_TABLE . '
0369 WHERE forum_id = ' . $src_forum_id;
0370 $result = $db->sql_query($sql);
0371 $src_forum_name = $db->sql_fetchfield('forum_name');
0372 $db->sql_freeresult($result);
0373
0374 // Source forum doesn't exist
0375 if (empty($src_forum_name))
0376 {
0377 return false;
0378 }
0379
0380 // Check if destination forums exists
0381 $sql = 'SELECT forum_id, forum_name
0382 FROM ' . FORUMS_TABLE . '
0383 WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
0384 $result = $db->sql_query($sql);
0385
0386 $dest_forum_ids = $dest_forum_names = array();
0387 while ($row = $db->sql_fetchrow($result))
0388 {
0389 $dest_forum_ids[] = (int) $row['forum_id'];
0390 $dest_forum_names[] = $row['forum_name'];
0391 }
0392 $db->sql_freeresult($result);
0393
0394 // No destination forum exists
0395 if (empty($dest_forum_ids))
0396 {
0397 return false;
0398 }
0399
0400 // From the mysql documentation:
0401 // Prior to MySQL 4.0.14, the target table of the INSERT statement cannot appear
0402 // in the FROM clause of the SELECT part of the query. This limitation is lifted in 4.0.14.
0403 // Due to this we stay on the safe side if we do the insertion "the manual way"
0404
0405 // Rowsets we're going to insert
0406 $users_sql_ary = $groups_sql_ary = array();
0407
0408 // Query acl users table for source forum data
0409 $sql = 'SELECT user_id, auth_option_id, auth_role_id, auth_setting
0410 FROM ' . ACL_USERS_TABLE . '
0411 WHERE forum_id = ' . $src_forum_id;
0412 $result = $db->sql_query($sql);
0413
0414 while ($row = $db->sql_fetchrow($result))
0415 {
0416 $row = array(
0417 'user_id' => (int) $row['user_id'],
0418 'auth_option_id' => (int) $row['auth_option_id'],
0419 'auth_role_id' => (int) $row['auth_role_id'],
0420 'auth_setting' => (int) $row['auth_setting'],
0421 );
0422
0423 foreach ($dest_forum_ids as $dest_forum_id)
0424 {
0425 $users_sql_ary[] = $row + array('forum_id' => $dest_forum_id);
0426 }
0427 }
0428 $db->sql_freeresult($result);
0429
0430 // Query acl groups table for source forum data
0431 $sql = 'SELECT group_id, auth_option_id, auth_role_id, auth_setting
0432 FROM ' . ACL_GROUPS_TABLE . '
0433 WHERE forum_id = ' . $src_forum_id;
0434 $result = $db->sql_query($sql);
0435
0436 while ($row = $db->sql_fetchrow($result))
0437 {
0438 $row = array(
0439 'group_id' => (int) $row['group_id'],
0440 'auth_option_id' => (int) $row['auth_option_id'],
0441 'auth_role_id' => (int) $row['auth_role_id'],
0442 'auth_setting' => (int) $row['auth_setting'],
0443 );
0444
0445 foreach ($dest_forum_ids as $dest_forum_id)
0446 {
0447 $groups_sql_ary[] = $row + array('forum_id' => $dest_forum_id);
0448 }
0449 }
0450 $db->sql_freeresult($result);
0451
0452 $db->sql_transaction('begin');
0453
0454 // Clear current permissions of destination forums
0455 if ($clear_dest_perms)
0456 {
0457 $sql = 'DELETE FROM ' . ACL_USERS_TABLE . '
0458 WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
0459 $db->sql_query($sql);
0460
0461 $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
0462 WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
0463 $db->sql_query($sql);
0464 }
0465
0466 $db->sql_multi_insert(ACL_USERS_TABLE, $users_sql_ary);
0467 $db->sql_multi_insert(ACL_GROUPS_TABLE, $groups_sql_ary);
0468
0469 if ($add_log)
0470 {
0471 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_FORUM_COPIED_PERMISSIONS', false, array($src_forum_name, implode(', ', $dest_forum_names)));
0472 }
0473
0474 $db->sql_transaction('commit');
0475
0476 return true;
0477 }
0478
0479 /**
0480 * Get physical file listing
0481 */
0482 function filelist($rootdir, $dir = '', $type = 'gif|jpg|jpeg|png|svg|webp')
0483 {
0484 $matches = array($dir => array());
0485
0486 // Remove initial / if present
0487 $rootdir = (substr($rootdir, 0, 1) == '/') ? substr($rootdir, 1) : $rootdir;
0488 // Add closing / if not present
0489 $rootdir = ($rootdir && substr($rootdir, -1) != '/') ? $rootdir . '/' : $rootdir;
0490
0491 // Remove initial / if present
0492 $dir = (substr($dir, 0, 1) == '/') ? substr($dir, 1) : $dir;
0493 // Add closing / if not present
0494 $dir = ($dir && substr($dir, -1) != '/') ? $dir . '/' : $dir;
0495
0496 if (!is_dir($rootdir . $dir))
0497 {
0498 return $matches;
0499 }
0500
0501 $dh = @opendir($rootdir . $dir);
0502
0503 if (!$dh)
0504 {
0505 return $matches;
0506 }
0507
0508 while (($fname = readdir($dh)) !== false)
0509 {
0510 if (is_file("$rootdir$dir$fname"))
0511 {
0512 if (filesize("$rootdir$dir$fname") && preg_match('#\.' . $type . '$#i', $fname))
0513 {
0514 $matches[$dir][] = $fname;
0515 }
0516 }
0517 else if ($fname[0] != '.' && is_dir("$rootdir$dir$fname"))
0518 {
0519 $matches += filelist($rootdir, "$dir$fname", $type);
0520 }
0521 }
0522 closedir($dh);
0523
0524 return $matches;
0525 }
0526
0527 /**
0528 * Move topic(s)
0529 */
0530 function move_topics($topic_ids, $forum_id, $auto_sync = true)
0531 {
0532 global $db, $phpbb_dispatcher;
0533
0534 if (empty($topic_ids))
0535 {
0536 return;
0537 }
0538
0539 $forum_ids = array($forum_id);
0540
0541 if (!is_array($topic_ids))
0542 {
0543 $topic_ids = array($topic_ids);
0544 }
0545
0546 /**
0547 * Perform additional actions before topics move
0548 *
0549 * @event core.move_topics_before
0550 * @var array topic_ids Array of the moved topic ids
0551 * @var string forum_id The forum id from where the topics are moved
0552 * @since 3.2.9-RC1
0553 */
0554 $vars = array(
0555 'topic_ids',
0556 'forum_id',
0557 );
0558 extract($phpbb_dispatcher->trigger_event('core.move_topics_before', compact($vars)));
0559
0560 $sql = 'DELETE FROM ' . TOPICS_TABLE . '
0561 WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids) . '
0562 AND forum_id = ' . $forum_id;
0563 $db->sql_query($sql);
0564
0565 if ($auto_sync)
0566 {
0567 $sql = 'SELECT DISTINCT forum_id
0568 FROM ' . TOPICS_TABLE . '
0569 WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
0570 $result = $db->sql_query($sql);
0571
0572 while ($row = $db->sql_fetchrow($result))
0573 {
0574 $forum_ids[] = $row['forum_id'];
0575 }
0576 $db->sql_freeresult($result);
0577 }
0578
0579 $table_ary = array(TOPICS_TABLE, POSTS_TABLE, LOG_TABLE, DRAFTS_TABLE, TOPICS_TRACK_TABLE);
0580
0581 /**
0582 * Perform additional actions before topics move
0583 *
0584 * @event core.move_topics_before_query
0585 * @var array table_ary Array of tables from which forum_id will be updated for all rows that hold the moved topics
0586 * @var array topic_ids Array of the moved topic ids
0587 * @var string forum_id The forum id from where the topics are moved
0588 * @var array forum_ids Array of the forums where the topics are moving (includes also forum_id)
0589 * @var bool auto_sync Whether or not to perform auto sync
0590 * @since 3.1.5-RC1
0591 */
0592 $vars = array(
0593 'table_ary',
0594 'topic_ids',
0595 'forum_id',
0596 'forum_ids',
0597 'auto_sync',
0598 );
0599 extract($phpbb_dispatcher->trigger_event('core.move_topics_before_query', compact($vars)));
0600
0601 foreach ($table_ary as $table)
0602 {
0603 $sql = "UPDATE $table
0604 SET forum_id = $forum_id
0605 WHERE " . $db->sql_in_set('topic_id', $topic_ids);
0606 $db->sql_query($sql);
0607 }
0608 unset($table_ary);
0609
0610 /**
0611 * Perform additional actions after topics move
0612 *
0613 * @event core.move_topics_after
0614 * @var array topic_ids Array of the moved topic ids
0615 * @var string forum_id The forum id from where the topics were moved
0616 * @var array forum_ids Array of the forums where the topics were moved (includes also forum_id)
0617 * @since 3.2.9-RC1
0618 */
0619 $vars = array(
0620 'topic_ids',
0621 'forum_id',
0622 'forum_ids',
0623 );
0624 extract($phpbb_dispatcher->trigger_event('core.move_topics_after', compact($vars)));
0625
0626 if ($auto_sync)
0627 {
0628 sync('forum', 'forum_id', $forum_ids, true, true);
0629 unset($forum_ids);
0630 }
0631 }
0632
0633 /**
0634 * Move post(s)
0635 */
0636 function move_posts($post_ids, $topic_id, $auto_sync = true)
0637 {
0638 global $db, $phpbb_dispatcher;
0639
0640 if (!is_array($post_ids))
0641 {
0642 $post_ids = array($post_ids);
0643 }
0644
0645 $forum_ids = array();
0646 $topic_ids = array($topic_id);
0647
0648 $sql = 'SELECT DISTINCT topic_id, forum_id
0649 FROM ' . POSTS_TABLE . '
0650 WHERE ' . $db->sql_in_set('post_id', $post_ids);
0651 $result = $db->sql_query($sql);
0652
0653 while ($row = $db->sql_fetchrow($result))
0654 {
0655 $forum_ids[] = (int) $row['forum_id'];
0656 $topic_ids[] = (int) $row['topic_id'];
0657 }
0658 $db->sql_freeresult($result);
0659
0660 $sql = 'SELECT forum_id
0661 FROM ' . TOPICS_TABLE . '
0662 WHERE topic_id = ' . $topic_id;
0663 $result = $db->sql_query($sql);
0664 $forum_row = $db->sql_fetchrow($result);
0665 $db->sql_freeresult($result);
0666
0667 if (!$forum_row)
0668 {
0669 trigger_error('NO_TOPIC');
0670 }
0671
0672 /**
0673 * Perform additional actions before moving posts
0674 *
0675 * @event core.move_posts_before
0676 * @var array post_ids Array of post ids to move
0677 * @var int topic_id The topic id the posts are moved to
0678 * @var bool auto_sync Whether or not to perform auto sync
0679 * @var array forum_ids Array of the forum ids the posts are moved from
0680 * @var array topic_ids Array of the topic ids the posts are moved from
0681 * @var array forum_row Array with the forum id of the topic the posts are moved to
0682 * @since 3.1.7-RC1
0683 */
0684 $vars = array(
0685 'post_ids',
0686 'topic_id',
0687 'auto_sync',
0688 'forum_ids',
0689 'topic_ids',
0690 'forum_row',
0691 );
0692 extract($phpbb_dispatcher->trigger_event('core.move_posts_before', compact($vars)));
0693
0694 $sql = 'UPDATE ' . POSTS_TABLE . '
0695 SET forum_id = ' . (int) $forum_row['forum_id'] . ", topic_id = $topic_id
0696 WHERE " . $db->sql_in_set('post_id', $post_ids);
0697 $db->sql_query($sql);
0698
0699 $sql = 'UPDATE ' . ATTACHMENTS_TABLE . "
0700 SET topic_id = $topic_id, in_message = 0
0701 WHERE " . $db->sql_in_set('post_msg_id', $post_ids);
0702 $db->sql_query($sql);
0703
0704 /**
0705 * Perform additional actions after moving posts
0706 *
0707 * @event core.move_posts_after
0708 * @var array post_ids Array of the moved post ids
0709 * @var int topic_id The topic id the posts are moved to
0710 * @var bool auto_sync Whether or not to perform auto sync
0711 * @var array forum_ids Array of the forum ids the posts are moved from
0712 * @var array topic_ids Array of the topic ids the posts are moved from
0713 * @var array forum_row Array with the forum id of the topic the posts are moved to
0714 * @since 3.1.7-RC1
0715 */
0716 $vars = array(
0717 'post_ids',
0718 'topic_id',
0719 'auto_sync',
0720 'forum_ids',
0721 'topic_ids',
0722 'forum_row',
0723 );
0724 extract($phpbb_dispatcher->trigger_event('core.move_posts_after', compact($vars)));
0725
0726 if ($auto_sync)
0727 {
0728 $forum_ids[] = (int) $forum_row['forum_id'];
0729
0730 sync('topic_reported', 'topic_id', $topic_ids);
0731 sync('topic_attachment', 'topic_id', $topic_ids);
0732 sync('topic', 'topic_id', $topic_ids, true);
0733 sync('forum', 'forum_id', $forum_ids, true, true);
0734
0735 /**
0736 * Perform additional actions after move post sync
0737 *
0738 * @event core.move_posts_sync_after
0739 * @var array post_ids Array of the moved post ids
0740 * @var int topic_id The topic id the posts are moved to
0741 * @var bool auto_sync Whether or not to perform auto sync
0742 * @var array forum_ids Array of the forum ids the posts are moved from
0743 * @var array topic_ids Array of the topic ids the posts are moved from
0744 * @var array forum_row Array with the forum id of the topic the posts are moved to
0745 * @since 3.1.11-RC1
0746 */
0747 $vars = array(
0748 'post_ids',
0749 'topic_id',
0750 'auto_sync',
0751 'forum_ids',
0752 'topic_ids',
0753 'forum_row',
0754 );
0755 extract($phpbb_dispatcher->trigger_event('core.move_posts_sync_after', compact($vars)));
0756 }
0757
0758 // Update posted information
0759 update_posted_info($topic_ids);
0760 }
0761
0762 /**
0763 * Remove topic(s)
0764 */
0765 function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_sync = true, $call_delete_posts = true)
0766 {
0767 global $db, $config, $phpbb_container, $phpbb_dispatcher;
0768
0769 $approved_topics = 0;
0770 $forum_ids = $topic_ids = array();
0771
0772 if ($where_type === 'range')
0773 {
0774 $where_clause = $where_ids;
0775 }
0776 else
0777 {
0778 $where_ids = (is_array($where_ids)) ? array_unique($where_ids) : array($where_ids);
0779
0780 if (!count($where_ids))
0781 {
0782 return array('topics' => 0, 'posts' => 0);
0783 }
0784
0785 $where_clause = $db->sql_in_set($where_type, $where_ids);
0786 }
0787
0788 // Making sure that delete_posts does not call delete_topics again...
0789 $return = array(
0790 'posts' => ($call_delete_posts) ? delete_posts($where_type, $where_ids, false, true, $post_count_sync, false) : 0,
0791 );
0792
0793 $sql = 'SELECT topic_id, forum_id, topic_visibility, topic_moved_id
0794 FROM ' . TOPICS_TABLE . '
0795 WHERE ' . $where_clause;
0796 $result = $db->sql_query($sql);
0797
0798 while ($row = $db->sql_fetchrow($result))
0799 {
0800 $forum_ids[] = $row['forum_id'];
0801 $topic_ids[] = $row['topic_id'];
0802
0803 if ($row['topic_visibility'] == ITEM_APPROVED && !$row['topic_moved_id'])
0804 {
0805 $approved_topics++;
0806 }
0807 }
0808 $db->sql_freeresult($result);
0809
0810 $return['topics'] = count($topic_ids);
0811
0812 if (!count($topic_ids))
0813 {
0814 return $return;
0815 }
0816
0817 $db->sql_transaction('begin');
0818
0819 $table_ary = array(BOOKMARKS_TABLE, TOPICS_TRACK_TABLE, TOPICS_POSTED_TABLE, POLL_VOTES_TABLE, POLL_OPTIONS_TABLE, TOPICS_WATCH_TABLE, TOPICS_TABLE);
0820
0821 /**
0822 * Perform additional actions before topic(s) deletion
0823 *
0824 * @event core.delete_topics_before_query
0825 * @var array table_ary Array of tables from which all rows will be deleted that hold a topic_id occuring in topic_ids
0826 * @var array topic_ids Array of topic ids to delete
0827 * @since 3.1.4-RC1
0828 */
0829 $vars = array(
0830 'table_ary',
0831 'topic_ids',
0832 );
0833 extract($phpbb_dispatcher->trigger_event('core.delete_topics_before_query', compact($vars)));
0834
0835 foreach ($table_ary as $table)
0836 {
0837 $sql = "DELETE FROM $table
0838 WHERE " . $db->sql_in_set('topic_id', $topic_ids);
0839 $db->sql_query($sql);
0840 }
0841 unset($table_ary);
0842
0843 /**
0844 * Perform additional actions after topic(s) deletion
0845 *
0846 * @event core.delete_topics_after_query
0847 * @var array topic_ids Array of topic ids that were deleted
0848 * @since 3.1.4-RC1
0849 */
0850 $vars = array(
0851 'topic_ids',
0852 );
0853 extract($phpbb_dispatcher->trigger_event('core.delete_topics_after_query', compact($vars)));
0854
0855 $moved_topic_ids = array();
0856
0857 // update the other forums
0858 $sql = 'SELECT topic_id, forum_id
0859 FROM ' . TOPICS_TABLE . '
0860 WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids);
0861 $result = $db->sql_query($sql);
0862
0863 while ($row = $db->sql_fetchrow($result))
0864 {
0865 $forum_ids[] = $row['forum_id'];
0866 $moved_topic_ids[] = $row['topic_id'];
0867 }
0868 $db->sql_freeresult($result);
0869
0870 if (count($moved_topic_ids))
0871 {
0872 $sql = 'DELETE FROM ' . TOPICS_TABLE . '
0873 WHERE ' . $db->sql_in_set('topic_id', $moved_topic_ids);
0874 $db->sql_query($sql);
0875 }
0876
0877 $db->sql_transaction('commit');
0878
0879 if ($auto_sync)
0880 {
0881 sync('forum', 'forum_id', array_unique($forum_ids), true, true);
0882 sync('topic_reported', $where_type, $where_ids);
0883 }
0884
0885 if ($approved_topics)
0886 {
0887 $config->increment('num_topics', $approved_topics * (-1), false);
0888 }
0889
0890 /* @var $phpbb_notifications \phpbb\notification\manager */
0891 $phpbb_notifications = $phpbb_container->get('notification_manager');
0892
0893 $phpbb_notifications->delete_notifications(array(
0894 'notification.type.topic',
0895 'notification.type.approve_topic',
0896 'notification.type.topic_in_queue',
0897 ), $topic_ids);
0898
0899 return $return;
0900 }
0901
0902 /**
0903 * Remove post(s)
0904 */
0905 function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync = true, $post_count_sync = true, $call_delete_topics = true)
0906 {
0907 global $db, $config, $phpbb_root_path, $phpEx, $auth, $user, $phpbb_container, $phpbb_dispatcher;
0908
0909 // Notifications types to delete
0910 $delete_notifications_types = array(
0911 'notification.type.quote',
0912 'notification.type.approve_post',
0913 'notification.type.post_in_queue',
0914 'notification.type.report_post',
0915 );
0916
0917 /**
0918 * Perform additional actions before post(s) deletion
0919 *
0920 * @event core.delete_posts_before
0921 * @var string where_type Variable containing posts deletion mode
0922 * @var mixed where_ids Array or comma separated list of posts ids to delete
0923 * @var bool auto_sync Flag indicating if topics/forums should be synchronized
0924 * @var bool posted_sync Flag indicating if topics_posted table should be resynchronized
0925 * @var bool post_count_sync Flag indicating if posts count should be resynchronized
0926 * @var bool call_delete_topics Flag indicating if topics having no posts should be deleted
0927 * @var array delete_notifications_types Array with notifications types to delete
0928 * @since 3.1.0-a4
0929 */
0930 $vars = array(
0931 'where_type',
0932 'where_ids',
0933 'auto_sync',
0934 'posted_sync',
0935 'post_count_sync',
0936 'call_delete_topics',
0937 'delete_notifications_types',
0938 );
0939 extract($phpbb_dispatcher->trigger_event('core.delete_posts_before', compact($vars)));
0940
0941 if ($where_type === 'range')
0942 {
0943 $where_clause = $where_ids;
0944 }
0945 else
0946 {
0947 if (is_array($where_ids))
0948 {
0949 $where_ids = array_unique($where_ids);
0950 }
0951 else
0952 {
0953 $where_ids = array($where_ids);
0954 }
0955
0956 if (!count($where_ids))
0957 {
0958 return false;
0959 }
0960
0961 $where_ids = array_map('intval', $where_ids);
0962
0963 /* Possible code for splitting post deletion
0964 if (count($where_ids) >= 1001)
0965 {
0966 // Split into chunks of 1000
0967 $chunks = array_chunk($where_ids, 1000);
0968
0969 foreach ($chunks as $_where_ids)
0970 {
0971 delete_posts($where_type, $_where_ids, $auto_sync, $posted_sync, $post_count_sync, $call_delete_topics);
0972 }
0973
0974 return;
0975 }*/
0976
0977 $where_clause = $db->sql_in_set($where_type, $where_ids);
0978 }
0979
0980 $approved_posts = 0;
0981 $post_ids = $topic_ids = $forum_ids = $post_counts = $remove_topics = array();
0982
0983 $sql = 'SELECT post_id, poster_id, post_visibility, post_postcount, topic_id, forum_id
0984 FROM ' . POSTS_TABLE . '
0985 WHERE ' . $where_clause;
0986 $result = $db->sql_query($sql);
0987
0988 while ($row = $db->sql_fetchrow($result))
0989 {
0990 $post_ids[] = (int) $row['post_id'];
0991 $poster_ids[] = (int) $row['poster_id'];
0992 $topic_ids[] = (int) $row['topic_id'];
0993 $forum_ids[] = (int) $row['forum_id'];
0994
0995 if ($row['post_postcount'] && $post_count_sync && $row['post_visibility'] == ITEM_APPROVED)
0996 {
0997 $post_counts[$row['poster_id']] = (!empty($post_counts[$row['poster_id']])) ? $post_counts[$row['poster_id']] + 1 : 1;
0998 }
0999
1000 if ($row['post_visibility'] == ITEM_APPROVED)
1001 {
1002 $approved_posts++;
1003 }
1004 }
1005 $db->sql_freeresult($result);
1006
1007 if (!count($post_ids))
1008 {
1009 return false;
1010 }
1011
1012 $db->sql_transaction('begin');
1013
1014 $table_ary = array(POSTS_TABLE, REPORTS_TABLE);
1015
1016 /**
1017 * Perform additional actions during post(s) deletion before running the queries
1018 *
1019 * @event core.delete_posts_in_transaction_before
1020 * @var array post_ids Array with deleted posts' ids
1021 * @var array poster_ids Array with deleted posts' author ids
1022 * @var array topic_ids Array with deleted posts' topic ids
1023 * @var array forum_ids Array with deleted posts' forum ids
1024 * @var string where_type Variable containing posts deletion mode
1025 * @var mixed where_ids Array or comma separated list of post ids to delete
1026 * @var array delete_notifications_types Array with notifications types to delete
1027 * @var array table_ary Array with table names to delete data from
1028 * @since 3.1.7-RC1
1029 */
1030 $vars = array(
1031 'post_ids',
1032 'poster_ids',
1033 'topic_ids',
1034 'forum_ids',
1035 'where_type',
1036 'where_ids',
1037 'delete_notifications_types',
1038 'table_ary',
1039 );
1040 extract($phpbb_dispatcher->trigger_event('core.delete_posts_in_transaction_before', compact($vars)));
1041
1042 foreach ($table_ary as $table)
1043 {
1044 $sql = "DELETE FROM $table
1045 WHERE " . $db->sql_in_set('post_id', $post_ids);
1046 $db->sql_query($sql);
1047 }
1048 unset($table_ary);
1049
1050 // Adjust users post counts
1051 if (count($post_counts) && $post_count_sync)
1052 {
1053 foreach ($post_counts as $poster_id => $substract)
1054 {
1055 $sql = 'UPDATE ' . USERS_TABLE . '
1056 SET user_posts = 0
1057 WHERE user_id = ' . $poster_id . '
1058 AND user_posts < ' . $substract;
1059 $db->sql_query($sql);
1060
1061 $sql = 'UPDATE ' . USERS_TABLE . '
1062 SET user_posts = user_posts - ' . $substract . '
1063 WHERE user_id = ' . $poster_id . '
1064 AND user_posts >= ' . $substract;
1065 $db->sql_query($sql);
1066 }
1067 }
1068
1069 // Remove topics now having no posts?
1070 if (count($topic_ids))
1071 {
1072 $sql = 'SELECT topic_id
1073 FROM ' . POSTS_TABLE . '
1074 WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
1075 GROUP BY topic_id';
1076 $result = $db->sql_query($sql);
1077
1078 while ($row = $db->sql_fetchrow($result))
1079 {
1080 $remove_topics[] = $row['topic_id'];
1081 }
1082 $db->sql_freeresult($result);
1083
1084 // Actually, those not within remove_topics should be removed. ;)
1085 $remove_topics = array_diff($topic_ids, $remove_topics);
1086 }
1087
1088 // Remove the message from the search index
1089 $search_type = $config['search_type'];
1090
1091 if (!class_exists($search_type))
1092 {
1093 trigger_error('NO_SUCH_SEARCH_MODULE');
1094 }
1095
1096 $error = false;
1097 $search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
1098
1099 if ($error)
1100 {
1101 trigger_error($error);
1102 }
1103
1104 $search->index_remove($post_ids, $poster_ids, $forum_ids);
1105
1106 /** @var \phpbb\attachment\manager $attachment_manager */
1107 $attachment_manager = $phpbb_container->get('attachment.manager');
1108 $attachment_manager->delete('post', $post_ids, false);
1109 unset($attachment_manager);
1110
1111 /**
1112 * Perform additional actions during post(s) deletion
1113 *
1114 * @event core.delete_posts_in_transaction
1115 * @var array post_ids Array with deleted posts' ids
1116 * @var array poster_ids Array with deleted posts' author ids
1117 * @var array topic_ids Array with deleted posts' topic ids
1118 * @var array forum_ids Array with deleted posts' forum ids
1119 * @var string where_type Variable containing posts deletion mode
1120 * @var mixed where_ids Array or comma separated list of posts ids to delete
1121 * @var array delete_notifications_types Array with notifications types to delete
1122 * @since 3.1.0-a4
1123 */
1124 $vars = array(
1125 'post_ids',
1126 'poster_ids',
1127 'topic_ids',
1128 'forum_ids',
1129 'where_type',
1130 'where_ids',
1131 'delete_notifications_types',
1132 );
1133 extract($phpbb_dispatcher->trigger_event('core.delete_posts_in_transaction', compact($vars)));
1134
1135 $db->sql_transaction('commit');
1136
1137 /**
1138 * Perform additional actions after post(s) deletion
1139 *
1140 * @event core.delete_posts_after
1141 * @var array post_ids Array with deleted posts' ids
1142 * @var array poster_ids Array with deleted posts' author ids
1143 * @var array topic_ids Array with deleted posts' topic ids
1144 * @var array forum_ids Array with deleted posts' forum ids
1145 * @var string where_type Variable containing posts deletion mode
1146 * @var mixed where_ids Array or comma separated list of posts ids to delete
1147 * @var array delete_notifications_types Array with notifications types to delete
1148 * @since 3.1.0-a4
1149 */
1150 $vars = array(
1151 'post_ids',
1152 'poster_ids',
1153 'topic_ids',
1154 'forum_ids',
1155 'where_type',
1156 'where_ids',
1157 'delete_notifications_types',
1158 );
1159 extract($phpbb_dispatcher->trigger_event('core.delete_posts_after', compact($vars)));
1160
1161 // Resync topics_posted table
1162 if ($posted_sync)
1163 {
1164 update_posted_info($topic_ids);
1165 }
1166
1167 if ($auto_sync)
1168 {
1169 sync('topic_reported', 'topic_id', $topic_ids);
1170 sync('topic', 'topic_id', $topic_ids, true);
1171 sync('forum', 'forum_id', $forum_ids, true, true);
1172 }
1173
1174 if ($approved_posts && $post_count_sync)
1175 {
1176 $config->increment('num_posts', $approved_posts * (-1), false);
1177 }
1178
1179 // We actually remove topics now to not be inconsistent (the delete_topics function calls this function too)
1180 if (count($remove_topics) && $call_delete_topics)
1181 {
1182 delete_topics('topic_id', $remove_topics, $auto_sync, $post_count_sync, false);
1183 }
1184
1185 /* @var $phpbb_notifications \phpbb\notification\manager */
1186 $phpbb_notifications = $phpbb_container->get('notification_manager');
1187
1188 $phpbb_notifications->delete_notifications($delete_notifications_types, $post_ids);
1189
1190 return count($post_ids);
1191 }
1192
1193 /**
1194 * Deletes shadow topics pointing to a specified forum.
1195 *
1196 * @param int $forum_id The forum id
1197 * @param string $sql_more Additional WHERE statement, e.g. t.topic_time < (time() - 1234)
1198 * @param bool $auto_sync Will call sync() if this is true
1199 *
1200 * @return array Array with affected forums
1201 */
1202 function delete_topic_shadows($forum_id, $sql_more = '', $auto_sync = true)
1203 {
1204 global $db;
1205
1206 if (!$forum_id)
1207 {
1208 // Nothing to do.
1209 return;
1210 }
1211
1212 // Set of affected forums we have to resync
1213 $sync_forum_ids = array();
1214
1215 // Amount of topics we select and delete at once.
1216 $batch_size = 500;
1217
1218 do
1219 {
1220 $sql = 'SELECT t2.forum_id, t2.topic_id
1221 FROM ' . TOPICS_TABLE . ' t2, ' . TOPICS_TABLE . ' t
1222 WHERE t2.topic_moved_id = t.topic_id
1223 AND t.forum_id = ' . (int) $forum_id . '
1224 ' . (($sql_more) ? 'AND ' . $sql_more : '');
1225 $result = $db->sql_query_limit($sql, $batch_size);
1226
1227 $topic_ids = array();
1228 while ($row = $db->sql_fetchrow($result))
1229 {
1230 $topic_ids[] = (int) $row['topic_id'];
1231
1232 $sync_forum_ids[(int) $row['forum_id']] = (int) $row['forum_id'];
1233 }
1234 $db->sql_freeresult($result);
1235
1236 if (!empty($topic_ids))
1237 {
1238 $sql = 'DELETE FROM ' . TOPICS_TABLE . '
1239 WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1240 $db->sql_query($sql);
1241 }
1242 }
1243 while (count($topic_ids) == $batch_size);
1244
1245 if ($auto_sync)
1246 {
1247 sync('forum', 'forum_id', $sync_forum_ids, true, true);
1248 }
1249
1250 return $sync_forum_ids;
1251 }
1252
1253 /**
1254 * Update/Sync posted information for topics
1255 */
1256 function update_posted_info(&$topic_ids)
1257 {
1258 global $db, $config;
1259
1260 if (empty($topic_ids) || !$config['load_db_track'])
1261 {
1262 return;
1263 }
1264
1265 // First of all, let us remove any posted information for these topics
1266 $sql = 'DELETE FROM ' . TOPICS_POSTED_TABLE . '
1267 WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1268 $db->sql_query($sql);
1269
1270 // Now, let us collect the user/topic combos for rebuilding the information
1271 $sql = 'SELECT poster_id, topic_id
1272 FROM ' . POSTS_TABLE . '
1273 WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
1274 AND poster_id <> ' . ANONYMOUS . '
1275 GROUP BY poster_id, topic_id';
1276 $result = $db->sql_query($sql);
1277
1278 $posted = array();
1279 while ($row = $db->sql_fetchrow($result))
1280 {
1281 // Add as key to make them unique (grouping by) and circumvent empty keys on array_unique
1282 $posted[$row['poster_id']][] = $row['topic_id'];
1283 }
1284 $db->sql_freeresult($result);
1285
1286 // Now add the information...
1287 $sql_ary = array();
1288 foreach ($posted as $user_id => $topic_row)
1289 {
1290 foreach ($topic_row as $topic_id)
1291 {
1292 $sql_ary[] = array(
1293 'user_id' => (int) $user_id,
1294 'topic_id' => (int) $topic_id,
1295 'topic_posted' => 1,
1296 );
1297 }
1298 }
1299 unset($posted);
1300
1301 $db->sql_multi_insert(TOPICS_POSTED_TABLE, $sql_ary);
1302 }
1303
1304 /**
1305 * All-encompasing sync function
1306 *
1307 * Exaples:
1308 * <code>
1309 * sync('topic', 'topic_id', 123); // resync topic #123
1310 * sync('topic', 'forum_id', array(2, 3)); // resync topics from forum #2 and #3
1311 * sync('topic'); // resync all topics
1312 * sync('topic', 'range', 'topic_id BETWEEN 1 AND 60'); // resync a range of topics/forums (only available for 'topic' and 'forum' modes)
1313 * </code>
1314 *
1315 * Modes:
1316 * - forum Resync complete forum
1317 * - topic Resync topics
1318 * - topic_moved Removes topic shadows that would be in the same forum as the topic they link to
1319 * - topic_visibility Resyncs the topic_visibility flag according to the status of the first post
1320 * - post_reported Resyncs the post_reported flag, relying on actual reports
1321 * - topic_reported Resyncs the topic_reported flag, relying on post_reported flags
1322 * - post_attachement Same as post_reported, but with attachment flags
1323 * - topic_attachement Same as topic_reported, but with attachment flags
1324 */
1325 function sync($mode, $where_type = '', $where_ids = '', $resync_parents = false, $sync_extra = false)
1326 {
1327 global $db, $phpbb_dispatcher;
1328
1329 if (is_array($where_ids))
1330 {
1331 $where_ids = array_unique($where_ids);
1332 $where_ids = array_map('intval', $where_ids);
1333 }
1334 else if ($where_type != 'range')
1335 {
1336 $where_ids = ($where_ids) ? array((int) $where_ids) : array();
1337 }
1338
1339 if ($mode == 'forum' || $mode == 'topic' || $mode == 'topic_visibility' || $mode == 'topic_reported' || $mode == 'post_reported')
1340 {
1341 if (!$where_type)
1342 {
1343 $where_sql = '';
1344 $where_sql_and = 'WHERE';
1345 }
1346 else if ($where_type == 'range')
1347 {
1348 // Only check a range of topics/forums. For instance: 'topic_id BETWEEN 1 AND 60'
1349 $where_sql = 'WHERE (' . $mode[0] . ".$where_ids)";
1350 $where_sql_and = $where_sql . "\n\tAND";
1351 }
1352 else
1353 {
1354 // Do not sync the "global forum"
1355 $where_ids = array_diff($where_ids, array(0));
1356
1357 if (!count($where_ids))
1358 {
1359 // Empty array with IDs. This means that we don't have any work to do. Just return.
1360 return;
1361 }
1362
1363 // Limit the topics/forums we are syncing, use specific topic/forum IDs.
1364 // $where_type contains the field for the where clause (forum_id, topic_id)
1365 $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids);
1366 $where_sql_and = $where_sql . "\n\tAND";
1367 }
1368 }
1369 else
1370 {
1371 if (!count($where_ids))
1372 {
1373 return;
1374 }
1375
1376 // $where_type contains the field for the where clause (forum_id, topic_id)
1377 $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids);
1378 $where_sql_and = $where_sql . "\n\tAND";
1379 }
1380
1381 switch ($mode)
1382 {
1383 case 'topic_moved':
1384 $db->sql_transaction('begin');
1385 switch ($db->get_sql_layer())
1386 {
1387 case 'mysqli':
1388 $sql = 'DELETE FROM ' . TOPICS_TABLE . '
1389 USING ' . TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2
1390 WHERE t1.topic_moved_id = t2.topic_id
1391 AND t1.forum_id = t2.forum_id";
1392 $db->sql_query($sql);
1393 break;
1394
1395 default:
1396 $sql = 'SELECT t1.topic_id
1397 FROM ' .TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2
1398 WHERE t1.topic_moved_id = t2.topic_id
1399 AND t1.forum_id = t2.forum_id";
1400 $result = $db->sql_query($sql);
1401
1402 $topic_id_ary = array();
1403 while ($row = $db->sql_fetchrow($result))
1404 {
1405 $topic_id_ary[] = $row['topic_id'];
1406 }
1407 $db->sql_freeresult($result);
1408
1409 if (!count($topic_id_ary))
1410 {
1411 return;
1412 }
1413
1414 $sql = 'DELETE FROM ' . TOPICS_TABLE . '
1415 WHERE ' . $db->sql_in_set('topic_id', $topic_id_ary);
1416 $db->sql_query($sql);
1417
1418 break;
1419 }
1420
1421 $db->sql_transaction('commit');
1422 break;
1423
1424 case 'topic_visibility':
1425
1426 $db->sql_transaction('begin');
1427
1428 $sql = 'SELECT t.topic_id, p.post_visibility
1429 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
1430 $where_sql_and p.topic_id = t.topic_id
1431 AND p.post_visibility = " . ITEM_APPROVED;
1432 $result = $db->sql_query($sql);
1433
1434 $topics_approved = array();
1435 while ($row = $db->sql_fetchrow($result))
1436 {
1437 $topics_approved[] = (int) $row['topic_id'];
1438 }
1439 $db->sql_freeresult($result);
1440
1441 $sql = 'SELECT t.topic_id, p.post_visibility
1442 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
1443 $where_sql_and " . $db->sql_in_set('t.topic_id', $topics_approved, true, true) . '
1444 AND p.topic_id = t.topic_id
1445 AND p.post_visibility = ' . ITEM_DELETED;
1446 $result = $db->sql_query($sql);
1447
1448 $topics_softdeleted = array();
1449 while ($row = $db->sql_fetchrow($result))
1450 {
1451 $topics_softdeleted[] = (int) $row['topic_id'];
1452 }
1453 $db->sql_freeresult($result);
1454
1455 $topics_softdeleted = array_diff($topics_softdeleted, $topics_approved);
1456 $topics_not_unapproved = array_merge($topics_softdeleted, $topics_approved);
1457
1458 $update_ary = array(
1459 ITEM_UNAPPROVED => (!empty($topics_not_unapproved)) ? $where_sql_and . ' ' . $db->sql_in_set('topic_id', $topics_not_unapproved, true) : '',
1460 ITEM_APPROVED => (!empty($topics_approved)) ? ' WHERE ' . $db->sql_in_set('topic_id', $topics_approved) : '',
1461 ITEM_DELETED => (!empty($topics_softdeleted)) ? ' WHERE ' . $db->sql_in_set('topic_id', $topics_softdeleted) : '',
1462 );
1463
1464 foreach ($update_ary as $visibility => $sql_where)
1465 {
1466 if ($sql_where)
1467 {
1468 $sql = 'UPDATE ' . TOPICS_TABLE . '
1469 SET topic_visibility = ' . $visibility . '
1470 ' . $sql_where;
1471 $db->sql_query($sql);
1472 }
1473 }
1474
1475 $db->sql_transaction('commit');
1476 break;
1477
1478 case 'post_reported':
1479 $post_ids = $post_reported = array();
1480
1481 $db->sql_transaction('begin');
1482
1483 $sql = 'SELECT p.post_id, p.post_reported
1484 FROM ' . POSTS_TABLE . " p
1485 $where_sql
1486 GROUP BY p.post_id, p.post_reported";
1487 $result = $db->sql_query($sql);
1488
1489 while ($row = $db->sql_fetchrow($result))
1490 {
1491 $post_ids[$row['post_id']] = $row['post_id'];
1492 if ($row['post_reported'])
1493 {
1494 $post_reported[$row['post_id']] = 1;
1495 }
1496 }
1497 $db->sql_freeresult($result);
1498
1499 $sql = 'SELECT DISTINCT(post_id)
1500 FROM ' . REPORTS_TABLE . '
1501 WHERE ' . $db->sql_in_set('post_id', $post_ids) . '
1502 AND report_closed = 0';
1503 $result = $db->sql_query($sql);
1504
1505 $post_ids = array();
1506 while ($row = $db->sql_fetchrow($result))
1507 {
1508 if (!isset($post_reported[$row['post_id']]))
1509 {
1510 $post_ids[] = $row['post_id'];
1511 }
1512 else
1513 {
1514 unset($post_reported[$row['post_id']]);
1515 }
1516 }
1517 $db->sql_freeresult($result);
1518
1519 // $post_reported should be empty by now, if it's not it contains
1520 // posts that are falsely flagged as reported
1521 foreach ($post_reported as $post_id => $void)
1522 {
1523 $post_ids[] = $post_id;
1524 }
1525
1526 if (count($post_ids))
1527 {
1528 $sql = 'UPDATE ' . POSTS_TABLE . '
1529 SET post_reported = 1 - post_reported
1530 WHERE ' . $db->sql_in_set('post_id', $post_ids);
1531 $db->sql_query($sql);
1532 }
1533
1534 $db->sql_transaction('commit');
1535 break;
1536
1537 case 'topic_reported':
1538 if ($sync_extra)
1539 {
1540 sync('post_reported', $where_type, $where_ids);
1541 }
1542
1543 $topic_ids = $topic_reported = array();
1544
1545 $db->sql_transaction('begin');
1546
1547 $sql = 'SELECT DISTINCT(t.topic_id)
1548 FROM ' . POSTS_TABLE . " t
1549 $where_sql_and t.post_reported = 1";
1550 $result = $db->sql_query($sql);
1551
1552 while ($row = $db->sql_fetchrow($result))
1553 {
1554 $topic_reported[$row['topic_id']] = 1;
1555 }
1556 $db->sql_freeresult($result);
1557
1558 $sql = 'SELECT t.topic_id, t.topic_reported
1559 FROM ' . TOPICS_TABLE . " t
1560 $where_sql";
1561 $result = $db->sql_query($sql);
1562
1563 while ($row = $db->sql_fetchrow($result))
1564 {
1565 if ($row['topic_reported'] ^ isset($topic_reported[$row['topic_id']]))
1566 {
1567 $topic_ids[] = $row['topic_id'];
1568 }
1569 }
1570 $db->sql_freeresult($result);
1571
1572 if (count($topic_ids))
1573 {
1574 $sql = 'UPDATE ' . TOPICS_TABLE . '
1575 SET topic_reported = 1 - topic_reported
1576 WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1577 $db->sql_query($sql);
1578 }
1579
1580 $db->sql_transaction('commit');
1581 break;
1582
1583 case 'post_attachment':
1584 $post_ids = $post_attachment = array();
1585
1586 $db->sql_transaction('begin');
1587
1588 $sql = 'SELECT p.post_id, p.post_attachment
1589 FROM ' . POSTS_TABLE . " p
1590 $where_sql
1591 GROUP BY p.post_id, p.post_attachment";
1592 $result = $db->sql_query($sql);
1593
1594 while ($row = $db->sql_fetchrow($result))
1595 {
1596 $post_ids[$row['post_id']] = $row['post_id'];
1597 if ($row['post_attachment'])
1598 {
1599 $post_attachment[$row['post_id']] = 1;
1600 }
1601 }
1602 $db->sql_freeresult($result);
1603
1604 $sql = 'SELECT DISTINCT(post_msg_id)
1605 FROM ' . ATTACHMENTS_TABLE . '
1606 WHERE ' . $db->sql_in_set('post_msg_id', $post_ids) . '
1607 AND in_message = 0';
1608 $result = $db->sql_query($sql);
1609
1610 $post_ids = array();
1611 while ($row = $db->sql_fetchrow($result))
1612 {
1613 if (!isset($post_attachment[$row['post_msg_id']]))
1614 {
1615 $post_ids[] = $row['post_msg_id'];
1616 }
1617 else
1618 {
1619 unset($post_attachment[$row['post_msg_id']]);
1620 }
1621 }
1622 $db->sql_freeresult($result);
1623
1624 // $post_attachment should be empty by now, if it's not it contains
1625 // posts that are falsely flagged as having attachments
1626 foreach ($post_attachment as $post_id => $void)
1627 {
1628 $post_ids[] = $post_id;
1629 }
1630
1631 if (count($post_ids))
1632 {
1633 $sql = 'UPDATE ' . POSTS_TABLE . '
1634 SET post_attachment = 1 - post_attachment
1635 WHERE ' . $db->sql_in_set('post_id', $post_ids);
1636 $db->sql_query($sql);
1637 }
1638
1639 $db->sql_transaction('commit');
1640 break;
1641
1642 case 'topic_attachment':
1643 if ($sync_extra)
1644 {
1645 sync('post_attachment', $where_type, $where_ids);
1646 }
1647
1648 $topic_ids = $topic_attachment = array();
1649
1650 $db->sql_transaction('begin');
1651
1652 $sql = 'SELECT DISTINCT(t.topic_id)
1653 FROM ' . POSTS_TABLE . " t
1654 $where_sql_and t.post_attachment = 1";
1655 $result = $db->sql_query($sql);
1656
1657 while ($row = $db->sql_fetchrow($result))
1658 {
1659 $topic_attachment[$row['topic_id']] = 1;
1660 }
1661 $db->sql_freeresult($result);
1662
1663 $sql = 'SELECT t.topic_id, t.topic_attachment
1664 FROM ' . TOPICS_TABLE . " t
1665 $where_sql";
1666 $result = $db->sql_query($sql);
1667
1668 while ($row = $db->sql_fetchrow($result))
1669 {
1670 if ($row['topic_attachment'] ^ isset($topic_attachment[$row['topic_id']]))
1671 {
1672 $topic_ids[] = $row['topic_id'];
1673 }
1674 }
1675 $db->sql_freeresult($result);
1676
1677 if (count($topic_ids))
1678 {
1679 $sql = 'UPDATE ' . TOPICS_TABLE . '
1680 SET topic_attachment = 1 - topic_attachment
1681 WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
1682 $db->sql_query($sql);
1683 }
1684
1685 $db->sql_transaction('commit');
1686
1687 break;
1688
1689 case 'forum':
1690
1691 $db->sql_transaction('begin');
1692
1693 // 1: Get the list of all forums
1694 $sql = 'SELECT f.*
1695 FROM ' . FORUMS_TABLE . " f
1696 $where_sql";
1697 $result = $db->sql_query($sql);
1698
1699 $forum_data = $forum_ids = $post_ids = $last_post_id = $post_info = array();
1700 while ($row = $db->sql_fetchrow($result))
1701 {
1702 if ($row['forum_type'] == FORUM_LINK)
1703 {
1704 continue;
1705 }
1706
1707 $forum_id = (int) $row['forum_id'];
1708 $forum_ids[$forum_id] = $forum_id;
1709
1710 $forum_data[$forum_id] = $row;
1711 if ($sync_extra)
1712 {
1713 $forum_data[$forum_id]['posts_approved'] = 0;
1714 $forum_data[$forum_id]['posts_unapproved'] = 0;
1715 $forum_data[$forum_id]['posts_softdeleted'] = 0;
1716 $forum_data[$forum_id]['topics_approved'] = 0;
1717 $forum_data[$forum_id]['topics_unapproved'] = 0;
1718 $forum_data[$forum_id]['topics_softdeleted'] = 0;
1719 }
1720 $forum_data[$forum_id]['last_post_id'] = 0;
1721 $forum_data[$forum_id]['last_post_subject'] = '';
1722 $forum_data[$forum_id]['last_post_time'] = 0;
1723 $forum_data[$forum_id]['last_poster_id'] = 0;
1724 $forum_data[$forum_id]['last_poster_name'] = '';
1725 $forum_data[$forum_id]['last_poster_colour'] = '';
1726 }
1727 $db->sql_freeresult($result);
1728
1729 if (!count($forum_ids))
1730 {
1731 break;
1732 }
1733
1734 $forum_ids = array_values($forum_ids);
1735
1736 // 2: Get topic counts for each forum (optional)
1737 if ($sync_extra)
1738 {
1739 $sql = 'SELECT forum_id, topic_visibility, COUNT(topic_id) AS total_topics
1740 FROM ' . TOPICS_TABLE . '
1741 WHERE ' . $db->sql_in_set('forum_id', $forum_ids) . '
1742 GROUP BY forum_id, topic_visibility';
1743 $result = $db->sql_query($sql);
1744
1745 while ($row = $db->sql_fetchrow($result))
1746 {
1747 $forum_id = (int) $row['forum_id'];
1748
1749 if ($row['topic_visibility'] == ITEM_APPROVED)
1750 {
1751 $forum_data[$forum_id]['topics_approved'] = $row['total_topics'];
1752 }
1753 else if ($row['topic_visibility'] == ITEM_UNAPPROVED || $row['topic_visibility'] == ITEM_REAPPROVE)
1754 {
1755 $forum_data[$forum_id]['topics_unapproved'] = $row['total_topics'];
1756 }
1757 else if ($row['topic_visibility'] == ITEM_DELETED)
1758 {
1759 $forum_data[$forum_id]['topics_softdeleted'] = $row['total_topics'];
1760 }
1761 }
1762 $db->sql_freeresult($result);
1763 }
1764
1765 // 3: Get post count for each forum (optional)
1766 if ($sync_extra)
1767 {
1768 if (count($forum_ids) == 1)
1769 {
1770 $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
1771 FROM ' . TOPICS_TABLE . ' t
1772 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
1773 AND t.topic_status <> ' . ITEM_MOVED;
1774 }
1775 else
1776 {
1777 $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
1778 FROM ' . TOPICS_TABLE . ' t
1779 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
1780 AND t.topic_status <> ' . ITEM_MOVED . '
1781 GROUP BY t.forum_id';
1782 }
1783
1784 $result = $db->sql_query($sql);
1785
1786 while ($row = $db->sql_fetchrow($result))
1787 {
1788 $forum_id = (count($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id'];
1789
1790 $forum_data[$forum_id]['posts_approved'] = (int) $row['forum_posts_approved'];
1791 $forum_data[$forum_id]['posts_unapproved'] = (int) $row['forum_posts_unapproved'];
1792 $forum_data[$forum_id]['posts_softdeleted'] = (int) $row['forum_posts_softdeleted'];
1793 }
1794 $db->sql_freeresult($result);
1795 }
1796
1797 // 4: Get last_post_id for each forum
1798 if (count($forum_ids) == 1)
1799 {
1800 $sql = 'SELECT MAX(t.topic_last_post_id) as last_post_id
1801 FROM ' . TOPICS_TABLE . ' t
1802 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
1803 AND t.topic_visibility = ' . ITEM_APPROVED;
1804 }
1805 else
1806 {
1807 $sql = 'SELECT t.forum_id, MAX(t.topic_last_post_id) as last_post_id
1808 FROM ' . TOPICS_TABLE . ' t
1809 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
1810 AND t.topic_visibility = ' . ITEM_APPROVED . '
1811 GROUP BY t.forum_id';
1812 }
1813
1814 $result = $db->sql_query($sql);
1815
1816 while ($row = $db->sql_fetchrow($result))
1817 {
1818 $forum_id = (count($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id'];
1819
1820 $forum_data[$forum_id]['last_post_id'] = (int) $row['last_post_id'];
1821
1822 $post_ids[] = $row['last_post_id'];
1823 }
1824 $db->sql_freeresult($result);
1825
1826 // 5: Retrieve last_post infos
1827 if (count($post_ids))
1828 {
1829 $sql_ary = array(
1830 'SELECT' => 'p.post_id, p.poster_id, p.post_subject, p.post_time, p.post_username, u.username, u.user_colour',
1831 'FROM' => array(
1832 POSTS_TABLE => 'p',
1833 USERS_TABLE => 'u',
1834 ),
1835 'WHERE' => $db->sql_in_set('p.post_id', $post_ids) . '
1836 AND p.poster_id = u.user_id',
1837 );
1838
1839 /**
1840 * Event to modify the SQL array to get the post and user data from all forums' last posts
1841 *
1842 * @event core.sync_forum_last_post_info_sql
1843 * @var array sql_ary SQL array with some post and user data from the last posts list
1844 * @since 3.3.5-RC1
1845 */
1846 $vars = ['sql_ary'];
1847 extract($phpbb_dispatcher->trigger_event('core.sync_forum_last_post_info_sql', compact($vars)));
1848 $result = $db->sql_query($db->sql_build_query('SELECT', $sql_ary));
1849
1850 while ($row = $db->sql_fetchrow($result))
1851 {
1852 $post_info[$row['post_id']] = $row;
1853 }
1854 $db->sql_freeresult($result);
1855
1856 foreach ($forum_data as $forum_id => $data)
1857 {
1858 if ($data['last_post_id'])
1859 {
1860 if (isset($post_info[$data['last_post_id']]))
1861 {
1862 $forum_data[$forum_id]['last_post_subject'] = $post_info[$data['last_post_id']]['post_subject'];
1863 $forum_data[$forum_id]['last_post_time'] = $post_info[$data['last_post_id']]['post_time'];
1864 $forum_data[$forum_id]['last_poster_id'] = $post_info[$data['last_post_id']]['poster_id'];
1865 $forum_data[$forum_id]['last_poster_name'] = ($post_info[$data['last_post_id']]['poster_id'] != ANONYMOUS) ? $post_info[$data['last_post_id']]['username'] : $post_info[$data['last_post_id']]['post_username'];
1866 $forum_data[$forum_id]['last_poster_colour'] = $post_info[$data['last_post_id']]['user_colour'];
1867 }
1868 else
1869 {
1870 // For some reason we did not find the post in the db
1871 $forum_data[$forum_id]['last_post_id'] = 0;
1872 $forum_data[$forum_id]['last_post_subject'] = '';
1873 $forum_data[$forum_id]['last_post_time'] = 0;
1874 $forum_data[$forum_id]['last_poster_id'] = 0;
1875 $forum_data[$forum_id]['last_poster_name'] = '';
1876 $forum_data[$forum_id]['last_poster_colour'] = '';
1877 }
1878 }
1879 }
1880 }
1881
1882 // 6: Now do that thing
1883 $fieldnames = array('last_post_id', 'last_post_subject', 'last_post_time', 'last_poster_id', 'last_poster_name', 'last_poster_colour');
1884
1885 if ($sync_extra)
1886 {
1887 array_push($fieldnames, 'posts_approved', 'posts_unapproved', 'posts_softdeleted', 'topics_approved', 'topics_unapproved', 'topics_softdeleted');
1888 }
1889
1890 /**
1891 * Event to modify the SQL array to get the post and user data from all forums' last posts
1892 *
1893 * @event core.sync_modify_forum_data
1894 * @var array forum_data Array with data to update for all forum ids
1895 * @var array post_info Array with some post and user data from the last posts list
1896 * @var array fieldnames Array with the partial column names that are being updated
1897 * @since 3.3.5-RC1
1898 */
1899 $vars = [
1900 'forum_data',
1901 'post_info',
1902 'fieldnames',
1903 ];
1904 extract($phpbb_dispatcher->trigger_event('core.sync_modify_forum_data', compact($vars)));
1905 unset($post_info);
1906
1907 foreach ($forum_data as $forum_id => $row)
1908 {
1909 $sql_ary = array();
1910
1911 foreach ($fieldnames as $fieldname)
1912 {
1913 if ($row['forum_' . $fieldname] != $row[$fieldname])
1914 {
1915 if (preg_match('#(name|colour|subject)$#', $fieldname))
1916 {
1917 $sql_ary['forum_' . $fieldname] = (string) $row[$fieldname];
1918 }
1919 else
1920 {
1921 $sql_ary['forum_' . $fieldname] = (int) $row[$fieldname];
1922 }
1923 }
1924 }
1925
1926 if (count($sql_ary))
1927 {
1928 $sql = 'UPDATE ' . FORUMS_TABLE . '
1929 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
1930 WHERE forum_id = ' . $forum_id;
1931 $db->sql_query($sql);
1932 }
1933 }
1934
1935 $db->sql_transaction('commit');
1936 break;
1937
1938 case 'topic':
1939 $topic_data = $post_ids = $resync_forums = $delete_topics = $delete_posts = $moved_topics = array();
1940
1941 $db->sql_transaction('begin');
1942
1943 $sql = 'SELECT t.topic_id, t.forum_id, t.topic_moved_id, t.topic_visibility, ' . (($sync_extra) ? 't.topic_attachment, t.topic_reported, ' : '') . 't.topic_poster, t.topic_time, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_first_post_id, t.topic_first_poster_name, t.topic_first_poster_colour, t.topic_last_post_id, t.topic_last_post_subject, t.topic_last_poster_id, t.topic_last_poster_name, t.topic_last_poster_colour, t.topic_last_post_time
1944 FROM ' . TOPICS_TABLE . " t
1945 $where_sql";
1946 $result = $db->sql_query($sql);
1947
1948 while ($row = $db->sql_fetchrow($result))
1949 {
1950 if ($row['topic_moved_id'])
1951 {
1952 $moved_topics[] = $row['topic_id'];
1953 continue;
1954 }
1955
1956 $topic_id = (int) $row['topic_id'];
1957 $topic_data[$topic_id] = $row;
1958 $topic_data[$topic_id]['visibility'] = ITEM_UNAPPROVED;
1959 $topic_data[$topic_id]['posts_approved'] = 0;
1960 $topic_data[$topic_id]['posts_unapproved'] = 0;
1961 $topic_data[$topic_id]['posts_softdeleted'] = 0;
1962 $topic_data[$topic_id]['first_post_id'] = 0;
1963 $topic_data[$topic_id]['last_post_id'] = 0;
1964 unset($topic_data[$topic_id]['topic_id']);
1965
1966 // This array holds all topic_ids
1967 $delete_topics[$topic_id] = '';
1968
1969 if ($sync_extra)
1970 {
1971 $topic_data[$topic_id]['reported'] = 0;
1972 $topic_data[$topic_id]['attachment'] = 0;
1973 }
1974 }
1975 $db->sql_freeresult($result);
1976
1977 // Use "t" as table alias because of the $where_sql clause
1978 // NOTE: 't.post_visibility' in the GROUP BY is causing a major slowdown.
1979 $sql = 'SELECT t.topic_id, t.post_visibility, COUNT(t.post_id) AS total_posts, MIN(t.post_id) AS first_post_id, MAX(t.post_id) AS last_post_id
1980 FROM ' . POSTS_TABLE . " t
1981 $where_sql
1982 GROUP BY t.topic_id, t.post_visibility";
1983 $result = $db->sql_query($sql);
1984
1985 while ($row = $db->sql_fetchrow($result))
1986 {
1987 $topic_id = (int) $row['topic_id'];
1988
1989 $row['first_post_id'] = (int) $row['first_post_id'];
1990 $row['last_post_id'] = (int) $row['last_post_id'];
1991
1992 if (!isset($topic_data[$topic_id]))
1993 {
1994 // Hey, these posts come from a topic that does not exist
1995 $delete_posts[$topic_id] = '';
1996 }
1997 else
1998 {
1999 // Unset the corresponding entry in $delete_topics
2000 // When we'll be done, only topics with no posts will remain
2001 unset($delete_topics[$topic_id]);
2002
2003 if ($row['post_visibility'] == ITEM_APPROVED)
2004 {
2005 $topic_data[$topic_id]['posts_approved'] = $row['total_posts'];
2006 }
2007 else if ($row['post_visibility'] == ITEM_UNAPPROVED || $row['post_visibility'] == ITEM_REAPPROVE)
2008 {
2009 $topic_data[$topic_id]['posts_unapproved'] = $row['total_posts'];
2010 }
2011 else if ($row['post_visibility'] == ITEM_DELETED)
2012 {
2013 $topic_data[$topic_id]['posts_softdeleted'] = $row['total_posts'];
2014 }
2015
2016 if ($row['post_visibility'] == ITEM_APPROVED)
2017 {
2018 $topic_data[$topic_id]['visibility'] = ITEM_APPROVED;
2019 $topic_data[$topic_id]['first_post_id'] = $row['first_post_id'];
2020 $topic_data[$topic_id]['last_post_id'] = $row['last_post_id'];
2021 }
2022 else if ($topic_data[$topic_id]['visibility'] != ITEM_APPROVED)
2023 {
2024 // If there is no approved post, we take the min/max of the other visibilities
2025 // for the last and first post info, because it is only visible to moderators anyway
2026 $topic_data[$topic_id]['first_post_id'] = (!empty($topic_data[$topic_id]['first_post_id'])) ? min($topic_data[$topic_id]['first_post_id'], $row['first_post_id']) : $row['first_post_id'];
2027 $topic_data[$topic_id]['last_post_id'] = max($topic_data[$topic_id]['last_post_id'], $row['last_post_id']);
2028
2029 if ($topic_data[$topic_id]['visibility'] == ITEM_UNAPPROVED || $topic_data[$topic_id]['visibility'] == ITEM_REAPPROVE)
2030 {
2031 // Soft delete status is stronger than unapproved.
2032 $topic_data[$topic_id]['visibility'] = $row['post_visibility'];
2033 }
2034 }
2035 }
2036 }
2037 $db->sql_freeresult($result);
2038
2039 foreach ($topic_data as $topic_id => $row)
2040 {
2041 $post_ids[] = $row['first_post_id'];
2042 if ($row['first_post_id'] != $row['last_post_id'])
2043 {
2044 $post_ids[] = $row['last_post_id'];
2045 }
2046 }
2047
2048 // Now we delete empty topics and orphan posts
2049 if (count($delete_posts))
2050 {
2051 delete_posts('topic_id', array_keys($delete_posts), false);
2052 unset($delete_posts);
2053 }
2054
2055 if (!count($topic_data))
2056 {
2057 // If we get there, topic ids were invalid or topics did not contain any posts
2058 delete_topics($where_type, $where_ids, true);
2059 return;
2060 }
2061
2062 if (count($delete_topics))
2063 {
2064 $delete_topic_ids = array();
2065 foreach ($delete_topics as $topic_id => $void)
2066 {
2067 unset($topic_data[$topic_id]);
2068 $delete_topic_ids[] = $topic_id;
2069 }
2070
2071 delete_topics('topic_id', $delete_topic_ids, false);
2072 unset($delete_topics, $delete_topic_ids);
2073 }
2074
2075 $sql_ary = array(
2076 'SELECT' => 'p.post_id, p.topic_id, p.post_visibility, p.poster_id, p.post_subject, p.post_username, p.post_time, u.username, u.user_colour',
2077 'FROM' => array(
2078 POSTS_TABLE => 'p',
2079 USERS_TABLE => 'u',
2080 ),
2081 'WHERE' => $db->sql_in_set('p.post_id', $post_ids) . '
2082 AND u.user_id = p.poster_id',
2083 );
2084
2085 $custom_fieldnames = [];
2086 /**
2087 * Event to modify the SQL array to get the post and user data from all topics' last posts
2088 *
2089 * @event core.sync_topic_last_post_info_sql
2090 * @var array sql_ary SQL array with some post and user data from the last posts list
2091 * @var array custom_fieldnames Empty array for custom fieldnames to update the topics_table with
2092 * @since 3.3.5-RC1
2093 */
2094 $vars = [
2095 'sql_ary',
2096 'custom_fieldnames',
2097 ];
2098 extract($phpbb_dispatcher->trigger_event('core.sync_topic_last_post_info_sql', compact($vars)));
2099 $result = $db->sql_query($db->sql_build_query('SELECT', $sql_ary));
2100
2101 while ($row = $db->sql_fetchrow($result))
2102 {
2103 $topic_id = intval($row['topic_id']);
2104
2105 if ($row['post_id'] == $topic_data[$topic_id]['first_post_id'])
2106 {
2107 $topic_data[$topic_id]['time'] = $row['post_time'];
2108 $topic_data[$topic_id]['poster'] = $row['poster_id'];
2109 $topic_data[$topic_id]['first_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username'];
2110 $topic_data[$topic_id]['first_poster_colour'] = $row['user_colour'];
2111 }
2112
2113 if ($row['post_id'] == $topic_data[$topic_id]['last_post_id'])
2114 {
2115 $topic_data[$topic_id]['last_poster_id'] = $row['poster_id'];
2116 $topic_data[$topic_id]['last_post_subject'] = $row['post_subject'];
2117 $topic_data[$topic_id]['last_post_time'] = $row['post_time'];
2118 $topic_data[$topic_id]['last_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username'];
2119 $topic_data[$topic_id]['last_poster_colour'] = $row['user_colour'];
2120 }
2121
2122 /**
2123 * Event to modify the topic_data when syncing topics
2124 *
2125 * @event core.sync_modify_topic_data
2126 * @var array topic_data Array with the topics' data we are syncing
2127 * @var array row Array with some of the current user and post data
2128 * @var int topic_id The current topic_id of $row
2129 * @since 3.3.5-RC1
2130 */
2131 $vars = [
2132 'topic_data',
2133 'row',
2134 'topic_id',
2135 ];
2136 extract($phpbb_dispatcher->trigger_event('core.sync_modify_topic_data', compact($vars)));
2137 }
2138 $db->sql_freeresult($result);
2139
2140 // Make sure shadow topics do link to existing topics
2141 if (count($moved_topics))
2142 {
2143 $delete_topics = array();
2144
2145 $sql = 'SELECT t1.topic_id, t1.topic_moved_id
2146 FROM ' . TOPICS_TABLE . ' t1
2147 LEFT JOIN ' . TOPICS_TABLE . ' t2 ON (t2.topic_id = t1.topic_moved_id)
2148 WHERE ' . $db->sql_in_set('t1.topic_id', $moved_topics) . '
2149 AND t2.topic_id IS NULL';
2150 $result = $db->sql_query($sql);
2151
2152 while ($row = $db->sql_fetchrow($result))
2153 {
2154 $delete_topics[] = $row['topic_id'];
2155 }
2156 $db->sql_freeresult($result);
2157
2158 if (count($delete_topics))
2159 {
2160 delete_topics('topic_id', $delete_topics, false);
2161 }
2162 unset($delete_topics);
2163
2164 // Make sure shadow topics having no last post data being updated (this only rarely happens...)
2165 $sql = 'SELECT topic_id, topic_moved_id, topic_last_post_id, topic_first_post_id
2166 FROM ' . TOPICS_TABLE . '
2167 WHERE ' . $db->sql_in_set('topic_id', $moved_topics) . '
2168 AND topic_last_post_time = 0';
2169 $result = $db->sql_query($sql);
2170
2171 $shadow_topic_data = $post_ids = array();
2172 while ($row = $db->sql_fetchrow($result))
2173 {
2174 $shadow_topic_data[$row['topic_moved_id']] = $row;
2175 $post_ids[] = $row['topic_last_post_id'];
2176 $post_ids[] = $row['topic_first_post_id'];
2177 }
2178 $db->sql_freeresult($result);
2179
2180 $sync_shadow_topics = array();
2181 if (count($post_ids))
2182 {
2183 $sql = 'SELECT p.post_id, p.topic_id, p.post_visibility, p.poster_id, p.post_subject, p.post_username, p.post_time, u.username, u.user_colour
2184 FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u
2185 WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . '
2186 AND u.user_id = p.poster_id';
2187 $result = $db->sql_query($sql);
2188
2189 while ($row = $db->sql_fetchrow($result))
2190 {
2191 $topic_id = (int) $row['topic_id'];
2192
2193 // Ok, there should be a shadow topic. If there isn't, then there's something wrong with the db.
2194 // However, there's not much we can do about it.
2195 if (!empty($shadow_topic_data[$topic_id]))
2196 {
2197 if ($row['post_id'] == $shadow_topic_data[$topic_id]['topic_first_post_id'])
2198 {
2199 $orig_topic_id = $shadow_topic_data[$topic_id]['topic_id'];
2200
2201 if (!isset($sync_shadow_topics[$orig_topic_id]))
2202 {
2203 $sync_shadow_topics[$orig_topic_id] = array();
2204 }
2205
2206 $sync_shadow_topics[$orig_topic_id]['topic_time'] = $row['post_time'];
2207 $sync_shadow_topics[$orig_topic_id]['topic_poster'] = $row['poster_id'];
2208 $sync_shadow_topics[$orig_topic_id]['topic_first_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username'];
2209 $sync_shadow_topics[$orig_topic_id]['topic_first_poster_colour'] = $row['user_colour'];
2210 }
2211
2212 if ($row['post_id'] == $shadow_topic_data[$topic_id]['topic_last_post_id'])
2213 {
2214 $orig_topic_id = $shadow_topic_data[$topic_id]['topic_id'];
2215
2216 if (!isset($sync_shadow_topics[$orig_topic_id]))
2217 {
2218 $sync_shadow_topics[$orig_topic_id] = array();
2219 }
2220
2221 $sync_shadow_topics[$orig_topic_id]['topic_last_poster_id'] = $row['poster_id'];
2222 $sync_shadow_topics[$orig_topic_id]['topic_last_post_subject'] = $row['post_subject'];
2223 $sync_shadow_topics[$orig_topic_id]['topic_last_post_time'] = $row['post_time'];
2224 $sync_shadow_topics[$orig_topic_id]['topic_last_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username'];
2225 $sync_shadow_topics[$orig_topic_id]['topic_last_poster_colour'] = $row['user_colour'];
2226 }
2227 }
2228 }
2229 $db->sql_freeresult($result);
2230
2231 $shadow_topic_data = array();
2232
2233 // Update the information we collected
2234 if (count($sync_shadow_topics))
2235 {
2236 foreach ($sync_shadow_topics as $sync_topic_id => $sql_ary)
2237 {
2238 $sql = 'UPDATE ' . TOPICS_TABLE . '
2239 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
2240 WHERE topic_id = ' . $sync_topic_id;
2241 $db->sql_query($sql);
2242 }
2243 }
2244 }
2245
2246 unset($sync_shadow_topics, $shadow_topic_data);
2247 }
2248
2249 // These are fields that will be synchronised
2250 $fieldnames = array('time', 'visibility', 'posts_approved', 'posts_unapproved', 'posts_softdeleted', 'poster', 'first_post_id', 'first_poster_name', 'first_poster_colour', 'last_post_id', 'last_post_subject', 'last_post_time', 'last_poster_id', 'last_poster_name', 'last_poster_colour');
2251
2252 // Add custom fieldnames
2253 $fieldnames = array_merge($fieldnames, $custom_fieldnames);
2254 unset($custom_fieldnames);
2255
2256 if ($sync_extra)
2257 {
2258 // This routine assumes that post_reported values are correct
2259 // if they are not, use sync('post_reported') first
2260 $sql = 'SELECT t.topic_id, p.post_id
2261 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
2262 $where_sql_and p.topic_id = t.topic_id
2263 AND p.post_reported = 1
2264 GROUP BY t.topic_id, p.post_id";
2265 $result = $db->sql_query($sql);
2266
2267 $fieldnames[] = 'reported';
2268 while ($row = $db->sql_fetchrow($result))
2269 {
2270 $topic_data[intval($row['topic_id'])]['reported'] = 1;
2271 }
2272 $db->sql_freeresult($result);
2273
2274 // This routine assumes that post_attachment values are correct
2275 // if they are not, use sync('post_attachment') first
2276 $sql = 'SELECT t.topic_id, p.post_id
2277 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
2278 $where_sql_and p.topic_id = t.topic_id
2279 AND p.post_attachment = 1
2280 GROUP BY t.topic_id, p.post_id";
2281 $result = $db->sql_query($sql);
2282
2283 $fieldnames[] = 'attachment';
2284 while ($row = $db->sql_fetchrow($result))
2285 {
2286 $topic_data[intval($row['topic_id'])]['attachment'] = 1;
2287 }
2288 $db->sql_freeresult($result);
2289 }
2290
2291 foreach ($topic_data as $topic_id => $row)
2292 {
2293 $sql_ary = array();
2294
2295 foreach ($fieldnames as $fieldname)
2296 {
2297 if (isset($row[$fieldname]) && isset($row['topic_' . $fieldname]) && $row['topic_' . $fieldname] != $row[$fieldname])
2298 {
2299 $sql_ary['topic_' . $fieldname] = $row[$fieldname];
2300 }
2301 }
2302
2303 if (count($sql_ary))
2304 {
2305 $sql = 'UPDATE ' . TOPICS_TABLE . '
2306 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
2307 WHERE topic_id = ' . $topic_id;
2308 $db->sql_query($sql);
2309
2310 $resync_forums[$row['forum_id']] = $row['forum_id'];
2311 }
2312 }
2313 unset($topic_data);
2314
2315 $db->sql_transaction('commit');
2316
2317 // if some topics have been resync'ed then resync parent forums
2318 // except when we're only syncing a range, we don't want to sync forums during
2319 // batch processing.
2320 if ($resync_parents && count($resync_forums) && $where_type != 'range')
2321 {
2322 sync('forum', 'forum_id', array_values($resync_forums), true, true);
2323 }
2324 break;
2325 }
2326
2327 return;
2328 }
2329
2330 /**
2331 * Prune function
2332 */
2333 function prune($forum_id, $prune_mode, $prune_date, $prune_flags = 0, $auto_sync = true, $prune_limit = 0)
2334 {
2335 global $db, $phpbb_dispatcher;
2336
2337 if (!is_array($forum_id))
2338 {
2339 $forum_id = array($forum_id);
2340 }
2341
2342 if (!count($forum_id))
2343 {
2344 return;
2345 }
2346
2347 $sql_and = '';
2348
2349 if (!($prune_flags & FORUM_FLAG_PRUNE_ANNOUNCE))
2350 {
2351 $sql_and .= ' AND topic_type <> ' . POST_ANNOUNCE;
2352 $sql_and .= ' AND topic_type <> ' . POST_GLOBAL;
2353 }
2354
2355 if (!($prune_flags & FORUM_FLAG_PRUNE_STICKY))
2356 {
2357 $sql_and .= ' AND topic_type <> ' . POST_STICKY;
2358 }
2359
2360 if ($prune_mode == 'posted')
2361 {
2362 $sql_and .= " AND topic_last_post_time < $prune_date";
2363 }
2364
2365 if ($prune_mode == 'viewed')
2366 {
2367 $sql_and .= " AND topic_last_view_time < $prune_date";
2368 }
2369
2370 if ($prune_mode == 'shadow')
2371 {
2372 $sql_and .= ' AND topic_status = ' . ITEM_MOVED . " AND topic_last_post_time < $prune_date";
2373 }
2374
2375 /**
2376 * Use this event to modify the SQL that selects topics to be pruned
2377 *
2378 * @event core.prune_sql
2379 * @var string forum_id The forum id
2380 * @var string prune_mode The prune mode
2381 * @var string prune_date The prune date
2382 * @var int prune_flags The prune flags
2383 * @var bool auto_sync Whether or not to perform auto sync
2384 * @var string sql_and SQL text appended to where clause
2385 * @var int prune_limit The prune limit
2386 * @since 3.1.3-RC1
2387 * @changed 3.1.10-RC1 Added prune_limit
2388 */
2389 $vars = array(
2390 'forum_id',
2391 'prune_mode',
2392 'prune_date',
2393 'prune_flags',
2394 'auto_sync',
2395 'sql_and',
2396 'prune_limit',
2397 );
2398 extract($phpbb_dispatcher->trigger_event('core.prune_sql', compact($vars)));
2399
2400 $sql = 'SELECT topic_id
2401 FROM ' . TOPICS_TABLE . '
2402 WHERE ' . $db->sql_in_set('forum_id', $forum_id) . "
2403 AND poll_start = 0
2404 $sql_and";
2405 $result = $db->sql_query_limit($sql, $prune_limit);
2406
2407 $topic_list = array();
2408 while ($row = $db->sql_fetchrow($result))
2409 {
2410 $topic_list[] = $row['topic_id'];
2411 }
2412 $db->sql_freeresult($result);
2413
2414 if ($prune_flags & FORUM_FLAG_PRUNE_POLL)
2415 {
2416 $sql = 'SELECT topic_id
2417 FROM ' . TOPICS_TABLE . '
2418 WHERE ' . $db->sql_in_set('forum_id', $forum_id) . "
2419 AND poll_start > 0
2420 AND poll_last_vote < $prune_date
2421 $sql_and";
2422 $result = $db->sql_query_limit($sql, $prune_limit);
2423
2424 while ($row = $db->sql_fetchrow($result))
2425 {
2426 $topic_list[] = $row['topic_id'];
2427 }
2428 $db->sql_freeresult($result);
2429
2430 $topic_list = array_unique($topic_list);
2431 }
2432
2433 /**
2434 * Perform additional actions before topic deletion via pruning
2435 *
2436 * @event core.prune_delete_before
2437 * @var int[] topic_list The IDs of the topics to be deleted
2438 * @since 3.2.2-RC1
2439 */
2440 $vars = array('topic_list');
2441 extract($phpbb_dispatcher->trigger_event('core.prune_delete_before', compact($vars)));
2442
2443 return delete_topics('topic_id', $topic_list, $auto_sync, false);
2444 }
2445
2446 /**
2447 * Function auto_prune(), this function now relies on passed vars
2448 */
2449 function auto_prune($forum_id, $prune_mode, $prune_flags, $prune_days, $prune_freq, $log_prune = true)
2450 {
2451 global $db, $user, $phpbb_log;
2452
2453 $sql = 'SELECT forum_name
2454 FROM ' . FORUMS_TABLE . "
2455 WHERE forum_id = $forum_id";
2456 $result = $db->sql_query($sql, 3600);
2457 $row = $db->sql_fetchrow($result);
2458 $db->sql_freeresult($result);
2459
2460 if ($row)
2461 {
2462 $prune_date = time() - ($prune_days * 86400);
2463 $next_prune = time() + ($prune_freq * 86400);
2464
2465 $result = prune($forum_id, $prune_mode, $prune_date, $prune_flags, true, 300);
2466
2467 if ($result['topics'] == 0 && $result['posts'] == 0)
2468 {
2469 $column = $prune_mode === 'shadow' ? 'prune_shadow_next' : 'prune_next';
2470
2471 $sql = 'UPDATE ' . FORUMS_TABLE . "
2472 SET $column = $next_prune
2473 WHERE forum_id = $forum_id";
2474 $db->sql_query($sql);
2475 }
2476
2477 if ($log_prune)
2478 {
2479 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_AUTO_PRUNE', false, [$row['forum_name']]);
2480 }
2481 }
2482
2483 return;
2484 }
2485
2486 /**
2487 * Cache moderators. Called whenever permissions are changed
2488 * via admin_permissions. Changes of usernames and group names
2489 * must be carried through for the moderators table.
2490 *
2491 * @param \phpbb\db\driver\driver_interface $db Database connection
2492 * @param \phpbb\cache\driver\driver_interface $cache Cache driver
2493 * @param \phpbb\auth\auth $auth Authentication object
2494 * @return null
2495 */
2496 function phpbb_cache_moderators($db, $cache, $auth)
2497 {
2498 // Remove cached sql results
2499 $cache->destroy('sql', MODERATOR_CACHE_TABLE);
2500
2501 // Clear table
2502 switch ($db->get_sql_layer())
2503 {
2504 case 'sqlite3':
2505 $db->sql_query('DELETE FROM ' . MODERATOR_CACHE_TABLE);
2506 break;
2507
2508 default:
2509 $db->sql_query('TRUNCATE TABLE ' . MODERATOR_CACHE_TABLE);
2510 break;
2511 }
2512
2513 // We add moderators who have forum moderator permissions without an explicit ACL_NEVER setting
2514 $sql_ary = array();
2515
2516 // Grab all users having moderative options...
2517 $hold_ary = $auth->acl_user_raw_data(false, 'm_%', false);
2518
2519 // Add users?
2520 if (!empty($hold_ary))
2521 {
2522 // At least one moderative option warrants a display
2523 $ug_id_ary = array_keys($hold_ary);
2524
2525 // Remove users who have group memberships with DENY moderator permissions
2526 $sql_ary_deny = array(
2527 'SELECT' => 'a.forum_id, ug.user_id, g.group_id',
2528
2529 'FROM' => array(
2530 ACL_OPTIONS_TABLE => 'o',
2531 USER_GROUP_TABLE => 'ug',
2532 GROUPS_TABLE => 'g',
2533 ACL_GROUPS_TABLE => 'a',
2534 ),
2535
2536 'LEFT_JOIN' => array(
2537 array(
2538 'FROM' => array(ACL_ROLES_DATA_TABLE => 'r'),
2539 'ON' => 'a.auth_role_id = r.role_id',
2540 ),
2541 ),
2542
2543 'WHERE' => '(o.auth_option_id = a.auth_option_id OR o.auth_option_id = r.auth_option_id)
2544 AND ((a.auth_setting = ' . ACL_NEVER . ' AND r.auth_setting IS NULL)
2545 OR r.auth_setting = ' . ACL_NEVER . ')
2546 AND a.group_id = ug.group_id
2547 AND g.group_id = ug.group_id
2548 AND NOT (ug.group_leader = 1 AND g.group_skip_auth = 1)
2549 AND ' . $db->sql_in_set('ug.user_id', $ug_id_ary) . "
2550 AND ug.user_pending = 0
2551 AND o.auth_option " . $db->sql_like_expression('m_' . $db->get_any_char()),
2552 );
2553 $sql = $db->sql_build_query('SELECT', $sql_ary_deny);
2554 $result = $db->sql_query($sql);
2555
2556 while ($row = $db->sql_fetchrow($result))
2557 {
2558 if (isset($hold_ary[$row['user_id']][$row['forum_id']]))
2559 {
2560 unset($hold_ary[$row['user_id']][$row['forum_id']]);
2561 }
2562 }
2563 $db->sql_freeresult($result);
2564
2565 if (count($hold_ary))
2566 {
2567 // Get usernames...
2568 $sql = 'SELECT user_id, username
2569 FROM ' . USERS_TABLE . '
2570 WHERE ' . $db->sql_in_set('user_id', array_keys($hold_ary));
2571 $result = $db->sql_query($sql);
2572
2573 $usernames_ary = array();
2574 while ($row = $db->sql_fetchrow($result))
2575 {
2576 $usernames_ary[$row['user_id']] = $row['username'];
2577 }
2578 $db->sql_freeresult($result);
2579
2580 foreach ($hold_ary as $user_id => $forum_id_ary)
2581 {
2582 // Do not continue if user does not exist
2583 if (!isset($usernames_ary[$user_id]))
2584 {
2585 continue;
2586 }
2587
2588 foreach ($forum_id_ary as $forum_id => $auth_ary)
2589 {
2590 $sql_ary[] = array(
2591 'forum_id' => (int) $forum_id,
2592 'user_id' => (int) $user_id,
2593 'username' => (string) $usernames_ary[$user_id],
2594 'group_id' => 0,
2595 'group_name' => ''
2596 );
2597 }
2598 }
2599 }
2600 }
2601
2602 // Now to the groups...
2603 $hold_ary = $auth->acl_group_raw_data(false, 'm_%', false);
2604
2605 if (!empty($hold_ary))
2606 {
2607 $ug_id_ary = array_keys($hold_ary);
2608
2609 // Make sure not hidden or special groups are involved...
2610 $sql = 'SELECT group_name, group_id, group_type
2611 FROM ' . GROUPS_TABLE . '
2612 WHERE ' . $db->sql_in_set('group_id', $ug_id_ary);
2613 $result = $db->sql_query($sql);
2614
2615 $groupnames_ary = array();
2616 while ($row = $db->sql_fetchrow($result))
2617 {
2618 if ($row['group_type'] == GROUP_HIDDEN || $row['group_type'] == GROUP_SPECIAL)
2619 {
2620 unset($hold_ary[$row['group_id']]);
2621 }
2622
2623 $groupnames_ary[$row['group_id']] = $row['group_name'];
2624 }
2625 $db->sql_freeresult($result);
2626
2627 foreach ($hold_ary as $group_id => $forum_id_ary)
2628 {
2629 // If there is no group, we do not assign it...
2630 if (!isset($groupnames_ary[$group_id]))
2631 {
2632 continue;
2633 }
2634
2635 foreach ($forum_id_ary as $forum_id => $auth_ary)
2636 {
2637 $flag = false;
2638 foreach ($auth_ary as $auth_option => $setting)
2639 {
2640 // Make sure at least one ACL_YES option is set...
2641 if ($setting == ACL_YES)
2642 {
2643 $flag = true;
2644 break;
2645 }
2646 }
2647
2648 if (!$flag)
2649 {
2650 continue;
2651 }
2652
2653 $sql_ary[] = array(
2654 'forum_id' => (int) $forum_id,
2655 'user_id' => 0,
2656 'username' => '',
2657 'group_id' => (int) $group_id,
2658 'group_name' => (string) $groupnames_ary[$group_id]
2659 );
2660 }
2661 }
2662 }
2663
2664 $db->sql_multi_insert(MODERATOR_CACHE_TABLE, $sql_ary);
2665 }
2666
2667 /**
2668 * View log
2669 *
2670 * @param string $mode The mode defines which log_type is used and from which log the entry is retrieved
2671 * @param array &$log The result array with the logs
2672 * @param mixed &$log_count If $log_count is set to false, we will skip counting all entries in the database.
2673 * Otherwise an integer with the number of total matching entries is returned.
2674 * @param int $limit Limit the number of entries that are returned
2675 * @param int $offset Offset when fetching the log entries, f.e. when paginating
2676 * @param mixed $forum_id Restrict the log entries to the given forum_id (can also be an array of forum_ids)
2677 * @param int $topic_id Restrict the log entries to the given topic_id
2678 * @param int $user_id Restrict the log entries to the given user_id
2679 * @param int $limit_days Only get log entries newer than the given timestamp
2680 * @param string $sort_by SQL order option, e.g. 'l.log_time DESC'
2681 * @param string $keywords Will only return log entries that have the keywords in log_operation or log_data
2682 *
2683 * @return int Returns the offset of the last valid page, if the specified offset was invalid (too high)
2684 */
2685 function view_log($mode, &$log, &$log_count, $limit = 0, $offset = 0, $forum_id = 0, $topic_id = 0, $user_id = 0, $limit_days = 0, $sort_by = 'l.log_time DESC', $keywords = '')
2686 {
2687 global $phpbb_log;
2688
2689 $count_logs = ($log_count !== false);
2690
2691 $log = $phpbb_log->get_logs($mode, $count_logs, $limit, $offset, $forum_id, $topic_id, $user_id, $limit_days, $sort_by, $keywords);
2692 $log_count = $phpbb_log->get_log_count();
2693
2694 return $phpbb_log->get_valid_offset();
2695 }
2696
2697 /**
2698 * Removes moderators and administrators from foe lists.
2699 *
2700 * @param \phpbb\db\driver\driver_interface $db Database connection
2701 * @param \phpbb\auth\auth $auth Authentication object
2702 * @param array|bool $group_id If an array, remove all members of this group from foe lists, or false to ignore
2703 * @param array|bool $user_id If an array, remove this user from foe lists, or false to ignore
2704 * @return null
2705 */
2706 function phpbb_update_foes($db, $auth, $group_id = false, $user_id = false)
2707 {
2708 // update foes for some user
2709 if (is_array($user_id) && count($user_id))
2710 {
2711 $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
2712 WHERE ' . $db->sql_in_set('zebra_id', $user_id) . '
2713 AND foe = 1';
2714 $db->sql_query($sql);
2715 return;
2716 }
2717
2718 // update foes for some group
2719 if (is_array($group_id) && count($group_id))
2720 {
2721 // Grab group settings...
2722 $sql_ary = array(
2723 'SELECT' => 'a.group_id',
2724
2725 'FROM' => array(
2726 ACL_OPTIONS_TABLE => 'ao',
2727 ACL_GROUPS_TABLE => 'a',
2728 ),
2729
2730 'LEFT_JOIN' => array(
2731 array(
2732 'FROM' => array(ACL_ROLES_DATA_TABLE => 'r'),
2733 'ON' => 'a.auth_role_id = r.role_id',
2734 ),
2735 ),
2736
2737 'WHERE' => '(ao.auth_option_id = a.auth_option_id OR ao.auth_option_id = r.auth_option_id)
2738 AND ' . $db->sql_in_set('a.group_id', $group_id) . "
2739 AND ao.auth_option IN ('a_', 'm_')",
2740
2741 'GROUP_BY' => 'a.group_id',
2742 );
2743 $sql = $db->sql_build_query('SELECT', $sql_ary);
2744 $result = $db->sql_query($sql);
2745
2746 $groups = array();
2747 while ($row = $db->sql_fetchrow($result))
2748 {
2749 $groups[] = (int) $row['group_id'];
2750 }
2751 $db->sql_freeresult($result);
2752
2753 if (!count($groups))
2754 {
2755 return;
2756 }
2757
2758 switch ($db->get_sql_layer())
2759 {
2760 case 'mysqli':
2761 $sql = 'DELETE z.*
2762 FROM ' . ZEBRA_TABLE . ' z, ' . USER_GROUP_TABLE . ' ug
2763 WHERE z.zebra_id = ug.user_id
2764 AND z.foe = 1
2765 AND ' . $db->sql_in_set('ug.group_id', $groups);
2766 $db->sql_query($sql);
2767 break;
2768
2769 default:
2770 $sql = 'SELECT user_id
2771 FROM ' . USER_GROUP_TABLE . '
2772 WHERE ' . $db->sql_in_set('group_id', $groups);
2773 $result = $db->sql_query($sql);
2774
2775 $users = array();
2776 while ($row = $db->sql_fetchrow($result))
2777 {
2778 $users[] = (int) $row['user_id'];
2779 }
2780 $db->sql_freeresult($result);
2781
2782 if (count($users))
2783 {
2784 $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
2785 WHERE ' . $db->sql_in_set('zebra_id', $users) . '
2786 AND foe = 1';
2787 $db->sql_query($sql);
2788 }
2789 break;
2790 }
2791
2792 return;
2793 }
2794
2795 // update foes for everyone
2796 $perms = array();
2797 foreach ($auth->acl_get_list(false, array('a_', 'm_'), false) as $forum_id => $forum_ary)
2798 {
2799 foreach ($forum_ary as $auth_option => $user_ary)
2800 {
2801 $perms = array_merge($perms, $user_ary);
2802 }
2803 }
2804
2805 if (count($perms))
2806 {
2807 $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
2808 WHERE ' . $db->sql_in_set('zebra_id', array_unique($perms)) . '
2809 AND foe = 1';
2810 $db->sql_query($sql);
2811 }
2812 unset($perms);
2813 }
2814
2815 /**
2816 * Lists inactive users
2817 */
2818 function view_inactive_users(&$users, &$user_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'user_inactive_time DESC')
2819 {
2820 global $db, $user;
2821
2822 $sql = 'SELECT COUNT(user_id) AS user_count
2823 FROM ' . USERS_TABLE . '
2824 WHERE user_type = ' . USER_INACTIVE .
2825 (($limit_days) ? " AND user_inactive_time >= $limit_days" : '');
2826 $result = $db->sql_query($sql);
2827 $user_count = (int) $db->sql_fetchfield('user_count');
2828 $db->sql_freeresult($result);
2829
2830 if ($user_count == 0)
2831 {
2832 // Save the queries, because there are no users to display
2833 return 0;
2834 }
2835
2836 if ($offset >= $user_count)
2837 {
2838 $offset = ($offset - $limit < 0) ? 0 : $offset - $limit;
2839 }
2840
2841 $sql = 'SELECT *
2842 FROM ' . USERS_TABLE . '
2843 WHERE user_type = ' . USER_INACTIVE .
2844 (($limit_days) ? " AND user_inactive_time >= $limit_days" : '') . "
2845 ORDER BY $sort_by";
2846 $result = $db->sql_query_limit($sql, $limit, $offset);
2847
2848 while ($row = $db->sql_fetchrow($result))
2849 {
2850 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_UNKNOWN'];
2851 switch ($row['user_inactive_reason'])
2852 {
2853 case INACTIVE_REGISTER:
2854 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_REGISTER'];
2855 break;
2856
2857 case INACTIVE_PROFILE:
2858 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_PROFILE'];
2859 break;
2860
2861 case INACTIVE_MANUAL:
2862 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_MANUAL'];
2863 break;
2864
2865 case INACTIVE_REMIND:
2866 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_REMIND'];
2867 break;
2868 }
2869
2870 $users[] = $row;
2871 }
2872 $db->sql_freeresult($result);
2873
2874 return $offset;
2875 }
2876
2877 /**
2878 * Lists warned users
2879 */
2880 function view_warned_users(&$users, &$user_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'user_warnings DESC')
2881 {
2882 global $db;
2883
2884 $sql = 'SELECT user_id, username, user_colour, user_warnings, user_last_warning
2885 FROM ' . USERS_TABLE . '
2886 WHERE user_warnings > 0
2887 ' . (($limit_days) ? "AND user_last_warning >= $limit_days" : '') . "
2888 ORDER BY $sort_by";
2889 $result = $db->sql_query_limit($sql, $limit, $offset);
2890 $users = $db->sql_fetchrowset($result);
2891 $db->sql_freeresult($result);
2892
2893 $sql = 'SELECT count(user_id) AS user_count
2894 FROM ' . USERS_TABLE . '
2895 WHERE user_warnings > 0
2896 ' . (($limit_days) ? "AND user_last_warning >= $limit_days" : '');
2897 $result = $db->sql_query($sql);
2898 $user_count = (int) $db->sql_fetchfield('user_count');
2899 $db->sql_freeresult($result);
2900
2901 return;
2902 }
2903
2904 /**
2905 * Get database size
2906 */
2907 function get_database_size()
2908 {
2909 global $db, $user;
2910
2911 $database_size = false;
2912
2913 switch ($db->get_sql_layer())
2914 {
2915 case 'mysqli':
2916 $mysql_engine = ['MyISAM', 'InnoDB', 'Aria'];
2917 $db_name = $db->get_db_name();
2918 $database_size = 0;
2919
2920 $sql = 'SHOW TABLE STATUS
2921 FROM ' . $db->sql_quote($db_name);
2922 $result = $db->sql_query($sql, 7200);
2923
2924 while ($row = $db->sql_fetchrow($result))
2925 {
2926 if (isset($row['Engine']) && in_array($row['Engine'], $mysql_engine))
2927 {
2928 $database_size += $row['Data_length'] + $row['Index_length'];
2929 }
2930 }
2931
2932 $db->sql_freeresult($result);
2933
2934 $database_size = $database_size ? $database_size : false;
2935
2936 break;
2937
2938 case 'sqlite3':
2939 global $dbhost;
2940
2941 if (file_exists($dbhost))
2942 {
2943 $database_size = filesize($dbhost);
2944 }
2945
2946 break;
2947
2948 case 'mssql_odbc':
2949 case 'mssqlnative':
2950 $sql = 'SELECT @@VERSION AS mssql_version';
2951 $result = $db->sql_query($sql);
2952 $row = $db->sql_fetchrow($result);
2953 $db->sql_freeresult($result);
2954
2955 $sql = 'SELECT ((SUM(size) * 8.0) * 1024.0) as dbsize
2956 FROM sysfiles';
2957
2958 if ($row)
2959 {
2960 // Azure stats are stored elsewhere
2961 if (strpos($row['mssql_version'], 'SQL Azure') !== false)
2962 {
2963 $sql = 'SELECT ((SUM(reserved_page_count) * 8.0) * 1024.0) as dbsize
2964 FROM sys.dm_db_partition_stats';
2965 }
2966 }
2967
2968 $result = $db->sql_query($sql, 7200);
2969 $database_size = ($row = $db->sql_fetchrow($result)) ? $row['dbsize'] : false;
2970 $db->sql_freeresult($result);
2971 break;
2972
2973 case 'postgres':
2974 $database = $db->get_db_name();
2975
2976 if (strpos($database, '.') !== false)
2977 {
2978 $database = explode('.', $database)[0];
2979 }
2980
2981 $sql = "SELECT pg_database_size('" . $database . "') AS dbsize";
2982 $result = $db->sql_query($sql, 7200);
2983 $row = $db->sql_fetchrow($result);
2984 $database_size = !empty($row['dbsize']) ? $row['dbsize'] : false;
2985 $db->sql_freeresult($result);
2986 break;
2987
2988 case 'oracle':
2989 $sql = 'SELECT SUM(bytes) as dbsize
2990 FROM user_segments';
2991 $result = $db->sql_query($sql, 7200);
2992 $database_size = ($row = $db->sql_fetchrow($result)) ? $row['dbsize'] : false;
2993 $db->sql_freeresult($result);
2994 break;
2995 }
2996
2997 $database_size = ($database_size !== false) ? get_formatted_filesize($database_size) : $user->lang['NOT_AVAILABLE'];
2998
2999 return $database_size;
3000 }
3001
3002 /*
3003 * Tidy Warnings
3004 * Remove all warnings which have now expired from the database
3005 * The duration of a warning can be defined by the administrator
3006 * This only removes the warning and reduces the associated count,
3007 * it does not remove the user note recording the contents of the warning
3008 */
3009 function tidy_warnings()
3010 {
3011 global $db, $config;
3012
3013 $expire_date = time() - ($config['warnings_expire_days'] * 86400);
3014 $warning_list = $user_list = array();
3015
3016 $sql = 'SELECT * FROM ' . WARNINGS_TABLE . "
3017 WHERE warning_time < $expire_date";
3018 $result = $db->sql_query($sql);
3019
3020 while ($row = $db->sql_fetchrow($result))
3021 {
3022 $warning_list[] = $row['warning_id'];
3023 $user_list[$row['user_id']] = isset($user_list[$row['user_id']]) ? ++$user_list[$row['user_id']] : 1;
3024 }
3025 $db->sql_freeresult($result);
3026
3027 if (count($warning_list))
3028 {
3029 $db->sql_transaction('begin');
3030
3031 $sql = 'DELETE FROM ' . WARNINGS_TABLE . '
3032 WHERE ' . $db->sql_in_set('warning_id', $warning_list);
3033 $db->sql_query($sql);
3034
3035 foreach ($user_list as $user_id => $value)
3036 {
3037 $sql = 'UPDATE ' . USERS_TABLE . " SET user_warnings = user_warnings - $value
3038 WHERE user_id = $user_id";
3039 $db->sql_query($sql);
3040 }
3041
3042 $db->sql_transaction('commit');
3043 }
3044
3045 $config->set('warnings_last_gc', time(), false);
3046 }
3047
3048 /**
3049 * Tidy database, doing some maintanance tasks
3050 */
3051 function tidy_database()
3052 {
3053 global $config, $db;
3054
3055 // Here we check permission consistency
3056
3057 // Sometimes, it can happen permission tables having forums listed which do not exist
3058 $sql = 'SELECT forum_id
3059 FROM ' . FORUMS_TABLE;
3060 $result = $db->sql_query($sql);
3061
3062 $forum_ids = array(0);
3063 while ($row = $db->sql_fetchrow($result))
3064 {
3065 $forum_ids[] = $row['forum_id'];
3066 }
3067 $db->sql_freeresult($result);
3068
3069 $db->sql_transaction('begin');
3070
3071 // Delete those rows from the acl tables not having listed the forums above
3072 $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
3073 WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true);
3074 $db->sql_query($sql);
3075
3076 $sql = 'DELETE FROM ' . ACL_USERS_TABLE . '
3077 WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true);
3078 $db->sql_query($sql);
3079
3080 $db->sql_transaction('commit');
3081
3082 $config->set('database_last_gc', time(), false);
3083 }
3084
3085 /**
3086 * Add permission language - this will make sure custom files will be included
3087 */
3088 function add_permission_language()
3089 {
3090 global $user, $phpEx, $phpbb_extension_manager;
3091
3092 // add permission language files from extensions
3093 $finder = $phpbb_extension_manager->get_finder();
3094
3095 $lang_files = $finder
3096 ->prefix('permissions_')
3097 ->suffix(".$phpEx")
3098 ->core_path('language/')
3099 ->extension_directory('/language')
3100 ->find();
3101
3102 foreach ($lang_files as $lang_file => $ext_name)
3103 {
3104 if ($ext_name === '/')
3105 {
3106 $user->add_lang($lang_file);
3107 }
3108 else
3109 {
3110 $user->add_lang_ext($ext_name, $lang_file);
3111 }
3112 }
3113 }
3114
3115 /**
3116 * Enables a particular flag in a bitfield column of a given table.
3117 *
3118 * @param string $table_name The table to update
3119 * @param string $column_name The column containing a bitfield to update
3120 * @param int $flag The binary flag which is OR-ed with the current column value
3121 * @param string $sql_more This string is attached to the sql query generated to update the table.
3122 *
3123 * @return null
3124 */
3125 function enable_bitfield_column_flag($table_name, $column_name, $flag, $sql_more = '')
3126 {
3127 global $db;
3128
3129 $sql = 'UPDATE ' . $table_name . '
3130 SET ' . $column_name . ' = ' . $db->sql_bit_or($column_name, $flag) . '
3131 ' . $sql_more;
3132 $db->sql_query($sql);
3133 }
3134
3135 function display_ban_end_options()
3136 {
3137 global $user, $template;
3138
3139 // Ban length options
3140 $ban_end_text = array(0 => $user->lang['PERMANENT'], 30 => $user->lang['30_MINS'], 60 => $user->lang['1_HOUR'], 360 => $user->lang['6_HOURS'], 1440 => $user->lang['1_DAY'], 10080 => $user->lang['7_DAYS'], 20160 => $user->lang['2_WEEKS'], 40320 => $user->lang['1_MONTH'], -1 => $user->lang['UNTIL'] . ' -> ');
3141
3142 $ban_end_options = '';
3143 foreach ($ban_end_text as $length => $text)
3144 {
3145 $ban_end_options .= '<option value="' . $length . '">' . $text . '</option>';
3146 }
3147
3148 $template->assign_vars(array(
3149 'S_BAN_END_OPTIONS' => $ban_end_options
3150 ));
3151 }
3152
3153 /**
3154 * Display ban options
3155 */
3156 function display_ban_options($mode)
3157 {
3158 global $user, $db, $template;
3159
3160 switch ($mode)
3161 {
3162 case 'user':
3163
3164 $field = 'username';
3165
3166 $sql = 'SELECT b.*, u.user_id, u.username, u.username_clean
3167 FROM ' . BANLIST_TABLE . ' b, ' . USERS_TABLE . ' u
3168 WHERE (b.ban_end >= ' . time() . '
3169 OR b.ban_end = 0)
3170 AND u.user_id = b.ban_userid
3171 ORDER BY u.username_clean ASC';
3172 break;
3173
3174 case 'ip':
3175
3176 $field = 'ban_ip';
3177
3178 $sql = 'SELECT *
3179 FROM ' . BANLIST_TABLE . '
3180 WHERE (ban_end >= ' . time() . "
3181 OR ban_end = 0)
3182 AND ban_ip <> ''
3183 ORDER BY ban_ip";
3184 break;
3185
3186 case 'email':
3187
3188 $field = 'ban_email';
3189
3190 $sql = 'SELECT *
3191 FROM ' . BANLIST_TABLE . '
3192 WHERE (ban_end >= ' . time() . "
3193 OR ban_end = 0)
3194 AND ban_email <> ''
3195 ORDER BY ban_email";
3196 break;
3197 }
3198 $result = $db->sql_query($sql);
3199
3200 $banned_options = $excluded_options = array();
3201 while ($row = $db->sql_fetchrow($result))
3202 {
3203 $option = '<option value="' . $row['ban_id'] . '">' . $row[$field] . '</option>';
3204
3205 if ($row['ban_exclude'])
3206 {
3207 $excluded_options[] = $option;
3208 }
3209 else
3210 {
3211 $banned_options[] = $option;
3212 }
3213
3214 $time_length = ($row['ban_end']) ? ($row['ban_end'] - $row['ban_start']) / 60 : 0;
3215
3216 if ($time_length == 0)
3217 {
3218 // Banned permanently
3219 $ban_length = $user->lang['PERMANENT'];
3220 }
3221 else if (isset($ban_end_text[$time_length]))
3222 {
3223 // Banned for a given duration
3224 $ban_length = $user->lang('BANNED_UNTIL_DURATION', $ban_end_text[$time_length], $user->format_date($row['ban_end'], false, true));
3225 }
3226 else
3227 {
3228 // Banned until given date
3229 $ban_length = $user->lang('BANNED_UNTIL_DATE', $user->format_date($row['ban_end'], false, true));
3230 }
3231
3232 $template->assign_block_vars('bans', array(
3233 'BAN_ID' => (int) $row['ban_id'],
3234 'LENGTH' => $ban_length,
3235 'A_LENGTH' => addslashes($ban_length),
3236 'REASON' => $row['ban_reason'],
3237 'A_REASON' => addslashes($row['ban_reason']),
3238 'GIVE_REASON' => $row['ban_give_reason'],
3239 'A_GIVE_REASON' => addslashes($row['ban_give_reason']),
3240 ));
3241 }
3242 $db->sql_freeresult($result);
3243
3244 $options = '';
3245 if ($excluded_options)
3246 {
3247 $options .= '<optgroup label="' . $user->lang['OPTIONS_EXCLUDED'] . '">';
3248 $options .= implode('', $excluded_options);
3249 $options .= '</optgroup>';
3250 }
3251
3252 if ($banned_options)
3253 {
3254 $options .= '<optgroup label="' . $user->lang['OPTIONS_BANNED'] . '">';
3255 $options .= implode('', $banned_options);
3256 $options .= '</optgroup>';
3257 }
3258
3259 $template->assign_vars(array(
3260 'S_BANNED_OPTIONS' => ($banned_options || $excluded_options) ? true : false,
3261 'BANNED_OPTIONS' => $options,
3262 ));
3263 }
3264