[ Index ] |
PHP Cross Reference of phpBB-3.2.11-deutsch |
[Summary view] [Print] [Text view]
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 .= ' '; 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') 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 * Delete Attachments 1195 * 1196 * @deprecated 3.2.0-a1 (To be removed: 3.4.0) 1197 * 1198 * @param string $mode can be: post|message|topic|attach|user 1199 * @param mixed $ids can be: post_ids, message_ids, topic_ids, attach_ids, user_ids 1200 * @param bool $resync set this to false if you are deleting posts or topics 1201 */ 1202 function delete_attachments($mode, $ids, $resync = true) 1203 { 1204 global $phpbb_container; 1205 1206 /** @var \phpbb\attachment\manager $attachment_manager */ 1207 $attachment_manager = $phpbb_container->get('attachment.manager'); 1208 $num_deleted = $attachment_manager->delete($mode, $ids, $resync); 1209 1210 unset($attachment_manager); 1211 1212 return $num_deleted; 1213 } 1214 1215 /** 1216 * Deletes shadow topics pointing to a specified forum. 1217 * 1218 * @param int $forum_id The forum id 1219 * @param string $sql_more Additional WHERE statement, e.g. t.topic_time < (time() - 1234) 1220 * @param bool $auto_sync Will call sync() if this is true 1221 * 1222 * @return array Array with affected forums 1223 */ 1224 function delete_topic_shadows($forum_id, $sql_more = '', $auto_sync = true) 1225 { 1226 global $db; 1227 1228 if (!$forum_id) 1229 { 1230 // Nothing to do. 1231 return; 1232 } 1233 1234 // Set of affected forums we have to resync 1235 $sync_forum_ids = array(); 1236 1237 // Amount of topics we select and delete at once. 1238 $batch_size = 500; 1239 1240 do 1241 { 1242 $sql = 'SELECT t2.forum_id, t2.topic_id 1243 FROM ' . TOPICS_TABLE . ' t2, ' . TOPICS_TABLE . ' t 1244 WHERE t2.topic_moved_id = t.topic_id 1245 AND t.forum_id = ' . (int) $forum_id . ' 1246 ' . (($sql_more) ? 'AND ' . $sql_more : ''); 1247 $result = $db->sql_query_limit($sql, $batch_size); 1248 1249 $topic_ids = array(); 1250 while ($row = $db->sql_fetchrow($result)) 1251 { 1252 $topic_ids[] = (int) $row['topic_id']; 1253 1254 $sync_forum_ids[(int) $row['forum_id']] = (int) $row['forum_id']; 1255 } 1256 $db->sql_freeresult($result); 1257 1258 if (!empty($topic_ids)) 1259 { 1260 $sql = 'DELETE FROM ' . TOPICS_TABLE . ' 1261 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 1262 $db->sql_query($sql); 1263 } 1264 } 1265 while (count($topic_ids) == $batch_size); 1266 1267 if ($auto_sync) 1268 { 1269 sync('forum', 'forum_id', $sync_forum_ids, true, true); 1270 } 1271 1272 return $sync_forum_ids; 1273 } 1274 1275 /** 1276 * Update/Sync posted information for topics 1277 */ 1278 function update_posted_info(&$topic_ids) 1279 { 1280 global $db, $config; 1281 1282 if (empty($topic_ids) || !$config['load_db_track']) 1283 { 1284 return; 1285 } 1286 1287 // First of all, let us remove any posted information for these topics 1288 $sql = 'DELETE FROM ' . TOPICS_POSTED_TABLE . ' 1289 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 1290 $db->sql_query($sql); 1291 1292 // Now, let us collect the user/topic combos for rebuilding the information 1293 $sql = 'SELECT poster_id, topic_id 1294 FROM ' . POSTS_TABLE . ' 1295 WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . ' 1296 AND poster_id <> ' . ANONYMOUS . ' 1297 GROUP BY poster_id, topic_id'; 1298 $result = $db->sql_query($sql); 1299 1300 $posted = array(); 1301 while ($row = $db->sql_fetchrow($result)) 1302 { 1303 // Add as key to make them unique (grouping by) and circumvent empty keys on array_unique 1304 $posted[$row['poster_id']][] = $row['topic_id']; 1305 } 1306 $db->sql_freeresult($result); 1307 1308 // Now add the information... 1309 $sql_ary = array(); 1310 foreach ($posted as $user_id => $topic_row) 1311 { 1312 foreach ($topic_row as $topic_id) 1313 { 1314 $sql_ary[] = array( 1315 'user_id' => (int) $user_id, 1316 'topic_id' => (int) $topic_id, 1317 'topic_posted' => 1, 1318 ); 1319 } 1320 } 1321 unset($posted); 1322 1323 $db->sql_multi_insert(TOPICS_POSTED_TABLE, $sql_ary); 1324 } 1325 1326 /** 1327 * Delete attached file 1328 * 1329 * @deprecated 3.2.0-a1 (To be removed: 3.4.0) 1330 */ 1331 function phpbb_unlink($filename, $mode = 'file', $entry_removed = false) 1332 { 1333 global $phpbb_container; 1334 1335 /** @var \phpbb\attachment\manager $attachment_manager */ 1336 $attachment_manager = $phpbb_container->get('attachment.manager'); 1337 $unlink = $attachment_manager->unlink($filename, $mode, $entry_removed); 1338 unset($attachment_manager); 1339 1340 return $unlink; 1341 } 1342 1343 /** 1344 * All-encompasing sync function 1345 * 1346 * Exaples: 1347 * <code> 1348 * sync('topic', 'topic_id', 123); // resync topic #123 1349 * sync('topic', 'forum_id', array(2, 3)); // resync topics from forum #2 and #3 1350 * sync('topic'); // resync all topics 1351 * sync('topic', 'range', 'topic_id BETWEEN 1 AND 60'); // resync a range of topics/forums (only available for 'topic' and 'forum' modes) 1352 * </code> 1353 * 1354 * Modes: 1355 * - forum Resync complete forum 1356 * - topic Resync topics 1357 * - topic_moved Removes topic shadows that would be in the same forum as the topic they link to 1358 * - topic_visibility Resyncs the topic_visibility flag according to the status of the first post 1359 * - post_reported Resyncs the post_reported flag, relying on actual reports 1360 * - topic_reported Resyncs the topic_reported flag, relying on post_reported flags 1361 * - post_attachement Same as post_reported, but with attachment flags 1362 * - topic_attachement Same as topic_reported, but with attachment flags 1363 */ 1364 function sync($mode, $where_type = '', $where_ids = '', $resync_parents = false, $sync_extra = false) 1365 { 1366 global $db; 1367 1368 if (is_array($where_ids)) 1369 { 1370 $where_ids = array_unique($where_ids); 1371 $where_ids = array_map('intval', $where_ids); 1372 } 1373 else if ($where_type != 'range') 1374 { 1375 $where_ids = ($where_ids) ? array((int) $where_ids) : array(); 1376 } 1377 1378 if ($mode == 'forum' || $mode == 'topic' || $mode == 'topic_visibility' || $mode == 'topic_reported' || $mode == 'post_reported') 1379 { 1380 if (!$where_type) 1381 { 1382 $where_sql = ''; 1383 $where_sql_and = 'WHERE'; 1384 } 1385 else if ($where_type == 'range') 1386 { 1387 // Only check a range of topics/forums. For instance: 'topic_id BETWEEN 1 AND 60' 1388 $where_sql = 'WHERE (' . $mode[0] . ".$where_ids)"; 1389 $where_sql_and = $where_sql . "\n\tAND"; 1390 } 1391 else 1392 { 1393 // Do not sync the "global forum" 1394 $where_ids = array_diff($where_ids, array(0)); 1395 1396 if (!count($where_ids)) 1397 { 1398 // Empty array with IDs. This means that we don't have any work to do. Just return. 1399 return; 1400 } 1401 1402 // Limit the topics/forums we are syncing, use specific topic/forum IDs. 1403 // $where_type contains the field for the where clause (forum_id, topic_id) 1404 $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids); 1405 $where_sql_and = $where_sql . "\n\tAND"; 1406 } 1407 } 1408 else 1409 { 1410 if (!count($where_ids)) 1411 { 1412 return; 1413 } 1414 1415 // $where_type contains the field for the where clause (forum_id, topic_id) 1416 $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids); 1417 $where_sql_and = $where_sql . "\n\tAND"; 1418 } 1419 1420 switch ($mode) 1421 { 1422 case 'topic_moved': 1423 $db->sql_transaction('begin'); 1424 switch ($db->get_sql_layer()) 1425 { 1426 case 'mysql4': 1427 case 'mysqli': 1428 $sql = 'DELETE FROM ' . TOPICS_TABLE . ' 1429 USING ' . TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2 1430 WHERE t1.topic_moved_id = t2.topic_id 1431 AND t1.forum_id = t2.forum_id"; 1432 $db->sql_query($sql); 1433 break; 1434 1435 default: 1436 $sql = 'SELECT t1.topic_id 1437 FROM ' .TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2 1438 WHERE t1.topic_moved_id = t2.topic_id 1439 AND t1.forum_id = t2.forum_id"; 1440 $result = $db->sql_query($sql); 1441 1442 $topic_id_ary = array(); 1443 while ($row = $db->sql_fetchrow($result)) 1444 { 1445 $topic_id_ary[] = $row['topic_id']; 1446 } 1447 $db->sql_freeresult($result); 1448 1449 if (!count($topic_id_ary)) 1450 { 1451 return; 1452 } 1453 1454 $sql = 'DELETE FROM ' . TOPICS_TABLE . ' 1455 WHERE ' . $db->sql_in_set('topic_id', $topic_id_ary); 1456 $db->sql_query($sql); 1457 1458 break; 1459 } 1460 1461 $db->sql_transaction('commit'); 1462 break; 1463 1464 case 'topic_visibility': 1465 1466 $db->sql_transaction('begin'); 1467 1468 $sql = 'SELECT t.topic_id, p.post_visibility 1469 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p 1470 $where_sql_and p.topic_id = t.topic_id 1471 AND p.post_visibility = " . ITEM_APPROVED; 1472 $result = $db->sql_query($sql); 1473 1474 $topics_approved = array(); 1475 while ($row = $db->sql_fetchrow($result)) 1476 { 1477 $topics_approved[] = (int) $row['topic_id']; 1478 } 1479 $db->sql_freeresult($result); 1480 1481 $sql = 'SELECT t.topic_id, p.post_visibility 1482 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p 1483 $where_sql_and " . $db->sql_in_set('t.topic_id', $topics_approved, true, true) . ' 1484 AND p.topic_id = t.topic_id 1485 AND p.post_visibility = ' . ITEM_DELETED; 1486 $result = $db->sql_query($sql); 1487 1488 $topics_softdeleted = array(); 1489 while ($row = $db->sql_fetchrow($result)) 1490 { 1491 $topics_softdeleted[] = (int) $row['topic_id']; 1492 } 1493 $db->sql_freeresult($result); 1494 1495 $topics_softdeleted = array_diff($topics_softdeleted, $topics_approved); 1496 $topics_not_unapproved = array_merge($topics_softdeleted, $topics_approved); 1497 1498 $update_ary = array( 1499 ITEM_UNAPPROVED => (!empty($topics_not_unapproved)) ? $where_sql_and . ' ' . $db->sql_in_set('topic_id', $topics_not_unapproved, true) : '', 1500 ITEM_APPROVED => (!empty($topics_approved)) ? ' WHERE ' . $db->sql_in_set('topic_id', $topics_approved) : '', 1501 ITEM_DELETED => (!empty($topics_softdeleted)) ? ' WHERE ' . $db->sql_in_set('topic_id', $topics_softdeleted) : '', 1502 ); 1503 1504 foreach ($update_ary as $visibility => $sql_where) 1505 { 1506 if ($sql_where) 1507 { 1508 $sql = 'UPDATE ' . TOPICS_TABLE . ' 1509 SET topic_visibility = ' . $visibility . ' 1510 ' . $sql_where; 1511 $db->sql_query($sql); 1512 } 1513 } 1514 1515 $db->sql_transaction('commit'); 1516 break; 1517 1518 case 'post_reported': 1519 $post_ids = $post_reported = array(); 1520 1521 $db->sql_transaction('begin'); 1522 1523 $sql = 'SELECT p.post_id, p.post_reported 1524 FROM ' . POSTS_TABLE . " p 1525 $where_sql 1526 GROUP BY p.post_id, p.post_reported"; 1527 $result = $db->sql_query($sql); 1528 1529 while ($row = $db->sql_fetchrow($result)) 1530 { 1531 $post_ids[$row['post_id']] = $row['post_id']; 1532 if ($row['post_reported']) 1533 { 1534 $post_reported[$row['post_id']] = 1; 1535 } 1536 } 1537 $db->sql_freeresult($result); 1538 1539 $sql = 'SELECT DISTINCT(post_id) 1540 FROM ' . REPORTS_TABLE . ' 1541 WHERE ' . $db->sql_in_set('post_id', $post_ids) . ' 1542 AND report_closed = 0'; 1543 $result = $db->sql_query($sql); 1544 1545 $post_ids = array(); 1546 while ($row = $db->sql_fetchrow($result)) 1547 { 1548 if (!isset($post_reported[$row['post_id']])) 1549 { 1550 $post_ids[] = $row['post_id']; 1551 } 1552 else 1553 { 1554 unset($post_reported[$row['post_id']]); 1555 } 1556 } 1557 $db->sql_freeresult($result); 1558 1559 // $post_reported should be empty by now, if it's not it contains 1560 // posts that are falsely flagged as reported 1561 foreach ($post_reported as $post_id => $void) 1562 { 1563 $post_ids[] = $post_id; 1564 } 1565 1566 if (count($post_ids)) 1567 { 1568 $sql = 'UPDATE ' . POSTS_TABLE . ' 1569 SET post_reported = 1 - post_reported 1570 WHERE ' . $db->sql_in_set('post_id', $post_ids); 1571 $db->sql_query($sql); 1572 } 1573 1574 $db->sql_transaction('commit'); 1575 break; 1576 1577 case 'topic_reported': 1578 if ($sync_extra) 1579 { 1580 sync('post_reported', $where_type, $where_ids); 1581 } 1582 1583 $topic_ids = $topic_reported = array(); 1584 1585 $db->sql_transaction('begin'); 1586 1587 $sql = 'SELECT DISTINCT(t.topic_id) 1588 FROM ' . POSTS_TABLE . " t 1589 $where_sql_and t.post_reported = 1"; 1590 $result = $db->sql_query($sql); 1591 1592 while ($row = $db->sql_fetchrow($result)) 1593 { 1594 $topic_reported[$row['topic_id']] = 1; 1595 } 1596 $db->sql_freeresult($result); 1597 1598 $sql = 'SELECT t.topic_id, t.topic_reported 1599 FROM ' . TOPICS_TABLE . " t 1600 $where_sql"; 1601 $result = $db->sql_query($sql); 1602 1603 while ($row = $db->sql_fetchrow($result)) 1604 { 1605 if ($row['topic_reported'] ^ isset($topic_reported[$row['topic_id']])) 1606 { 1607 $topic_ids[] = $row['topic_id']; 1608 } 1609 } 1610 $db->sql_freeresult($result); 1611 1612 if (count($topic_ids)) 1613 { 1614 $sql = 'UPDATE ' . TOPICS_TABLE . ' 1615 SET topic_reported = 1 - topic_reported 1616 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 1617 $db->sql_query($sql); 1618 } 1619 1620 $db->sql_transaction('commit'); 1621 break; 1622 1623 case 'post_attachment': 1624 $post_ids = $post_attachment = array(); 1625 1626 $db->sql_transaction('begin'); 1627 1628 $sql = 'SELECT p.post_id, p.post_attachment 1629 FROM ' . POSTS_TABLE . " p 1630 $where_sql 1631 GROUP BY p.post_id, p.post_attachment"; 1632 $result = $db->sql_query($sql); 1633 1634 while ($row = $db->sql_fetchrow($result)) 1635 { 1636 $post_ids[$row['post_id']] = $row['post_id']; 1637 if ($row['post_attachment']) 1638 { 1639 $post_attachment[$row['post_id']] = 1; 1640 } 1641 } 1642 $db->sql_freeresult($result); 1643 1644 $sql = 'SELECT DISTINCT(post_msg_id) 1645 FROM ' . ATTACHMENTS_TABLE . ' 1646 WHERE ' . $db->sql_in_set('post_msg_id', $post_ids) . ' 1647 AND in_message = 0'; 1648 $result = $db->sql_query($sql); 1649 1650 $post_ids = array(); 1651 while ($row = $db->sql_fetchrow($result)) 1652 { 1653 if (!isset($post_attachment[$row['post_msg_id']])) 1654 { 1655 $post_ids[] = $row['post_msg_id']; 1656 } 1657 else 1658 { 1659 unset($post_attachment[$row['post_msg_id']]); 1660 } 1661 } 1662 $db->sql_freeresult($result); 1663 1664 // $post_attachment should be empty by now, if it's not it contains 1665 // posts that are falsely flagged as having attachments 1666 foreach ($post_attachment as $post_id => $void) 1667 { 1668 $post_ids[] = $post_id; 1669 } 1670 1671 if (count($post_ids)) 1672 { 1673 $sql = 'UPDATE ' . POSTS_TABLE . ' 1674 SET post_attachment = 1 - post_attachment 1675 WHERE ' . $db->sql_in_set('post_id', $post_ids); 1676 $db->sql_query($sql); 1677 } 1678 1679 $db->sql_transaction('commit'); 1680 break; 1681 1682 case 'topic_attachment': 1683 if ($sync_extra) 1684 { 1685 sync('post_attachment', $where_type, $where_ids); 1686 } 1687 1688 $topic_ids = $topic_attachment = array(); 1689 1690 $db->sql_transaction('begin'); 1691 1692 $sql = 'SELECT DISTINCT(t.topic_id) 1693 FROM ' . POSTS_TABLE . " t 1694 $where_sql_and t.post_attachment = 1"; 1695 $result = $db->sql_query($sql); 1696 1697 while ($row = $db->sql_fetchrow($result)) 1698 { 1699 $topic_attachment[$row['topic_id']] = 1; 1700 } 1701 $db->sql_freeresult($result); 1702 1703 $sql = 'SELECT t.topic_id, t.topic_attachment 1704 FROM ' . TOPICS_TABLE . " t 1705 $where_sql"; 1706 $result = $db->sql_query($sql); 1707 1708 while ($row = $db->sql_fetchrow($result)) 1709 { 1710 if ($row['topic_attachment'] ^ isset($topic_attachment[$row['topic_id']])) 1711 { 1712 $topic_ids[] = $row['topic_id']; 1713 } 1714 } 1715 $db->sql_freeresult($result); 1716 1717 if (count($topic_ids)) 1718 { 1719 $sql = 'UPDATE ' . TOPICS_TABLE . ' 1720 SET topic_attachment = 1 - topic_attachment 1721 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 1722 $db->sql_query($sql); 1723 } 1724 1725 $db->sql_transaction('commit'); 1726 1727 break; 1728 1729 case 'forum': 1730 1731 $db->sql_transaction('begin'); 1732 1733 // 1: Get the list of all forums 1734 $sql = 'SELECT f.* 1735 FROM ' . FORUMS_TABLE . " f 1736 $where_sql"; 1737 $result = $db->sql_query($sql); 1738 1739 $forum_data = $forum_ids = $post_ids = $last_post_id = $post_info = array(); 1740 while ($row = $db->sql_fetchrow($result)) 1741 { 1742 if ($row['forum_type'] == FORUM_LINK) 1743 { 1744 continue; 1745 } 1746 1747 $forum_id = (int) $row['forum_id']; 1748 $forum_ids[$forum_id] = $forum_id; 1749 1750 $forum_data[$forum_id] = $row; 1751 if ($sync_extra) 1752 { 1753 $forum_data[$forum_id]['posts_approved'] = 0; 1754 $forum_data[$forum_id]['posts_unapproved'] = 0; 1755 $forum_data[$forum_id]['posts_softdeleted'] = 0; 1756 $forum_data[$forum_id]['topics_approved'] = 0; 1757 $forum_data[$forum_id]['topics_unapproved'] = 0; 1758 $forum_data[$forum_id]['topics_softdeleted'] = 0; 1759 } 1760 $forum_data[$forum_id]['last_post_id'] = 0; 1761 $forum_data[$forum_id]['last_post_subject'] = ''; 1762 $forum_data[$forum_id]['last_post_time'] = 0; 1763 $forum_data[$forum_id]['last_poster_id'] = 0; 1764 $forum_data[$forum_id]['last_poster_name'] = ''; 1765 $forum_data[$forum_id]['last_poster_colour'] = ''; 1766 } 1767 $db->sql_freeresult($result); 1768 1769 if (!count($forum_ids)) 1770 { 1771 break; 1772 } 1773 1774 $forum_ids = array_values($forum_ids); 1775 1776 // 2: Get topic counts for each forum (optional) 1777 if ($sync_extra) 1778 { 1779 $sql = 'SELECT forum_id, topic_visibility, COUNT(topic_id) AS total_topics 1780 FROM ' . TOPICS_TABLE . ' 1781 WHERE ' . $db->sql_in_set('forum_id', $forum_ids) . ' 1782 GROUP BY forum_id, topic_visibility'; 1783 $result = $db->sql_query($sql); 1784 1785 while ($row = $db->sql_fetchrow($result)) 1786 { 1787 $forum_id = (int) $row['forum_id']; 1788 1789 if ($row['topic_visibility'] == ITEM_APPROVED) 1790 { 1791 $forum_data[$forum_id]['topics_approved'] = $row['total_topics']; 1792 } 1793 else if ($row['topic_visibility'] == ITEM_UNAPPROVED || $row['topic_visibility'] == ITEM_REAPPROVE) 1794 { 1795 $forum_data[$forum_id]['topics_unapproved'] = $row['total_topics']; 1796 } 1797 else if ($row['topic_visibility'] == ITEM_DELETED) 1798 { 1799 $forum_data[$forum_id]['topics_softdeleted'] = $row['total_topics']; 1800 } 1801 } 1802 $db->sql_freeresult($result); 1803 } 1804 1805 // 3: Get post count for each forum (optional) 1806 if ($sync_extra) 1807 { 1808 if (count($forum_ids) == 1) 1809 { 1810 $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 1811 FROM ' . TOPICS_TABLE . ' t 1812 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . ' 1813 AND t.topic_status <> ' . ITEM_MOVED; 1814 } 1815 else 1816 { 1817 $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 1818 FROM ' . TOPICS_TABLE . ' t 1819 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . ' 1820 AND t.topic_status <> ' . ITEM_MOVED . ' 1821 GROUP BY t.forum_id'; 1822 } 1823 1824 $result = $db->sql_query($sql); 1825 1826 while ($row = $db->sql_fetchrow($result)) 1827 { 1828 $forum_id = (count($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id']; 1829 1830 $forum_data[$forum_id]['posts_approved'] = (int) $row['forum_posts_approved']; 1831 $forum_data[$forum_id]['posts_unapproved'] = (int) $row['forum_posts_unapproved']; 1832 $forum_data[$forum_id]['posts_softdeleted'] = (int) $row['forum_posts_softdeleted']; 1833 } 1834 $db->sql_freeresult($result); 1835 } 1836 1837 // 4: Get last_post_id for each forum 1838 if (count($forum_ids) == 1) 1839 { 1840 $sql = 'SELECT MAX(t.topic_last_post_id) as last_post_id 1841 FROM ' . TOPICS_TABLE . ' t 1842 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . ' 1843 AND t.topic_visibility = ' . ITEM_APPROVED; 1844 } 1845 else 1846 { 1847 $sql = 'SELECT t.forum_id, MAX(t.topic_last_post_id) as last_post_id 1848 FROM ' . TOPICS_TABLE . ' t 1849 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . ' 1850 AND t.topic_visibility = ' . ITEM_APPROVED . ' 1851 GROUP BY t.forum_id'; 1852 } 1853 1854 $result = $db->sql_query($sql); 1855 1856 while ($row = $db->sql_fetchrow($result)) 1857 { 1858 $forum_id = (count($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id']; 1859 1860 $forum_data[$forum_id]['last_post_id'] = (int) $row['last_post_id']; 1861 1862 $post_ids[] = $row['last_post_id']; 1863 } 1864 $db->sql_freeresult($result); 1865 1866 // 5: Retrieve last_post infos 1867 if (count($post_ids)) 1868 { 1869 $sql = 'SELECT p.post_id, p.poster_id, p.post_subject, p.post_time, p.post_username, u.username, u.user_colour 1870 FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u 1871 WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . ' 1872 AND p.poster_id = u.user_id'; 1873 $result = $db->sql_query($sql); 1874 1875 while ($row = $db->sql_fetchrow($result)) 1876 { 1877 $post_info[$row['post_id']] = $row; 1878 } 1879 $db->sql_freeresult($result); 1880 1881 foreach ($forum_data as $forum_id => $data) 1882 { 1883 if ($data['last_post_id']) 1884 { 1885 if (isset($post_info[$data['last_post_id']])) 1886 { 1887 $forum_data[$forum_id]['last_post_subject'] = $post_info[$data['last_post_id']]['post_subject']; 1888 $forum_data[$forum_id]['last_post_time'] = $post_info[$data['last_post_id']]['post_time']; 1889 $forum_data[$forum_id]['last_poster_id'] = $post_info[$data['last_post_id']]['poster_id']; 1890 $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']; 1891 $forum_data[$forum_id]['last_poster_colour'] = $post_info[$data['last_post_id']]['user_colour']; 1892 } 1893 else 1894 { 1895 // For some reason we did not find the post in the db 1896 $forum_data[$forum_id]['last_post_id'] = 0; 1897 $forum_data[$forum_id]['last_post_subject'] = ''; 1898 $forum_data[$forum_id]['last_post_time'] = 0; 1899 $forum_data[$forum_id]['last_poster_id'] = 0; 1900 $forum_data[$forum_id]['last_poster_name'] = ''; 1901 $forum_data[$forum_id]['last_poster_colour'] = ''; 1902 } 1903 } 1904 } 1905 unset($post_info); 1906 } 1907 1908 // 6: Now do that thing 1909 $fieldnames = array('last_post_id', 'last_post_subject', 'last_post_time', 'last_poster_id', 'last_poster_name', 'last_poster_colour'); 1910 1911 if ($sync_extra) 1912 { 1913 array_push($fieldnames, 'posts_approved', 'posts_unapproved', 'posts_softdeleted', 'topics_approved', 'topics_unapproved', 'topics_softdeleted'); 1914 } 1915 1916 foreach ($forum_data as $forum_id => $row) 1917 { 1918 $sql_ary = array(); 1919 1920 foreach ($fieldnames as $fieldname) 1921 { 1922 if ($row['forum_' . $fieldname] != $row[$fieldname]) 1923 { 1924 if (preg_match('#(name|colour|subject)$#', $fieldname)) 1925 { 1926 $sql_ary['forum_' . $fieldname] = (string) $row[$fieldname]; 1927 } 1928 else 1929 { 1930 $sql_ary['forum_' . $fieldname] = (int) $row[$fieldname]; 1931 } 1932 } 1933 } 1934 1935 if (count($sql_ary)) 1936 { 1937 $sql = 'UPDATE ' . FORUMS_TABLE . ' 1938 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' 1939 WHERE forum_id = ' . $forum_id; 1940 $db->sql_query($sql); 1941 } 1942 } 1943 1944 $db->sql_transaction('commit'); 1945 break; 1946 1947 case 'topic': 1948 $topic_data = $post_ids = $resync_forums = $delete_topics = $delete_posts = $moved_topics = array(); 1949 1950 $db->sql_transaction('begin'); 1951 1952 $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 1953 FROM ' . TOPICS_TABLE . " t 1954 $where_sql"; 1955 $result = $db->sql_query($sql); 1956 1957 while ($row = $db->sql_fetchrow($result)) 1958 { 1959 if ($row['topic_moved_id']) 1960 { 1961 $moved_topics[] = $row['topic_id']; 1962 continue; 1963 } 1964 1965 $topic_id = (int) $row['topic_id']; 1966 $topic_data[$topic_id] = $row; 1967 $topic_data[$topic_id]['visibility'] = ITEM_UNAPPROVED; 1968 $topic_data[$topic_id]['posts_approved'] = 0; 1969 $topic_data[$topic_id]['posts_unapproved'] = 0; 1970 $topic_data[$topic_id]['posts_softdeleted'] = 0; 1971 $topic_data[$topic_id]['first_post_id'] = 0; 1972 $topic_data[$topic_id]['last_post_id'] = 0; 1973 unset($topic_data[$topic_id]['topic_id']); 1974 1975 // This array holds all topic_ids 1976 $delete_topics[$topic_id] = ''; 1977 1978 if ($sync_extra) 1979 { 1980 $topic_data[$topic_id]['reported'] = 0; 1981 $topic_data[$topic_id]['attachment'] = 0; 1982 } 1983 } 1984 $db->sql_freeresult($result); 1985 1986 // Use "t" as table alias because of the $where_sql clause 1987 // NOTE: 't.post_visibility' in the GROUP BY is causing a major slowdown. 1988 $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 1989 FROM ' . POSTS_TABLE . " t 1990 $where_sql 1991 GROUP BY t.topic_id, t.post_visibility"; 1992 $result = $db->sql_query($sql); 1993 1994 while ($row = $db->sql_fetchrow($result)) 1995 { 1996 $topic_id = (int) $row['topic_id']; 1997 1998 $row['first_post_id'] = (int) $row['first_post_id']; 1999 $row['last_post_id'] = (int) $row['last_post_id']; 2000 2001 if (!isset($topic_data[$topic_id])) 2002 { 2003 // Hey, these posts come from a topic that does not exist 2004 $delete_posts[$topic_id] = ''; 2005 } 2006 else 2007 { 2008 // Unset the corresponding entry in $delete_topics 2009 // When we'll be done, only topics with no posts will remain 2010 unset($delete_topics[$topic_id]); 2011 2012 if ($row['post_visibility'] == ITEM_APPROVED) 2013 { 2014 $topic_data[$topic_id]['posts_approved'] = $row['total_posts']; 2015 } 2016 else if ($row['post_visibility'] == ITEM_UNAPPROVED || $row['post_visibility'] == ITEM_REAPPROVE) 2017 { 2018 $topic_data[$topic_id]['posts_unapproved'] = $row['total_posts']; 2019 } 2020 else if ($row['post_visibility'] == ITEM_DELETED) 2021 { 2022 $topic_data[$topic_id]['posts_softdeleted'] = $row['total_posts']; 2023 } 2024 2025 if ($row['post_visibility'] == ITEM_APPROVED) 2026 { 2027 $topic_data[$topic_id]['visibility'] = ITEM_APPROVED; 2028 $topic_data[$topic_id]['first_post_id'] = $row['first_post_id']; 2029 $topic_data[$topic_id]['last_post_id'] = $row['last_post_id']; 2030 } 2031 else if ($topic_data[$topic_id]['visibility'] != ITEM_APPROVED) 2032 { 2033 // If there is no approved post, we take the min/max of the other visibilities 2034 // for the last and first post info, because it is only visible to moderators anyway 2035 $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']; 2036 $topic_data[$topic_id]['last_post_id'] = max($topic_data[$topic_id]['last_post_id'], $row['last_post_id']); 2037 2038 if ($topic_data[$topic_id]['visibility'] == ITEM_UNAPPROVED || $topic_data[$topic_id]['visibility'] == ITEM_REAPPROVE) 2039 { 2040 // Soft delete status is stronger than unapproved. 2041 $topic_data[$topic_id]['visibility'] = $row['post_visibility']; 2042 } 2043 } 2044 } 2045 } 2046 $db->sql_freeresult($result); 2047 2048 foreach ($topic_data as $topic_id => $row) 2049 { 2050 $post_ids[] = $row['first_post_id']; 2051 if ($row['first_post_id'] != $row['last_post_id']) 2052 { 2053 $post_ids[] = $row['last_post_id']; 2054 } 2055 } 2056 2057 // Now we delete empty topics and orphan posts 2058 if (count($delete_posts)) 2059 { 2060 delete_posts('topic_id', array_keys($delete_posts), false); 2061 unset($delete_posts); 2062 } 2063 2064 if (!count($topic_data)) 2065 { 2066 // If we get there, topic ids were invalid or topics did not contain any posts 2067 delete_topics($where_type, $where_ids, true); 2068 return; 2069 } 2070 2071 if (count($delete_topics)) 2072 { 2073 $delete_topic_ids = array(); 2074 foreach ($delete_topics as $topic_id => $void) 2075 { 2076 unset($topic_data[$topic_id]); 2077 $delete_topic_ids[] = $topic_id; 2078 } 2079 2080 delete_topics('topic_id', $delete_topic_ids, false); 2081 unset($delete_topics, $delete_topic_ids); 2082 } 2083 2084 $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 2085 FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u 2086 WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . ' 2087 AND u.user_id = p.poster_id'; 2088 $result = $db->sql_query($sql); 2089 2090 while ($row = $db->sql_fetchrow($result)) 2091 { 2092 $topic_id = intval($row['topic_id']); 2093 2094 if ($row['post_id'] == $topic_data[$topic_id]['first_post_id']) 2095 { 2096 $topic_data[$topic_id]['time'] = $row['post_time']; 2097 $topic_data[$topic_id]['poster'] = $row['poster_id']; 2098 $topic_data[$topic_id]['first_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username']; 2099 $topic_data[$topic_id]['first_poster_colour'] = $row['user_colour']; 2100 } 2101 2102 if ($row['post_id'] == $topic_data[$topic_id]['last_post_id']) 2103 { 2104 $topic_data[$topic_id]['last_poster_id'] = $row['poster_id']; 2105 $topic_data[$topic_id]['last_post_subject'] = $row['post_subject']; 2106 $topic_data[$topic_id]['last_post_time'] = $row['post_time']; 2107 $topic_data[$topic_id]['last_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username']; 2108 $topic_data[$topic_id]['last_poster_colour'] = $row['user_colour']; 2109 } 2110 } 2111 $db->sql_freeresult($result); 2112 2113 // Make sure shadow topics do link to existing topics 2114 if (count($moved_topics)) 2115 { 2116 $delete_topics = array(); 2117 2118 $sql = 'SELECT t1.topic_id, t1.topic_moved_id 2119 FROM ' . TOPICS_TABLE . ' t1 2120 LEFT JOIN ' . TOPICS_TABLE . ' t2 ON (t2.topic_id = t1.topic_moved_id) 2121 WHERE ' . $db->sql_in_set('t1.topic_id', $moved_topics) . ' 2122 AND t2.topic_id IS NULL'; 2123 $result = $db->sql_query($sql); 2124 2125 while ($row = $db->sql_fetchrow($result)) 2126 { 2127 $delete_topics[] = $row['topic_id']; 2128 } 2129 $db->sql_freeresult($result); 2130 2131 if (count($delete_topics)) 2132 { 2133 delete_topics('topic_id', $delete_topics, false); 2134 } 2135 unset($delete_topics); 2136 2137 // Make sure shadow topics having no last post data being updated (this only rarely happens...) 2138 $sql = 'SELECT topic_id, topic_moved_id, topic_last_post_id, topic_first_post_id 2139 FROM ' . TOPICS_TABLE . ' 2140 WHERE ' . $db->sql_in_set('topic_id', $moved_topics) . ' 2141 AND topic_last_post_time = 0'; 2142 $result = $db->sql_query($sql); 2143 2144 $shadow_topic_data = $post_ids = array(); 2145 while ($row = $db->sql_fetchrow($result)) 2146 { 2147 $shadow_topic_data[$row['topic_moved_id']] = $row; 2148 $post_ids[] = $row['topic_last_post_id']; 2149 $post_ids[] = $row['topic_first_post_id']; 2150 } 2151 $db->sql_freeresult($result); 2152 2153 $sync_shadow_topics = array(); 2154 if (count($post_ids)) 2155 { 2156 $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 2157 FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u 2158 WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . ' 2159 AND u.user_id = p.poster_id'; 2160 $result = $db->sql_query($sql); 2161 2162 while ($row = $db->sql_fetchrow($result)) 2163 { 2164 $topic_id = (int) $row['topic_id']; 2165 2166 // Ok, there should be a shadow topic. If there isn't, then there's something wrong with the db. 2167 // However, there's not much we can do about it. 2168 if (!empty($shadow_topic_data[$topic_id])) 2169 { 2170 if ($row['post_id'] == $shadow_topic_data[$topic_id]['topic_first_post_id']) 2171 { 2172 $orig_topic_id = $shadow_topic_data[$topic_id]['topic_id']; 2173 2174 if (!isset($sync_shadow_topics[$orig_topic_id])) 2175 { 2176 $sync_shadow_topics[$orig_topic_id] = array(); 2177 } 2178 2179 $sync_shadow_topics[$orig_topic_id]['topic_time'] = $row['post_time']; 2180 $sync_shadow_topics[$orig_topic_id]['topic_poster'] = $row['poster_id']; 2181 $sync_shadow_topics[$orig_topic_id]['topic_first_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username']; 2182 $sync_shadow_topics[$orig_topic_id]['topic_first_poster_colour'] = $row['user_colour']; 2183 } 2184 2185 if ($row['post_id'] == $shadow_topic_data[$topic_id]['topic_last_post_id']) 2186 { 2187 $orig_topic_id = $shadow_topic_data[$topic_id]['topic_id']; 2188 2189 if (!isset($sync_shadow_topics[$orig_topic_id])) 2190 { 2191 $sync_shadow_topics[$orig_topic_id] = array(); 2192 } 2193 2194 $sync_shadow_topics[$orig_topic_id]['topic_last_poster_id'] = $row['poster_id']; 2195 $sync_shadow_topics[$orig_topic_id]['topic_last_post_subject'] = $row['post_subject']; 2196 $sync_shadow_topics[$orig_topic_id]['topic_last_post_time'] = $row['post_time']; 2197 $sync_shadow_topics[$orig_topic_id]['topic_last_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username']; 2198 $sync_shadow_topics[$orig_topic_id]['topic_last_poster_colour'] = $row['user_colour']; 2199 } 2200 } 2201 } 2202 $db->sql_freeresult($result); 2203 2204 $shadow_topic_data = array(); 2205 2206 // Update the information we collected 2207 if (count($sync_shadow_topics)) 2208 { 2209 foreach ($sync_shadow_topics as $sync_topic_id => $sql_ary) 2210 { 2211 $sql = 'UPDATE ' . TOPICS_TABLE . ' 2212 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' 2213 WHERE topic_id = ' . $sync_topic_id; 2214 $db->sql_query($sql); 2215 } 2216 } 2217 } 2218 2219 unset($sync_shadow_topics, $shadow_topic_data); 2220 } 2221 2222 // These are fields that will be synchronised 2223 $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'); 2224 2225 if ($sync_extra) 2226 { 2227 // This routine assumes that post_reported values are correct 2228 // if they are not, use sync('post_reported') first 2229 $sql = 'SELECT t.topic_id, p.post_id 2230 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p 2231 $where_sql_and p.topic_id = t.topic_id 2232 AND p.post_reported = 1 2233 GROUP BY t.topic_id, p.post_id"; 2234 $result = $db->sql_query($sql); 2235 2236 $fieldnames[] = 'reported'; 2237 while ($row = $db->sql_fetchrow($result)) 2238 { 2239 $topic_data[intval($row['topic_id'])]['reported'] = 1; 2240 } 2241 $db->sql_freeresult($result); 2242 2243 // This routine assumes that post_attachment values are correct 2244 // if they are not, use sync('post_attachment') first 2245 $sql = 'SELECT t.topic_id, p.post_id 2246 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p 2247 $where_sql_and p.topic_id = t.topic_id 2248 AND p.post_attachment = 1 2249 GROUP BY t.topic_id, p.post_id"; 2250 $result = $db->sql_query($sql); 2251 2252 $fieldnames[] = 'attachment'; 2253 while ($row = $db->sql_fetchrow($result)) 2254 { 2255 $topic_data[intval($row['topic_id'])]['attachment'] = 1; 2256 } 2257 $db->sql_freeresult($result); 2258 } 2259 2260 foreach ($topic_data as $topic_id => $row) 2261 { 2262 $sql_ary = array(); 2263 2264 foreach ($fieldnames as $fieldname) 2265 { 2266 if (isset($row[$fieldname]) && isset($row['topic_' . $fieldname]) && $row['topic_' . $fieldname] != $row[$fieldname]) 2267 { 2268 $sql_ary['topic_' . $fieldname] = $row[$fieldname]; 2269 } 2270 } 2271 2272 if (count($sql_ary)) 2273 { 2274 $sql = 'UPDATE ' . TOPICS_TABLE . ' 2275 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' 2276 WHERE topic_id = ' . $topic_id; 2277 $db->sql_query($sql); 2278 2279 $resync_forums[$row['forum_id']] = $row['forum_id']; 2280 } 2281 } 2282 unset($topic_data); 2283 2284 $db->sql_transaction('commit'); 2285 2286 // if some topics have been resync'ed then resync parent forums 2287 // except when we're only syncing a range, we don't want to sync forums during 2288 // batch processing. 2289 if ($resync_parents && count($resync_forums) && $where_type != 'range') 2290 { 2291 sync('forum', 'forum_id', array_values($resync_forums), true, true); 2292 } 2293 break; 2294 } 2295 2296 return; 2297 } 2298 2299 /** 2300 * Prune function 2301 */ 2302 function prune($forum_id, $prune_mode, $prune_date, $prune_flags = 0, $auto_sync = true, $prune_limit = 0) 2303 { 2304 global $db, $phpbb_dispatcher; 2305 2306 if (!is_array($forum_id)) 2307 { 2308 $forum_id = array($forum_id); 2309 } 2310 2311 if (!count($forum_id)) 2312 { 2313 return; 2314 } 2315 2316 $sql_and = ''; 2317 2318 if (!($prune_flags & FORUM_FLAG_PRUNE_ANNOUNCE)) 2319 { 2320 $sql_and .= ' AND topic_type <> ' . POST_ANNOUNCE; 2321 $sql_and .= ' AND topic_type <> ' . POST_GLOBAL; 2322 } 2323 2324 if (!($prune_flags & FORUM_FLAG_PRUNE_STICKY)) 2325 { 2326 $sql_and .= ' AND topic_type <> ' . POST_STICKY; 2327 } 2328 2329 if ($prune_mode == 'posted') 2330 { 2331 $sql_and .= " AND topic_last_post_time < $prune_date"; 2332 } 2333 2334 if ($prune_mode == 'viewed') 2335 { 2336 $sql_and .= " AND topic_last_view_time < $prune_date"; 2337 } 2338 2339 if ($prune_mode == 'shadow') 2340 { 2341 $sql_and .= ' AND topic_status = ' . ITEM_MOVED . " AND topic_last_post_time < $prune_date"; 2342 } 2343 2344 /** 2345 * Use this event to modify the SQL that selects topics to be pruned 2346 * 2347 * @event core.prune_sql 2348 * @var string forum_id The forum id 2349 * @var string prune_mode The prune mode 2350 * @var string prune_date The prune date 2351 * @var int prune_flags The prune flags 2352 * @var bool auto_sync Whether or not to perform auto sync 2353 * @var string sql_and SQL text appended to where clause 2354 * @var int prune_limit The prune limit 2355 * @since 3.1.3-RC1 2356 * @changed 3.1.10-RC1 Added prune_limit 2357 */ 2358 $vars = array( 2359 'forum_id', 2360 'prune_mode', 2361 'prune_date', 2362 'prune_flags', 2363 'auto_sync', 2364 'sql_and', 2365 'prune_limit', 2366 ); 2367 extract($phpbb_dispatcher->trigger_event('core.prune_sql', compact($vars))); 2368 2369 $sql = 'SELECT topic_id 2370 FROM ' . TOPICS_TABLE . ' 2371 WHERE ' . $db->sql_in_set('forum_id', $forum_id) . " 2372 AND poll_start = 0 2373 $sql_and"; 2374 $result = $db->sql_query_limit($sql, $prune_limit); 2375 2376 $topic_list = array(); 2377 while ($row = $db->sql_fetchrow($result)) 2378 { 2379 $topic_list[] = $row['topic_id']; 2380 } 2381 $db->sql_freeresult($result); 2382 2383 if ($prune_flags & FORUM_FLAG_PRUNE_POLL) 2384 { 2385 $sql = 'SELECT topic_id 2386 FROM ' . TOPICS_TABLE . ' 2387 WHERE ' . $db->sql_in_set('forum_id', $forum_id) . " 2388 AND poll_start > 0 2389 AND poll_last_vote < $prune_date 2390 $sql_and"; 2391 $result = $db->sql_query_limit($sql, $prune_limit); 2392 2393 while ($row = $db->sql_fetchrow($result)) 2394 { 2395 $topic_list[] = $row['topic_id']; 2396 } 2397 $db->sql_freeresult($result); 2398 2399 $topic_list = array_unique($topic_list); 2400 } 2401 2402 /** 2403 * Perform additional actions before topic deletion via pruning 2404 * 2405 * @event core.prune_delete_before 2406 * @var int[] topic_list The IDs of the topics to be deleted 2407 * @since 3.2.2-RC1 2408 */ 2409 $vars = array('topic_list'); 2410 extract($phpbb_dispatcher->trigger_event('core.prune_delete_before', compact($vars))); 2411 2412 return delete_topics('topic_id', $topic_list, $auto_sync, false); 2413 } 2414 2415 /** 2416 * Function auto_prune(), this function now relies on passed vars 2417 */ 2418 function auto_prune($forum_id, $prune_mode, $prune_flags, $prune_days, $prune_freq, $log_prune = true) 2419 { 2420 global $db, $user, $phpbb_log; 2421 2422 $sql = 'SELECT forum_name 2423 FROM ' . FORUMS_TABLE . " 2424 WHERE forum_id = $forum_id"; 2425 $result = $db->sql_query($sql, 3600); 2426 $row = $db->sql_fetchrow($result); 2427 $db->sql_freeresult($result); 2428 2429 if ($row) 2430 { 2431 $prune_date = time() - ($prune_days * 86400); 2432 $next_prune = time() + ($prune_freq * 86400); 2433 2434 $result = prune($forum_id, $prune_mode, $prune_date, $prune_flags, true, 300); 2435 2436 if ($result['topics'] == 0 && $result['posts'] == 0) 2437 { 2438 $column = $prune_mode === 'shadow' ? 'prune_shadow_next' : 'prune_next'; 2439 2440 $sql = 'UPDATE ' . FORUMS_TABLE . " 2441 SET $column = $next_prune 2442 WHERE forum_id = $forum_id"; 2443 $db->sql_query($sql); 2444 } 2445 2446 if ($log_prune) 2447 { 2448 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_AUTO_PRUNE', false, [$row['forum_name']]); 2449 } 2450 } 2451 2452 return; 2453 } 2454 2455 /** 2456 * Cache moderators. Called whenever permissions are changed 2457 * via admin_permissions. Changes of usernames and group names 2458 * must be carried through for the moderators table. 2459 * 2460 * @param \phpbb\db\driver\driver_interface $db Database connection 2461 * @param \phpbb\cache\driver\driver_interface Cache driver 2462 * @param \phpbb\auth\auth $auth Authentication object 2463 * @return null 2464 */ 2465 function phpbb_cache_moderators($db, $cache, $auth) 2466 { 2467 // Remove cached sql results 2468 $cache->destroy('sql', MODERATOR_CACHE_TABLE); 2469 2470 // Clear table 2471 switch ($db->get_sql_layer()) 2472 { 2473 case 'sqlite3': 2474 $db->sql_query('DELETE FROM ' . MODERATOR_CACHE_TABLE); 2475 break; 2476 2477 default: 2478 $db->sql_query('TRUNCATE TABLE ' . MODERATOR_CACHE_TABLE); 2479 break; 2480 } 2481 2482 // We add moderators who have forum moderator permissions without an explicit ACL_NEVER setting 2483 $sql_ary = array(); 2484 2485 // Grab all users having moderative options... 2486 $hold_ary = $auth->acl_user_raw_data(false, 'm_%', false); 2487 2488 // Add users? 2489 if (!empty($hold_ary)) 2490 { 2491 // At least one moderative option warrants a display 2492 $ug_id_ary = array_keys($hold_ary); 2493 2494 // Remove users who have group memberships with DENY moderator permissions 2495 $sql_ary_deny = array( 2496 'SELECT' => 'a.forum_id, ug.user_id, g.group_id', 2497 2498 'FROM' => array( 2499 ACL_OPTIONS_TABLE => 'o', 2500 USER_GROUP_TABLE => 'ug', 2501 GROUPS_TABLE => 'g', 2502 ACL_GROUPS_TABLE => 'a', 2503 ), 2504 2505 'LEFT_JOIN' => array( 2506 array( 2507 'FROM' => array(ACL_ROLES_DATA_TABLE => 'r'), 2508 'ON' => 'a.auth_role_id = r.role_id', 2509 ), 2510 ), 2511 2512 'WHERE' => '(o.auth_option_id = a.auth_option_id OR o.auth_option_id = r.auth_option_id) 2513 AND ((a.auth_setting = ' . ACL_NEVER . ' AND r.auth_setting IS NULL) 2514 OR r.auth_setting = ' . ACL_NEVER . ') 2515 AND a.group_id = ug.group_id 2516 AND g.group_id = ug.group_id 2517 AND NOT (ug.group_leader = 1 AND g.group_skip_auth = 1) 2518 AND ' . $db->sql_in_set('ug.user_id', $ug_id_ary) . " 2519 AND ug.user_pending = 0 2520 AND o.auth_option " . $db->sql_like_expression('m_' . $db->get_any_char()), 2521 ); 2522 $sql = $db->sql_build_query('SELECT', $sql_ary_deny); 2523 $result = $db->sql_query($sql); 2524 2525 while ($row = $db->sql_fetchrow($result)) 2526 { 2527 if (isset($hold_ary[$row['user_id']][$row['forum_id']])) 2528 { 2529 unset($hold_ary[$row['user_id']][$row['forum_id']]); 2530 } 2531 } 2532 $db->sql_freeresult($result); 2533 2534 if (count($hold_ary)) 2535 { 2536 // Get usernames... 2537 $sql = 'SELECT user_id, username 2538 FROM ' . USERS_TABLE . ' 2539 WHERE ' . $db->sql_in_set('user_id', array_keys($hold_ary)); 2540 $result = $db->sql_query($sql); 2541 2542 $usernames_ary = array(); 2543 while ($row = $db->sql_fetchrow($result)) 2544 { 2545 $usernames_ary[$row['user_id']] = $row['username']; 2546 } 2547 $db->sql_freeresult($result); 2548 2549 foreach ($hold_ary as $user_id => $forum_id_ary) 2550 { 2551 // Do not continue if user does not exist 2552 if (!isset($usernames_ary[$user_id])) 2553 { 2554 continue; 2555 } 2556 2557 foreach ($forum_id_ary as $forum_id => $auth_ary) 2558 { 2559 $sql_ary[] = array( 2560 'forum_id' => (int) $forum_id, 2561 'user_id' => (int) $user_id, 2562 'username' => (string) $usernames_ary[$user_id], 2563 'group_id' => 0, 2564 'group_name' => '' 2565 ); 2566 } 2567 } 2568 } 2569 } 2570 2571 // Now to the groups... 2572 $hold_ary = $auth->acl_group_raw_data(false, 'm_%', false); 2573 2574 if (!empty($hold_ary)) 2575 { 2576 $ug_id_ary = array_keys($hold_ary); 2577 2578 // Make sure not hidden or special groups are involved... 2579 $sql = 'SELECT group_name, group_id, group_type 2580 FROM ' . GROUPS_TABLE . ' 2581 WHERE ' . $db->sql_in_set('group_id', $ug_id_ary); 2582 $result = $db->sql_query($sql); 2583 2584 $groupnames_ary = array(); 2585 while ($row = $db->sql_fetchrow($result)) 2586 { 2587 if ($row['group_type'] == GROUP_HIDDEN || $row['group_type'] == GROUP_SPECIAL) 2588 { 2589 unset($hold_ary[$row['group_id']]); 2590 } 2591 2592 $groupnames_ary[$row['group_id']] = $row['group_name']; 2593 } 2594 $db->sql_freeresult($result); 2595 2596 foreach ($hold_ary as $group_id => $forum_id_ary) 2597 { 2598 // If there is no group, we do not assign it... 2599 if (!isset($groupnames_ary[$group_id])) 2600 { 2601 continue; 2602 } 2603 2604 foreach ($forum_id_ary as $forum_id => $auth_ary) 2605 { 2606 $flag = false; 2607 foreach ($auth_ary as $auth_option => $setting) 2608 { 2609 // Make sure at least one ACL_YES option is set... 2610 if ($setting == ACL_YES) 2611 { 2612 $flag = true; 2613 break; 2614 } 2615 } 2616 2617 if (!$flag) 2618 { 2619 continue; 2620 } 2621 2622 $sql_ary[] = array( 2623 'forum_id' => (int) $forum_id, 2624 'user_id' => 0, 2625 'username' => '', 2626 'group_id' => (int) $group_id, 2627 'group_name' => (string) $groupnames_ary[$group_id] 2628 ); 2629 } 2630 } 2631 } 2632 2633 $db->sql_multi_insert(MODERATOR_CACHE_TABLE, $sql_ary); 2634 } 2635 2636 /** 2637 * View log 2638 * 2639 * @param string $mode The mode defines which log_type is used and from which log the entry is retrieved 2640 * @param array &$log The result array with the logs 2641 * @param mixed &$log_count If $log_count is set to false, we will skip counting all entries in the database. 2642 * Otherwise an integer with the number of total matching entries is returned. 2643 * @param int $limit Limit the number of entries that are returned 2644 * @param int $offset Offset when fetching the log entries, f.e. when paginating 2645 * @param mixed $forum_id Restrict the log entries to the given forum_id (can also be an array of forum_ids) 2646 * @param int $topic_id Restrict the log entries to the given topic_id 2647 * @param int $user_id Restrict the log entries to the given user_id 2648 * @param int $log_time Only get log entries newer than the given timestamp 2649 * @param string $sort_by SQL order option, e.g. 'l.log_time DESC' 2650 * @param string $keywords Will only return log entries that have the keywords in log_operation or log_data 2651 * 2652 * @return int Returns the offset of the last valid page, if the specified offset was invalid (too high) 2653 */ 2654 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 = '') 2655 { 2656 global $phpbb_log; 2657 2658 $count_logs = ($log_count !== false); 2659 2660 $log = $phpbb_log->get_logs($mode, $count_logs, $limit, $offset, $forum_id, $topic_id, $user_id, $limit_days, $sort_by, $keywords); 2661 $log_count = $phpbb_log->get_log_count(); 2662 2663 return $phpbb_log->get_valid_offset(); 2664 } 2665 2666 /** 2667 * Removes moderators and administrators from foe lists. 2668 * 2669 * @param \phpbb\db\driver\driver_interface $db Database connection 2670 * @param \phpbb\auth\auth $auth Authentication object 2671 * @param array|bool $group_id If an array, remove all members of this group from foe lists, or false to ignore 2672 * @param array|bool $user_id If an array, remove this user from foe lists, or false to ignore 2673 * @return null 2674 */ 2675 function phpbb_update_foes($db, $auth, $group_id = false, $user_id = false) 2676 { 2677 // update foes for some user 2678 if (is_array($user_id) && count($user_id)) 2679 { 2680 $sql = 'DELETE FROM ' . ZEBRA_TABLE . ' 2681 WHERE ' . $db->sql_in_set('zebra_id', $user_id) . ' 2682 AND foe = 1'; 2683 $db->sql_query($sql); 2684 return; 2685 } 2686 2687 // update foes for some group 2688 if (is_array($group_id) && count($group_id)) 2689 { 2690 // Grab group settings... 2691 $sql_ary = array( 2692 'SELECT' => 'a.group_id', 2693 2694 'FROM' => array( 2695 ACL_OPTIONS_TABLE => 'ao', 2696 ACL_GROUPS_TABLE => 'a', 2697 ), 2698 2699 'LEFT_JOIN' => array( 2700 array( 2701 'FROM' => array(ACL_ROLES_DATA_TABLE => 'r'), 2702 'ON' => 'a.auth_role_id = r.role_id', 2703 ), 2704 ), 2705 2706 'WHERE' => '(ao.auth_option_id = a.auth_option_id OR ao.auth_option_id = r.auth_option_id) 2707 AND ' . $db->sql_in_set('a.group_id', $group_id) . " 2708 AND ao.auth_option IN ('a_', 'm_')", 2709 2710 'GROUP_BY' => 'a.group_id', 2711 ); 2712 $sql = $db->sql_build_query('SELECT', $sql_ary); 2713 $result = $db->sql_query($sql); 2714 2715 $groups = array(); 2716 while ($row = $db->sql_fetchrow($result)) 2717 { 2718 $groups[] = (int) $row['group_id']; 2719 } 2720 $db->sql_freeresult($result); 2721 2722 if (!count($groups)) 2723 { 2724 return; 2725 } 2726 2727 switch ($db->get_sql_layer()) 2728 { 2729 case 'mysqli': 2730 case 'mysql4': 2731 $sql = 'DELETE ' . (($db->get_sql_layer() === 'mysqli' || version_compare($db->sql_server_info(true), '4.1', '>=')) ? 'z.*' : ZEBRA_TABLE) . ' 2732 FROM ' . ZEBRA_TABLE . ' z, ' . USER_GROUP_TABLE . ' ug 2733 WHERE z.zebra_id = ug.user_id 2734 AND z.foe = 1 2735 AND ' . $db->sql_in_set('ug.group_id', $groups); 2736 $db->sql_query($sql); 2737 break; 2738 2739 default: 2740 $sql = 'SELECT user_id 2741 FROM ' . USER_GROUP_TABLE . ' 2742 WHERE ' . $db->sql_in_set('group_id', $groups); 2743 $result = $db->sql_query($sql); 2744 2745 $users = array(); 2746 while ($row = $db->sql_fetchrow($result)) 2747 { 2748 $users[] = (int) $row['user_id']; 2749 } 2750 $db->sql_freeresult($result); 2751 2752 if (count($users)) 2753 { 2754 $sql = 'DELETE FROM ' . ZEBRA_TABLE . ' 2755 WHERE ' . $db->sql_in_set('zebra_id', $users) . ' 2756 AND foe = 1'; 2757 $db->sql_query($sql); 2758 } 2759 break; 2760 } 2761 2762 return; 2763 } 2764 2765 // update foes for everyone 2766 $perms = array(); 2767 foreach ($auth->acl_get_list(false, array('a_', 'm_'), false) as $forum_id => $forum_ary) 2768 { 2769 foreach ($forum_ary as $auth_option => $user_ary) 2770 { 2771 $perms = array_merge($perms, $user_ary); 2772 } 2773 } 2774 2775 if (count($perms)) 2776 { 2777 $sql = 'DELETE FROM ' . ZEBRA_TABLE . ' 2778 WHERE ' . $db->sql_in_set('zebra_id', array_unique($perms)) . ' 2779 AND foe = 1'; 2780 $db->sql_query($sql); 2781 } 2782 unset($perms); 2783 } 2784 2785 /** 2786 * Lists inactive users 2787 */ 2788 function view_inactive_users(&$users, &$user_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'user_inactive_time DESC') 2789 { 2790 global $db, $user; 2791 2792 $sql = 'SELECT COUNT(user_id) AS user_count 2793 FROM ' . USERS_TABLE . ' 2794 WHERE user_type = ' . USER_INACTIVE . 2795 (($limit_days) ? " AND user_inactive_time >= $limit_days" : ''); 2796 $result = $db->sql_query($sql); 2797 $user_count = (int) $db->sql_fetchfield('user_count'); 2798 $db->sql_freeresult($result); 2799 2800 if ($user_count == 0) 2801 { 2802 // Save the queries, because there are no users to display 2803 return 0; 2804 } 2805 2806 if ($offset >= $user_count) 2807 { 2808 $offset = ($offset - $limit < 0) ? 0 : $offset - $limit; 2809 } 2810 2811 $sql = 'SELECT * 2812 FROM ' . USERS_TABLE . ' 2813 WHERE user_type = ' . USER_INACTIVE . 2814 (($limit_days) ? " AND user_inactive_time >= $limit_days" : '') . " 2815 ORDER BY $sort_by"; 2816 $result = $db->sql_query_limit($sql, $limit, $offset); 2817 2818 while ($row = $db->sql_fetchrow($result)) 2819 { 2820 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_UNKNOWN']; 2821 switch ($row['user_inactive_reason']) 2822 { 2823 case INACTIVE_REGISTER: 2824 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_REGISTER']; 2825 break; 2826 2827 case INACTIVE_PROFILE: 2828 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_PROFILE']; 2829 break; 2830 2831 case INACTIVE_MANUAL: 2832 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_MANUAL']; 2833 break; 2834 2835 case INACTIVE_REMIND: 2836 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_REMIND']; 2837 break; 2838 } 2839 2840 $users[] = $row; 2841 } 2842 $db->sql_freeresult($result); 2843 2844 return $offset; 2845 } 2846 2847 /** 2848 * Lists warned users 2849 */ 2850 function view_warned_users(&$users, &$user_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'user_warnings DESC') 2851 { 2852 global $db; 2853 2854 $sql = 'SELECT user_id, username, user_colour, user_warnings, user_last_warning 2855 FROM ' . USERS_TABLE . ' 2856 WHERE user_warnings > 0 2857 ' . (($limit_days) ? "AND user_last_warning >= $limit_days" : '') . " 2858 ORDER BY $sort_by"; 2859 $result = $db->sql_query_limit($sql, $limit, $offset); 2860 $users = $db->sql_fetchrowset($result); 2861 $db->sql_freeresult($result); 2862 2863 $sql = 'SELECT count(user_id) AS user_count 2864 FROM ' . USERS_TABLE . ' 2865 WHERE user_warnings > 0 2866 ' . (($limit_days) ? "AND user_last_warning >= $limit_days" : ''); 2867 $result = $db->sql_query($sql); 2868 $user_count = (int) $db->sql_fetchfield('user_count'); 2869 $db->sql_freeresult($result); 2870 2871 return; 2872 } 2873 2874 /** 2875 * Get database size 2876 * Currently only mysql and mssql are supported 2877 */ 2878 function get_database_size() 2879 { 2880 global $db, $user, $table_prefix; 2881 2882 $database_size = false; 2883 2884 // This code is heavily influenced by a similar routine in phpMyAdmin 2.2.0 2885 switch ($db->get_sql_layer()) 2886 { 2887 case 'mysql': 2888 case 'mysql4': 2889 case 'mysqli': 2890 $sql = 'SELECT VERSION() AS mysql_version'; 2891 $result = $db->sql_query($sql); 2892 $row = $db->sql_fetchrow($result); 2893 $db->sql_freeresult($result); 2894 2895 if ($row) 2896 { 2897 $version = $row['mysql_version']; 2898 2899 if (preg_match('#(3\.23|[45]\.|10\.[0-9]\.[0-9]{1,2}-+Maria)#', $version)) 2900 { 2901 $db_name = (preg_match('#^(?:3\.23\.(?:[6-9]|[1-9]{2}))|[45]\.|10\.[0-9]\.[0-9]{1,2}-+Maria#', $version)) ? "`{$db->get_db_name()}`" : $db->get_db_name(); 2902 2903 $sql = 'SHOW TABLE STATUS 2904 FROM ' . $db_name; 2905 $result = $db->sql_query($sql, 7200); 2906 2907 $database_size = 0; 2908 while ($row = $db->sql_fetchrow($result)) 2909 { 2910 if ((isset($row['Type']) && $row['Type'] != 'MRG_MyISAM') || (isset($row['Engine']) && ($row['Engine'] == 'MyISAM' || $row['Engine'] == 'InnoDB' || $row['Engine'] == 'Aria'))) 2911 { 2912 if ($table_prefix != '') 2913 { 2914 if (strpos($row['Name'], $table_prefix) !== false) 2915 { 2916 $database_size += $row['Data_length'] + $row['Index_length']; 2917 } 2918 } 2919 else 2920 { 2921 $database_size += $row['Data_length'] + $row['Index_length']; 2922 } 2923 } 2924 } 2925 $db->sql_freeresult($result); 2926 } 2927 } 2928 break; 2929 2930 case 'sqlite3': 2931 global $dbhost; 2932 2933 if (file_exists($dbhost)) 2934 { 2935 $database_size = filesize($dbhost); 2936 } 2937 2938 break; 2939 2940 case 'mssql_odbc': 2941 case 'mssqlnative': 2942 $sql = 'SELECT @@VERSION AS mssql_version'; 2943 $result = $db->sql_query($sql); 2944 $row = $db->sql_fetchrow($result); 2945 $db->sql_freeresult($result); 2946 2947 $sql = 'SELECT ((SUM(size) * 8.0) * 1024.0) as dbsize 2948 FROM sysfiles'; 2949 2950 if ($row) 2951 { 2952 // Azure stats are stored elsewhere 2953 if (strpos($row['mssql_version'], 'SQL Azure') !== false) 2954 { 2955 $sql = 'SELECT ((SUM(reserved_page_count) * 8.0) * 1024.0) as dbsize 2956 FROM sys.dm_db_partition_stats'; 2957 } 2958 } 2959 2960 $result = $db->sql_query($sql, 7200); 2961 $database_size = ($row = $db->sql_fetchrow($result)) ? $row['dbsize'] : false; 2962 $db->sql_freeresult($result); 2963 break; 2964 2965 case 'postgres': 2966 $sql = "SELECT proname 2967 FROM pg_proc 2968 WHERE proname = 'pg_database_size'"; 2969 $result = $db->sql_query($sql); 2970 $row = $db->sql_fetchrow($result); 2971 $db->sql_freeresult($result); 2972 2973 if ($row['proname'] == 'pg_database_size') 2974 { 2975 $database = $db->get_db_name(); 2976 if (strpos($database, '.') !== false) 2977 { 2978 list($database, ) = explode('.', $database); 2979 } 2980 2981 $sql = "SELECT oid 2982 FROM pg_database 2983 WHERE datname = '$database'"; 2984 $result = $db->sql_query($sql); 2985 $row = $db->sql_fetchrow($result); 2986 $db->sql_freeresult($result); 2987 2988 $oid = $row['oid']; 2989 2990 $sql = 'SELECT pg_database_size(' . $oid . ') as size'; 2991 $result = $db->sql_query($sql); 2992 $row = $db->sql_fetchrow($result); 2993 $db->sql_freeresult($result); 2994 2995 $database_size = $row['size']; 2996 } 2997 break; 2998 2999 case 'oracle': 3000 $sql = 'SELECT SUM(bytes) as dbsize 3001 FROM user_segments'; 3002 $result = $db->sql_query($sql, 7200); 3003 $database_size = ($row = $db->sql_fetchrow($result)) ? $row['dbsize'] : false; 3004 $db->sql_freeresult($result); 3005 break; 3006 } 3007 3008 $database_size = ($database_size !== false) ? get_formatted_filesize($database_size) : $user->lang['NOT_AVAILABLE']; 3009 3010 return $database_size; 3011 } 3012 3013 /* 3014 * Tidy Warnings 3015 * Remove all warnings which have now expired from the database 3016 * The duration of a warning can be defined by the administrator 3017 * This only removes the warning and reduces the associated count, 3018 * it does not remove the user note recording the contents of the warning 3019 */ 3020 function tidy_warnings() 3021 { 3022 global $db, $config; 3023 3024 $expire_date = time() - ($config['warnings_expire_days'] * 86400); 3025 $warning_list = $user_list = array(); 3026 3027 $sql = 'SELECT * FROM ' . WARNINGS_TABLE . " 3028 WHERE warning_time < $expire_date"; 3029 $result = $db->sql_query($sql); 3030 3031 while ($row = $db->sql_fetchrow($result)) 3032 { 3033 $warning_list[] = $row['warning_id']; 3034 $user_list[$row['user_id']] = isset($user_list[$row['user_id']]) ? ++$user_list[$row['user_id']] : 1; 3035 } 3036 $db->sql_freeresult($result); 3037 3038 if (count($warning_list)) 3039 { 3040 $db->sql_transaction('begin'); 3041 3042 $sql = 'DELETE FROM ' . WARNINGS_TABLE . ' 3043 WHERE ' . $db->sql_in_set('warning_id', $warning_list); 3044 $db->sql_query($sql); 3045 3046 foreach ($user_list as $user_id => $value) 3047 { 3048 $sql = 'UPDATE ' . USERS_TABLE . " SET user_warnings = user_warnings - $value 3049 WHERE user_id = $user_id"; 3050 $db->sql_query($sql); 3051 } 3052 3053 $db->sql_transaction('commit'); 3054 } 3055 3056 $config->set('warnings_last_gc', time(), false); 3057 } 3058 3059 /** 3060 * Tidy database, doing some maintanance tasks 3061 */ 3062 function tidy_database() 3063 { 3064 global $config, $db; 3065 3066 // Here we check permission consistency 3067 3068 // Sometimes, it can happen permission tables having forums listed which do not exist 3069 $sql = 'SELECT forum_id 3070 FROM ' . FORUMS_TABLE; 3071 $result = $db->sql_query($sql); 3072 3073 $forum_ids = array(0); 3074 while ($row = $db->sql_fetchrow($result)) 3075 { 3076 $forum_ids[] = $row['forum_id']; 3077 } 3078 $db->sql_freeresult($result); 3079 3080 $db->sql_transaction('begin'); 3081 3082 // Delete those rows from the acl tables not having listed the forums above 3083 $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . ' 3084 WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true); 3085 $db->sql_query($sql); 3086 3087 $sql = 'DELETE FROM ' . ACL_USERS_TABLE . ' 3088 WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true); 3089 $db->sql_query($sql); 3090 3091 $db->sql_transaction('commit'); 3092 3093 $config->set('database_last_gc', time(), false); 3094 } 3095 3096 /** 3097 * Add permission language - this will make sure custom files will be included 3098 */ 3099 function add_permission_language() 3100 { 3101 global $user, $phpEx, $phpbb_extension_manager; 3102 3103 // add permission language files from extensions 3104 $finder = $phpbb_extension_manager->get_finder(); 3105 3106 $lang_files = $finder 3107 ->prefix('permissions_') 3108 ->suffix(".$phpEx") 3109 ->core_path('language/') 3110 ->extension_directory('/language') 3111 ->find(); 3112 3113 foreach ($lang_files as $lang_file => $ext_name) 3114 { 3115 if ($ext_name === '/') 3116 { 3117 $user->add_lang($lang_file); 3118 } 3119 else 3120 { 3121 $user->add_lang_ext($ext_name, $lang_file); 3122 } 3123 } 3124 } 3125 3126 /** 3127 * Enables a particular flag in a bitfield column of a given table. 3128 * 3129 * @param string $table_name The table to update 3130 * @param string $column_name The column containing a bitfield to update 3131 * @param int $flag The binary flag which is OR-ed with the current column value 3132 * @param string $sql_more This string is attached to the sql query generated to update the table. 3133 * 3134 * @return null 3135 */ 3136 function enable_bitfield_column_flag($table_name, $column_name, $flag, $sql_more = '') 3137 { 3138 global $db; 3139 3140 $sql = 'UPDATE ' . $table_name . ' 3141 SET ' . $column_name . ' = ' . $db->sql_bit_or($column_name, $flag) . ' 3142 ' . $sql_more; 3143 $db->sql_query($sql); 3144 } 3145 3146 function display_ban_end_options() 3147 { 3148 global $user, $template; 3149 3150 // Ban length options 3151 $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'] . ' -> '); 3152 3153 $ban_end_options = ''; 3154 foreach ($ban_end_text as $length => $text) 3155 { 3156 $ban_end_options .= '<option value="' . $length . '">' . $text . '</option>'; 3157 } 3158 3159 $template->assign_vars(array( 3160 'S_BAN_END_OPTIONS' => $ban_end_options 3161 )); 3162 } 3163 3164 /** 3165 * Display ban options 3166 */ 3167 function display_ban_options($mode) 3168 { 3169 global $user, $db, $template; 3170 3171 switch ($mode) 3172 { 3173 case 'user': 3174 3175 $field = 'username'; 3176 3177 $sql = 'SELECT b.*, u.user_id, u.username, u.username_clean 3178 FROM ' . BANLIST_TABLE . ' b, ' . USERS_TABLE . ' u 3179 WHERE (b.ban_end >= ' . time() . ' 3180 OR b.ban_end = 0) 3181 AND u.user_id = b.ban_userid 3182 ORDER BY u.username_clean ASC'; 3183 break; 3184 3185 case 'ip': 3186 3187 $field = 'ban_ip'; 3188 3189 $sql = 'SELECT * 3190 FROM ' . BANLIST_TABLE . ' 3191 WHERE (ban_end >= ' . time() . " 3192 OR ban_end = 0) 3193 AND ban_ip <> '' 3194 ORDER BY ban_ip"; 3195 break; 3196 3197 case 'email': 3198 3199 $field = 'ban_email'; 3200 3201 $sql = 'SELECT * 3202 FROM ' . BANLIST_TABLE . ' 3203 WHERE (ban_end >= ' . time() . " 3204 OR ban_end = 0) 3205 AND ban_email <> '' 3206 ORDER BY ban_email"; 3207 break; 3208 } 3209 $result = $db->sql_query($sql); 3210 3211 $banned_options = $excluded_options = array(); 3212 while ($row = $db->sql_fetchrow($result)) 3213 { 3214 $option = '<option value="' . $row['ban_id'] . '">' . $row[$field] . '</option>'; 3215 3216 if ($row['ban_exclude']) 3217 { 3218 $excluded_options[] = $option; 3219 } 3220 else 3221 { 3222 $banned_options[] = $option; 3223 } 3224 3225 $time_length = ($row['ban_end']) ? ($row['ban_end'] - $row['ban_start']) / 60 : 0; 3226 3227 if ($time_length == 0) 3228 { 3229 // Banned permanently 3230 $ban_length = $user->lang['PERMANENT']; 3231 } 3232 else if (isset($ban_end_text[$time_length])) 3233 { 3234 // Banned for a given duration 3235 $ban_length = $user->lang('BANNED_UNTIL_DURATION', $ban_end_text[$time_length], $user->format_date($row['ban_end'], false, true)); 3236 } 3237 else 3238 { 3239 // Banned until given date 3240 $ban_length = $user->lang('BANNED_UNTIL_DATE', $user->format_date($row['ban_end'], false, true)); 3241 } 3242 3243 $template->assign_block_vars('bans', array( 3244 'BAN_ID' => (int) $row['ban_id'], 3245 'LENGTH' => $ban_length, 3246 'A_LENGTH' => addslashes($ban_length), 3247 'REASON' => $row['ban_reason'], 3248 'A_REASON' => addslashes($row['ban_reason']), 3249 'GIVE_REASON' => $row['ban_give_reason'], 3250 'A_GIVE_REASON' => addslashes($row['ban_give_reason']), 3251 )); 3252 } 3253 $db->sql_freeresult($result); 3254 3255 $options = ''; 3256 if ($excluded_options) 3257 { 3258 $options .= '<optgroup label="' . $user->lang['OPTIONS_EXCLUDED'] . '">'; 3259 $options .= implode('', $excluded_options); 3260 $options .= '</optgroup>'; 3261 } 3262 3263 if ($banned_options) 3264 { 3265 $options .= '<optgroup label="' . $user->lang['OPTIONS_BANNED'] . '">'; 3266 $options .= implode('', $banned_options); 3267 $options .= '</optgroup>'; 3268 } 3269 3270 $template->assign_vars(array( 3271 'S_BANNED_OPTIONS' => ($banned_options || $excluded_options) ? true : false, 3272 'BANNED_OPTIONS' => $options, 3273 )); 3274 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |