[ Index ]

PHP Cross Reference of phpBB-3.3.11-deutsch

title

Body

[close]

/includes/ -> functions_admin.php (source)

   1  <?php
   2  /**
   3  *
   4  * This file is part of the phpBB Forum Software package.
   5  *
   6  * @copyright (c) phpBB Limited <https://www.phpbb.com>
   7  * @license GNU General Public License, version 2 (GPL-2.0)
   8  *
   9  * For full copyright and license information, please see
  10  * the docs/CREDITS.txt file.
  11  *
  12  */
  13  
  14  /**
  15  * @ignore
  16  */
  17  if (!defined('IN_PHPBB'))
  18  {
  19      exit;
  20  }
  21  
  22  /**
  23  * Recalculate Nested Sets
  24  *
  25  * @param int    $new_id    first left_id (should start with 1)
  26  * @param string    $pkey    primary key-column (containing the id for the parent_id of the children)
  27  * @param string    $table    constant or fullname of the table
  28  * @param int    $parent_id parent_id of the current set (default = 0)
  29  * @param array    $where    contains strings to compare closer on the where statement (additional)
  30  */
  31  function recalc_nested_sets(&$new_id, $pkey, $table, $parent_id = 0, $where = array())
  32  {
  33      global $db;
  34  
  35      $sql = 'SELECT *
  36          FROM ' . $table . '
  37          WHERE parent_id = ' . (int) $parent_id .
  38          ((!empty($where)) ? ' AND ' . implode(' AND ', $where) : '') . '
  39          ORDER BY left_id ASC';
  40      $result = $db->sql_query($sql);
  41      while ($row = $db->sql_fetchrow($result))
  42      {
  43          // First we update the left_id for this module
  44          if ($row['left_id'] != $new_id)
  45          {
  46              $db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('left_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}");
  47          }
  48          $new_id++;
  49  
  50          // Then we go through any children and update their left/right id's
  51          recalc_nested_sets($new_id, $pkey, $table, $row[$pkey], $where);
  52  
  53          // Then we come back and update the right_id for this module
  54          if ($row['right_id'] != $new_id)
  55          {
  56              $db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('right_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}");
  57          }
  58          $new_id++;
  59      }
  60      $db->sql_freeresult($result);
  61  }
  62  
  63  /**
  64  * Simple version of jumpbox, just lists authed forums
  65  */
  66  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)
  67  {
  68      global $db, $auth, $phpbb_dispatcher;
  69  
  70      // This query is identical to the jumpbox one
  71      $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, forum_flags, forum_options, left_id, right_id
  72          FROM ' . FORUMS_TABLE . '
  73          ORDER BY left_id ASC';
  74      $result = $db->sql_query($sql, 600);
  75  
  76      $rowset = array();
  77      while ($row = $db->sql_fetchrow($result))
  78      {
  79          $rowset[(int) $row['forum_id']] = $row;
  80      }
  81      $db->sql_freeresult($result);
  82  
  83      $right = 0;
  84      $padding_store = array('0' => '');
  85      $padding = '';
  86      $forum_list = ($return_array) ? array() : '';
  87  
  88      /**
  89      * Modify the forum list data
  90      *
  91      * @event core.make_forum_select_modify_forum_list
  92      * @var    array    rowset    Array with the forums list data
  93      * @since 3.1.10-RC1
  94      */
  95      $vars = array('rowset');
  96      extract($phpbb_dispatcher->trigger_event('core.make_forum_select_modify_forum_list', compact($vars)));
  97  
  98      // Sometimes it could happen that forums will be displayed here not be displayed within the index page
  99      // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
 100      // If this happens, the padding could be "broken"
 101  
 102      foreach ($rowset as $row)
 103      {
 104          if ($row['left_id'] < $right)
 105          {
 106              $padding .= '&nbsp; &nbsp;';
 107              $padding_store[$row['parent_id']] = $padding;
 108          }
 109          else if ($row['left_id'] > $right + 1)
 110          {
 111              $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : '';
 112          }
 113  
 114          $right = $row['right_id'];
 115          $disabled = false;
 116  
 117          if (!$ignore_acl && $auth->acl_gets(array('f_list', 'a_forum', 'a_forumadd', 'a_forumdel'), $row['forum_id']))
 118          {
 119              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'])))
 120              {
 121                  $disabled = true;
 122              }
 123          }
 124          else if (!$ignore_acl)
 125          {
 126              continue;
 127          }
 128  
 129          if (
 130              ((is_array($ignore_id) && in_array($row['forum_id'], $ignore_id)) || $row['forum_id'] == $ignore_id)
 131              ||
 132              // Non-postable forum with no subforums, don't display
 133              ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']) && $ignore_emptycat)
 134              ||
 135              ($row['forum_type'] != FORUM_POST && $ignore_nonpost)
 136              )
 137          {
 138              $disabled = true;
 139          }
 140  
 141          if ($return_array)
 142          {
 143              // Include some more information...
 144              $selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? true : false) : (($row['forum_id'] == $select_id) ? true : false);
 145              $forum_list[$row['forum_id']] = array_merge(array('padding' => $padding, 'selected' => ($selected && !$disabled), 'disabled' => $disabled), $row);
 146          }
 147          else
 148          {
 149              $selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? ' selected="selected"' : '') : (($row['forum_id'] == $select_id) ? ' selected="selected"' : '');
 150              $forum_list .= '<option value="' . $row['forum_id'] . '"' . (($disabled) ? ' disabled="disabled" class="disabled-option"' : $selected) . '>' . $padding . $row['forum_name'] . '</option>';
 151          }
 152      }
 153      unset($padding_store, $rowset);
 154  
 155      return $forum_list;
 156  }
 157  
 158  /**
 159  * Generate size select options
 160  */
 161  function size_select_options($size_compare)
 162  {
 163      global $user;
 164  
 165      $size_types_text = array($user->lang['BYTES'], $user->lang['KIB'], $user->lang['MIB']);
 166      $size_types = array('b', 'kb', 'mb');
 167  
 168      $s_size_options = '';
 169  
 170      for ($i = 0, $size = count($size_types_text); $i < $size; $i++)
 171      {
 172          $selected = ($size_compare == $size_types[$i]) ? ' selected="selected"' : '';
 173          $s_size_options .= '<option value="' . $size_types[$i] . '"' . $selected . '>' . $size_types_text[$i] . '</option>';
 174      }
 175  
 176      return $s_size_options;
 177  }
 178  
 179  /**
 180  * Generate list of groups (option fields without select)
 181  *
 182  * @param int $group_id The default group id to mark as selected
 183  * @param array $exclude_ids The group ids to exclude from the list, false (default) if you whish to exclude no id
 184  * @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.
 185  *
 186  * @return string The list of options.
 187  */
 188  function group_select_options($group_id, $exclude_ids = false, $manage_founder = false)
 189  {
 190      global $db, $config, $phpbb_container;
 191  
 192      /** @var \phpbb\group\helper $group_helper */
 193      $group_helper = $phpbb_container->get('group_helper');
 194  
 195      $exclude_sql = ($exclude_ids !== false && count($exclude_ids)) ? 'WHERE ' . $db->sql_in_set('group_id', array_map('intval', $exclude_ids), true) : '';
 196      $sql_and = (!$config['coppa_enable']) ? (($exclude_sql) ? ' AND ' : ' WHERE ') . "group_name <> 'REGISTERED_COPPA'" : '';
 197      $sql_founder = ($manage_founder !== false) ? (($exclude_sql || $sql_and) ? ' AND ' : ' WHERE ') . 'group_founder_manage = ' . (int) $manage_founder : '';
 198  
 199      $sql = 'SELECT group_id, group_name, group_type
 200          FROM ' . GROUPS_TABLE . "
 201          $exclude_sql
 202          $sql_and
 203          $sql_founder
 204          ORDER BY group_type DESC, group_name ASC";
 205      $result = $db->sql_query($sql);
 206  
 207      $s_group_options = '';
 208      while ($row = $db->sql_fetchrow($result))
 209      {
 210          $selected = ($row['group_id'] == $group_id) ? ' selected="selected"' : '';
 211          $s_group_options .= '<option' . (($row['group_type'] == GROUP_SPECIAL) ? ' class="sep"' : '') . ' value="' . $row['group_id'] . '"' . $selected . '>' . $group_helper->get_name($row['group_name']) . '</option>';
 212      }
 213      $db->sql_freeresult($result);
 214  
 215      return $s_group_options;
 216  }
 217  
 218  /**
 219  * Obtain authed forums list
 220  */
 221  function get_forum_list($acl_list = 'f_list', $id_only = true, $postable_only = false, $no_cache = false)
 222  {
 223      global $db, $auth, $phpbb_dispatcher;
 224      static $forum_rows;
 225  
 226      if (!isset($forum_rows))
 227      {
 228          // This query is identical to the jumpbox one
 229          $expire_time = ($no_cache) ? 0 : 600;
 230  
 231          $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id
 232              FROM ' . FORUMS_TABLE . '
 233              ORDER BY left_id ASC';
 234          $result = $db->sql_query($sql, $expire_time);
 235  
 236          $forum_rows = array();
 237  
 238          $right = $padding = 0;
 239          $padding_store = array('0' => 0);
 240  
 241          while ($row = $db->sql_fetchrow($result))
 242          {
 243              if ($row['left_id'] < $right)
 244              {
 245                  $padding++;
 246                  $padding_store[$row['parent_id']] = $padding;
 247              }
 248              else if ($row['left_id'] > $right + 1)
 249              {
 250                  // Ok, if the $padding_store for this parent is empty there is something wrong. For now we will skip over it.
 251                  // @todo digging deep to find out "how" this can happen.
 252                  $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : $padding;
 253              }
 254  
 255              $right = $row['right_id'];
 256              $row['padding'] = $padding;
 257  
 258              $forum_rows[] = $row;
 259          }
 260          $db->sql_freeresult($result);
 261          unset($padding_store);
 262      }
 263  
 264      $rowset = array();
 265      foreach ($forum_rows as $row)
 266      {
 267          if ($postable_only && $row['forum_type'] != FORUM_POST)
 268          {
 269              continue;
 270          }
 271  
 272          if ($acl_list == '' || ($acl_list != '' && $auth->acl_gets($acl_list, $row['forum_id'])))
 273          {
 274              $rowset[] = ($id_only) ? (int) $row['forum_id'] : $row;
 275          }
 276      }
 277  
 278      /**
 279      * Modify the forum list data
 280      *
 281      * @event core.get_forum_list_modify_data
 282      * @var    array    rowset    Array with the forum list data
 283      * @since 3.1.10-RC1
 284      */
 285      $vars = array('rowset');
 286      extract($phpbb_dispatcher->trigger_event('core.get_forum_list_modify_data', compact($vars)));
 287  
 288      return $rowset;
 289  }
 290  
 291  /**
 292  * Get forum branch
 293  */
 294  function get_forum_branch($forum_id, $type = 'all', $order = 'descending', $include_forum = true)
 295  {
 296      global $db;
 297  
 298      switch ($type)
 299      {
 300          case 'parents':
 301              $condition = 'f1.left_id BETWEEN f2.left_id AND f2.right_id';
 302          break;
 303  
 304          case 'children':
 305              $condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id';
 306          break;
 307  
 308          default:
 309              $condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id OR f1.left_id BETWEEN f2.left_id AND f2.right_id';
 310          break;
 311      }
 312  
 313      $rows = array();
 314  
 315      $sql = 'SELECT f2.*
 316          FROM ' . FORUMS_TABLE . ' f1
 317          LEFT JOIN ' . FORUMS_TABLE . " f2 ON ($condition)
 318          WHERE f1.forum_id = $forum_id
 319          ORDER BY f2.left_id " . (($order == 'descending') ? 'ASC' : 'DESC');
 320      $result = $db->sql_query($sql);
 321  
 322      while ($row = $db->sql_fetchrow($result))
 323      {
 324          if (!$include_forum && $row['forum_id'] == $forum_id)
 325          {
 326              continue;
 327          }
 328  
 329          $rows[] = $row;
 330      }
 331      $db->sql_freeresult($result);
 332  
 333      return $rows;
 334  }
 335  
 336  /**
 337  * Copies permissions from one forum to others
 338  *
 339  * @param int    $src_forum_id        The source forum we want to copy permissions from
 340  * @param array    $dest_forum_ids        The destination forum(s) we want to copy to
 341  * @param bool    $clear_dest_perms    True if destination permissions should be deleted
 342  * @param bool    $add_log            True if log entry should be added
 343  *
 344  * @return bool                        False on error
 345  */
 346  function copy_forum_permissions($src_forum_id, $dest_forum_ids, $clear_dest_perms = true, $add_log = true)
 347  {
 348      global $db, $user, $phpbb_log;
 349  
 350      // Only one forum id specified
 351      if (!is_array($dest_forum_ids))
 352      {
 353          $dest_forum_ids = array($dest_forum_ids);
 354      }
 355  
 356      // Make sure forum ids are integers
 357      $src_forum_id = (int) $src_forum_id;
 358      $dest_forum_ids = array_map('intval', $dest_forum_ids);
 359  
 360      // No source forum or no destination forums specified
 361      if (empty($src_forum_id) || empty($dest_forum_ids))
 362      {
 363          return false;
 364      }
 365  
 366      // Check if source forum exists
 367      $sql = 'SELECT forum_name
 368          FROM ' . FORUMS_TABLE . '
 369          WHERE forum_id = ' . $src_forum_id;
 370      $result = $db->sql_query($sql);
 371      $src_forum_name = $db->sql_fetchfield('forum_name');
 372      $db->sql_freeresult($result);
 373  
 374      // Source forum doesn't exist
 375      if (empty($src_forum_name))
 376      {
 377          return false;
 378      }
 379  
 380      // Check if destination forums exists
 381      $sql = 'SELECT forum_id, forum_name
 382          FROM ' . FORUMS_TABLE . '
 383          WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
 384      $result = $db->sql_query($sql);
 385  
 386      $dest_forum_ids = $dest_forum_names = array();
 387      while ($row = $db->sql_fetchrow($result))
 388      {
 389          $dest_forum_ids[]    = (int) $row['forum_id'];
 390          $dest_forum_names[]    = $row['forum_name'];
 391      }
 392      $db->sql_freeresult($result);
 393  
 394      // No destination forum exists
 395      if (empty($dest_forum_ids))
 396      {
 397          return false;
 398      }
 399  
 400      // From the mysql documentation:
 401      // Prior to MySQL 4.0.14, the target table of the INSERT statement cannot appear
 402      // in the FROM clause of the SELECT part of the query. This limitation is lifted in 4.0.14.
 403      // Due to this we stay on the safe side if we do the insertion "the manual way"
 404  
 405      // Rowsets we're going to insert
 406      $users_sql_ary = $groups_sql_ary = array();
 407  
 408      // Query acl users table for source forum data
 409      $sql = 'SELECT user_id, auth_option_id, auth_role_id, auth_setting
 410          FROM ' . ACL_USERS_TABLE . '
 411          WHERE forum_id = ' . $src_forum_id;
 412      $result = $db->sql_query($sql);
 413  
 414      while ($row = $db->sql_fetchrow($result))
 415      {
 416          $row = array(
 417              'user_id'            => (int) $row['user_id'],
 418              'auth_option_id'    => (int) $row['auth_option_id'],
 419              'auth_role_id'        => (int) $row['auth_role_id'],
 420              'auth_setting'        => (int) $row['auth_setting'],
 421          );
 422  
 423          foreach ($dest_forum_ids as $dest_forum_id)
 424          {
 425              $users_sql_ary[] = $row + array('forum_id' => $dest_forum_id);
 426          }
 427      }
 428      $db->sql_freeresult($result);
 429  
 430      // Query acl groups table for source forum data
 431      $sql = 'SELECT group_id, auth_option_id, auth_role_id, auth_setting
 432          FROM ' . ACL_GROUPS_TABLE . '
 433          WHERE forum_id = ' . $src_forum_id;
 434      $result = $db->sql_query($sql);
 435  
 436      while ($row = $db->sql_fetchrow($result))
 437      {
 438          $row = array(
 439              'group_id'            => (int) $row['group_id'],
 440              'auth_option_id'    => (int) $row['auth_option_id'],
 441              'auth_role_id'        => (int) $row['auth_role_id'],
 442              'auth_setting'        => (int) $row['auth_setting'],
 443          );
 444  
 445          foreach ($dest_forum_ids as $dest_forum_id)
 446          {
 447              $groups_sql_ary[] = $row + array('forum_id' => $dest_forum_id);
 448          }
 449      }
 450      $db->sql_freeresult($result);
 451  
 452      $db->sql_transaction('begin');
 453  
 454      // Clear current permissions of destination forums
 455      if ($clear_dest_perms)
 456      {
 457          $sql = 'DELETE FROM ' . ACL_USERS_TABLE . '
 458              WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
 459          $db->sql_query($sql);
 460  
 461          $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
 462              WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
 463          $db->sql_query($sql);
 464      }
 465  
 466      $db->sql_multi_insert(ACL_USERS_TABLE, $users_sql_ary);
 467      $db->sql_multi_insert(ACL_GROUPS_TABLE, $groups_sql_ary);
 468  
 469      if ($add_log)
 470      {
 471          $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_FORUM_COPIED_PERMISSIONS', false, array($src_forum_name, implode(', ', $dest_forum_names)));
 472      }
 473  
 474      $db->sql_transaction('commit');
 475  
 476      return true;
 477  }
 478  
 479  /**
 480  * Get physical file listing
 481  */
 482  function filelist($rootdir, $dir = '', $type = 'gif|jpg|jpeg|png|svg|webp')
 483  {
 484      $matches = array($dir => array());
 485  
 486      // Remove initial / if present
 487      $rootdir = (substr($rootdir, 0, 1) == '/') ? substr($rootdir, 1) : $rootdir;
 488      // Add closing / if not present
 489      $rootdir = ($rootdir && substr($rootdir, -1) != '/') ? $rootdir . '/' : $rootdir;
 490  
 491      // Remove initial / if present
 492      $dir = (substr($dir, 0, 1) == '/') ? substr($dir, 1) : $dir;
 493      // Add closing / if not present
 494      $dir = ($dir && substr($dir, -1) != '/') ? $dir . '/' : $dir;
 495  
 496      if (!is_dir($rootdir . $dir))
 497      {
 498          return $matches;
 499      }
 500  
 501      $dh = @opendir($rootdir . $dir);
 502  
 503      if (!$dh)
 504      {
 505          return $matches;
 506      }
 507  
 508      while (($fname = readdir($dh)) !== false)
 509      {
 510          if (is_file("$rootdir$dir$fname"))
 511          {
 512              if (filesize("$rootdir$dir$fname") && preg_match('#\.' . $type . '$#i', $fname))
 513              {
 514                  $matches[$dir][] = $fname;
 515              }
 516          }
 517          else if ($fname[0] != '.' && is_dir("$rootdir$dir$fname"))
 518          {
 519              $matches += filelist($rootdir, "$dir$fname", $type);
 520          }
 521      }
 522      closedir($dh);
 523  
 524      return $matches;
 525  }
 526  
 527  /**
 528  * Move topic(s)
 529  */
 530  function move_topics($topic_ids, $forum_id, $auto_sync = true)
 531  {
 532      global $db, $phpbb_dispatcher;
 533  
 534      if (empty($topic_ids))
 535      {
 536          return;
 537      }
 538  
 539      $forum_ids = array($forum_id);
 540  
 541      if (!is_array($topic_ids))
 542      {
 543          $topic_ids = array($topic_ids);
 544      }
 545  
 546      /**
 547       * Perform additional actions before topics move
 548       *
 549       * @event core.move_topics_before
 550       * @var    array    topic_ids    Array of the moved topic ids
 551       * @var    string    forum_id    The forum id from where the topics are moved
 552       * @since 3.2.9-RC1
 553       */
 554      $vars = array(
 555          'topic_ids',
 556          'forum_id',
 557      );
 558      extract($phpbb_dispatcher->trigger_event('core.move_topics_before', compact($vars)));
 559  
 560      $sql = 'DELETE FROM ' . TOPICS_TABLE . '
 561          WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids) . '
 562              AND forum_id = ' . $forum_id;
 563      $db->sql_query($sql);
 564  
 565      if ($auto_sync)
 566      {
 567          $sql = 'SELECT DISTINCT forum_id
 568              FROM ' . TOPICS_TABLE . '
 569              WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
 570          $result = $db->sql_query($sql);
 571  
 572          while ($row = $db->sql_fetchrow($result))
 573          {
 574              $forum_ids[] = $row['forum_id'];
 575          }
 576          $db->sql_freeresult($result);
 577      }
 578  
 579      $table_ary = array(TOPICS_TABLE, POSTS_TABLE, LOG_TABLE, DRAFTS_TABLE, TOPICS_TRACK_TABLE);
 580  
 581      /**
 582       * Perform additional actions before topics move
 583       *
 584       * @event core.move_topics_before_query
 585       * @var    array    table_ary    Array of tables from which forum_id will be updated for all rows that hold the moved topics
 586       * @var    array    topic_ids    Array of the moved topic ids
 587       * @var    string    forum_id    The forum id from where the topics are moved
 588       * @var    array    forum_ids    Array of the forums where the topics are moving (includes also forum_id)
 589       * @var bool    auto_sync    Whether or not to perform auto sync
 590       * @since 3.1.5-RC1
 591       */
 592      $vars = array(
 593              'table_ary',
 594              'topic_ids',
 595              'forum_id',
 596              'forum_ids',
 597              'auto_sync',
 598      );
 599      extract($phpbb_dispatcher->trigger_event('core.move_topics_before_query', compact($vars)));
 600  
 601      foreach ($table_ary as $table)
 602      {
 603          $sql = "UPDATE $table
 604              SET forum_id = $forum_id
 605              WHERE " . $db->sql_in_set('topic_id', $topic_ids);
 606          $db->sql_query($sql);
 607      }
 608      unset($table_ary);
 609  
 610      /**
 611       * Perform additional actions after topics move
 612       *
 613       * @event core.move_topics_after
 614       * @var    array    topic_ids    Array of the moved topic ids
 615       * @var    string    forum_id    The forum id from where the topics were moved
 616       * @var    array    forum_ids    Array of the forums where the topics were moved (includes also forum_id)
 617       * @since 3.2.9-RC1
 618       */
 619      $vars = array(
 620          'topic_ids',
 621          'forum_id',
 622          'forum_ids',
 623      );
 624      extract($phpbb_dispatcher->trigger_event('core.move_topics_after', compact($vars)));
 625  
 626      if ($auto_sync)
 627      {
 628          sync('forum', 'forum_id', $forum_ids, true, true);
 629          unset($forum_ids);
 630      }
 631  }
 632  
 633  /**
 634  * Move post(s)
 635  */
 636  function move_posts($post_ids, $topic_id, $auto_sync = true)
 637  {
 638      global $db, $phpbb_dispatcher;
 639  
 640      if (!is_array($post_ids))
 641      {
 642          $post_ids = array($post_ids);
 643      }
 644  
 645      $forum_ids = array();
 646      $topic_ids = array($topic_id);
 647  
 648      $sql = 'SELECT DISTINCT topic_id, forum_id
 649          FROM ' . POSTS_TABLE . '
 650          WHERE ' . $db->sql_in_set('post_id', $post_ids);
 651      $result = $db->sql_query($sql);
 652  
 653      while ($row = $db->sql_fetchrow($result))
 654      {
 655          $forum_ids[] = (int) $row['forum_id'];
 656          $topic_ids[] = (int) $row['topic_id'];
 657      }
 658      $db->sql_freeresult($result);
 659  
 660      $sql = 'SELECT forum_id
 661          FROM ' . TOPICS_TABLE . '
 662          WHERE topic_id = ' . $topic_id;
 663      $result = $db->sql_query($sql);
 664      $forum_row = $db->sql_fetchrow($result);
 665      $db->sql_freeresult($result);
 666  
 667      if (!$forum_row)
 668      {
 669          trigger_error('NO_TOPIC');
 670      }
 671  
 672      /**
 673       * Perform additional actions before moving posts
 674       *
 675       * @event core.move_posts_before
 676       * @var    array    post_ids    Array of post ids to move
 677       * @var    int        topic_id    The topic id the posts are moved to
 678       * @var    bool    auto_sync    Whether or not to perform auto sync
 679       * @var    array    forum_ids    Array of the forum ids the posts are moved from
 680       * @var    array    topic_ids    Array of the topic ids the posts are moved from
 681       * @var    array    forum_row    Array with the forum id of the topic the posts are moved to
 682       * @since 3.1.7-RC1
 683       */
 684      $vars = array(
 685              'post_ids',
 686              'topic_id',
 687              'auto_sync',
 688              'forum_ids',
 689              'topic_ids',
 690              'forum_row',
 691      );
 692      extract($phpbb_dispatcher->trigger_event('core.move_posts_before', compact($vars)));
 693  
 694      $sql = 'UPDATE ' . POSTS_TABLE . '
 695          SET forum_id = ' . (int) $forum_row['forum_id'] . ", topic_id = $topic_id
 696          WHERE " . $db->sql_in_set('post_id', $post_ids);
 697      $db->sql_query($sql);
 698  
 699      $sql = 'UPDATE ' . ATTACHMENTS_TABLE . "
 700          SET topic_id = $topic_id, in_message = 0
 701          WHERE " . $db->sql_in_set('post_msg_id', $post_ids);
 702      $db->sql_query($sql);
 703  
 704      /**
 705       * Perform additional actions after moving posts
 706       *
 707       * @event core.move_posts_after
 708       * @var    array    post_ids    Array of the moved post ids
 709       * @var    int        topic_id    The topic id the posts are moved to
 710       * @var    bool    auto_sync    Whether or not to perform auto sync
 711       * @var    array    forum_ids    Array of the forum ids the posts are moved from
 712       * @var    array    topic_ids    Array of the topic ids the posts are moved from
 713       * @var    array    forum_row    Array with the forum id of the topic the posts are moved to
 714       * @since 3.1.7-RC1
 715       */
 716      $vars = array(
 717              'post_ids',
 718              'topic_id',
 719              'auto_sync',
 720              'forum_ids',
 721              'topic_ids',
 722              'forum_row',
 723      );
 724      extract($phpbb_dispatcher->trigger_event('core.move_posts_after', compact($vars)));
 725  
 726      if ($auto_sync)
 727      {
 728          $forum_ids[] = (int) $forum_row['forum_id'];
 729  
 730          sync('topic_reported', 'topic_id', $topic_ids);
 731          sync('topic_attachment', 'topic_id', $topic_ids);
 732          sync('topic', 'topic_id', $topic_ids, true);
 733          sync('forum', 'forum_id', $forum_ids, true, true);
 734  
 735          /**
 736           * Perform additional actions after move post sync
 737           *
 738           * @event core.move_posts_sync_after
 739           * @var    array    post_ids    Array of the moved post ids
 740           * @var    int        topic_id    The topic id the posts are moved to
 741           * @var    bool    auto_sync    Whether or not to perform auto sync
 742           * @var    array    forum_ids    Array of the forum ids the posts are moved from
 743           * @var    array    topic_ids    Array of the topic ids the posts are moved from
 744           * @var    array    forum_row    Array with the forum id of the topic the posts are moved to
 745           * @since 3.1.11-RC1
 746           */
 747          $vars = array(
 748              'post_ids',
 749              'topic_id',
 750              'auto_sync',
 751              'forum_ids',
 752              'topic_ids',
 753              'forum_row',
 754          );
 755          extract($phpbb_dispatcher->trigger_event('core.move_posts_sync_after', compact($vars)));
 756      }
 757  
 758      // Update posted information
 759      update_posted_info($topic_ids);
 760  }
 761  
 762  /**
 763  * Remove topic(s)
 764  */
 765  function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_sync = true, $call_delete_posts = true)
 766  {
 767      global $db, $config, $phpbb_container, $phpbb_dispatcher;
 768  
 769      $approved_topics = 0;
 770      $forum_ids = $topic_ids = array();
 771  
 772      if ($where_type === 'range')
 773      {
 774          $where_clause = $where_ids;
 775      }
 776      else
 777      {
 778          $where_ids = (is_array($where_ids)) ? array_unique($where_ids) : array($where_ids);
 779  
 780          if (!count($where_ids))
 781          {
 782              return array('topics' => 0, 'posts' => 0);
 783          }
 784  
 785          $where_clause = $db->sql_in_set($where_type, $where_ids);
 786      }
 787  
 788      // Making sure that delete_posts does not call delete_topics again...
 789      $return = array(
 790          'posts' => ($call_delete_posts) ? delete_posts($where_type, $where_ids, false, true, $post_count_sync, false) : 0,
 791      );
 792  
 793      $sql = 'SELECT topic_id, forum_id, topic_visibility, topic_moved_id
 794          FROM ' . TOPICS_TABLE . '
 795          WHERE ' . $where_clause;
 796      $result = $db->sql_query($sql);
 797  
 798      while ($row = $db->sql_fetchrow($result))
 799      {
 800          $forum_ids[] = $row['forum_id'];
 801          $topic_ids[] = $row['topic_id'];
 802  
 803          if ($row['topic_visibility'] == ITEM_APPROVED && !$row['topic_moved_id'])
 804          {
 805              $approved_topics++;
 806          }
 807      }
 808      $db->sql_freeresult($result);
 809  
 810      $return['topics'] = count($topic_ids);
 811  
 812      if (!count($topic_ids))
 813      {
 814          return $return;
 815      }
 816  
 817      $db->sql_transaction('begin');
 818  
 819      $table_ary = array(BOOKMARKS_TABLE, TOPICS_TRACK_TABLE, TOPICS_POSTED_TABLE, POLL_VOTES_TABLE, POLL_OPTIONS_TABLE, TOPICS_WATCH_TABLE, TOPICS_TABLE);
 820  
 821      /**
 822       * Perform additional actions before topic(s) deletion
 823       *
 824       * @event core.delete_topics_before_query
 825       * @var    array    table_ary    Array of tables from which all rows will be deleted that hold a topic_id occuring in topic_ids
 826       * @var    array    topic_ids    Array of topic ids to delete
 827       * @since 3.1.4-RC1
 828       */
 829      $vars = array(
 830              'table_ary',
 831              'topic_ids',
 832      );
 833      extract($phpbb_dispatcher->trigger_event('core.delete_topics_before_query', compact($vars)));
 834  
 835      foreach ($table_ary as $table)
 836      {
 837          $sql = "DELETE FROM $table
 838              WHERE " . $db->sql_in_set('topic_id', $topic_ids);
 839          $db->sql_query($sql);
 840      }
 841      unset($table_ary);
 842  
 843      /**
 844       * Perform additional actions after topic(s) deletion
 845       *
 846       * @event core.delete_topics_after_query
 847       * @var    array    topic_ids    Array of topic ids that were deleted
 848       * @since 3.1.4-RC1
 849       */
 850      $vars = array(
 851              'topic_ids',
 852      );
 853      extract($phpbb_dispatcher->trigger_event('core.delete_topics_after_query', compact($vars)));
 854  
 855      $moved_topic_ids = array();
 856  
 857      // update the other forums
 858      $sql = 'SELECT topic_id, forum_id
 859          FROM ' . TOPICS_TABLE . '
 860          WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids);
 861      $result = $db->sql_query($sql);
 862  
 863      while ($row = $db->sql_fetchrow($result))
 864      {
 865          $forum_ids[] = $row['forum_id'];
 866          $moved_topic_ids[] = $row['topic_id'];
 867      }
 868      $db->sql_freeresult($result);
 869  
 870      if (count($moved_topic_ids))
 871      {
 872          $sql = 'DELETE FROM ' . TOPICS_TABLE . '
 873              WHERE ' . $db->sql_in_set('topic_id', $moved_topic_ids);
 874          $db->sql_query($sql);
 875      }
 876  
 877      $db->sql_transaction('commit');
 878  
 879      if ($auto_sync)
 880      {
 881          sync('forum', 'forum_id', array_unique($forum_ids), true, true);
 882          sync('topic_reported', $where_type, $where_ids);
 883      }
 884  
 885      if ($approved_topics)
 886      {
 887          $config->increment('num_topics', $approved_topics * (-1), false);
 888      }
 889  
 890      /* @var $phpbb_notifications \phpbb\notification\manager */
 891      $phpbb_notifications = $phpbb_container->get('notification_manager');
 892  
 893      $phpbb_notifications->delete_notifications(array(
 894          'notification.type.topic',
 895          'notification.type.approve_topic',
 896          'notification.type.topic_in_queue',
 897      ), $topic_ids);
 898  
 899      return $return;
 900  }
 901  
 902  /**
 903  * Remove post(s)
 904  */
 905  function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync = true, $post_count_sync = true, $call_delete_topics = true)
 906  {
 907      global $db, $config, $phpbb_root_path, $phpEx, $auth, $user, $phpbb_container, $phpbb_dispatcher;
 908  
 909      // Notifications types to delete
 910      $delete_notifications_types = array(
 911          'notification.type.quote',
 912          'notification.type.approve_post',
 913          'notification.type.post_in_queue',
 914          'notification.type.report_post',
 915      );
 916  
 917      /**
 918      * Perform additional actions before post(s) deletion
 919      *
 920      * @event core.delete_posts_before
 921      * @var    string    where_type                    Variable containing posts deletion mode
 922      * @var    mixed    where_ids                    Array or comma separated list of posts ids to delete
 923      * @var    bool    auto_sync                    Flag indicating if topics/forums should be synchronized
 924      * @var    bool    posted_sync                    Flag indicating if topics_posted table should be resynchronized
 925      * @var    bool    post_count_sync                Flag indicating if posts count should be resynchronized
 926      * @var    bool    call_delete_topics            Flag indicating if topics having no posts should be deleted
 927      * @var    array    delete_notifications_types    Array with notifications types to delete
 928      * @since 3.1.0-a4
 929      */
 930      $vars = array(
 931          'where_type',
 932          'where_ids',
 933          'auto_sync',
 934          'posted_sync',
 935          'post_count_sync',
 936          'call_delete_topics',
 937          'delete_notifications_types',
 938      );
 939      extract($phpbb_dispatcher->trigger_event('core.delete_posts_before', compact($vars)));
 940  
 941      if ($where_type === 'range')
 942      {
 943          $where_clause = $where_ids;
 944      }
 945      else
 946      {
 947          if (is_array($where_ids))
 948          {
 949              $where_ids = array_unique($where_ids);
 950          }
 951          else
 952          {
 953              $where_ids = array($where_ids);
 954          }
 955  
 956          if (!count($where_ids))
 957          {
 958              return false;
 959          }
 960  
 961          $where_ids = array_map('intval', $where_ids);
 962  
 963  /*        Possible code for splitting post deletion
 964          if (count($where_ids) >= 1001)
 965          {
 966              // Split into chunks of 1000
 967              $chunks = array_chunk($where_ids, 1000);
 968  
 969              foreach ($chunks as $_where_ids)
 970              {
 971                  delete_posts($where_type, $_where_ids, $auto_sync, $posted_sync, $post_count_sync, $call_delete_topics);
 972              }
 973  
 974              return;
 975          }*/
 976  
 977          $where_clause = $db->sql_in_set($where_type, $where_ids);
 978      }
 979  
 980      $approved_posts = 0;
 981      $post_ids = $topic_ids = $forum_ids = $post_counts = $remove_topics = array();
 982  
 983      $sql = 'SELECT post_id, poster_id, post_visibility, post_postcount, topic_id, forum_id
 984          FROM ' . POSTS_TABLE . '
 985          WHERE ' . $where_clause;
 986      $result = $db->sql_query($sql);
 987  
 988      while ($row = $db->sql_fetchrow($result))
 989      {
 990          $post_ids[] = (int) $row['post_id'];
 991          $poster_ids[] = (int) $row['poster_id'];
 992          $topic_ids[] = (int) $row['topic_id'];
 993          $forum_ids[] = (int) $row['forum_id'];
 994  
 995          if ($row['post_postcount'] && $post_count_sync && $row['post_visibility'] == ITEM_APPROVED)
 996          {
 997              $post_counts[$row['poster_id']] = (!empty($post_counts[$row['poster_id']])) ? $post_counts[$row['poster_id']] + 1 : 1;
 998          }
 999  
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'] . ' -&gt; ');
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  }


Generated: Sat Nov 4 14:26:03 2023 Cross-referenced by PHPXref 0.7.1