[ Index ] |
PHP Cross Reference of phpBB-3.3.14-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|svg|webp') 483 { 484 $matches = array($dir => array()); 485 486 // Remove initial / if present 487 $rootdir = (substr($rootdir, 0, 1) == '/') ? substr($rootdir, 1) : $rootdir; 488 // Add closing / if not present 489 $rootdir = ($rootdir && substr($rootdir, -1) != '/') ? $rootdir . '/' : $rootdir; 490 491 // Remove initial / if present 492 $dir = (substr($dir, 0, 1) == '/') ? substr($dir, 1) : $dir; 493 // Add closing / if not present 494 $dir = ($dir && substr($dir, -1) != '/') ? $dir . '/' : $dir; 495 496 if (!is_dir($rootdir . $dir)) 497 { 498 return $matches; 499 } 500 501 $dh = @opendir($rootdir . $dir); 502 503 if (!$dh) 504 { 505 return $matches; 506 } 507 508 while (($fname = readdir($dh)) !== false) 509 { 510 if (is_file("$rootdir$dir$fname")) 511 { 512 if (filesize("$rootdir$dir$fname") && preg_match('#\.' . $type . '$#i', $fname)) 513 { 514 $matches[$dir][] = $fname; 515 } 516 } 517 else if ($fname[0] != '.' && is_dir("$rootdir$dir$fname")) 518 { 519 $matches += filelist($rootdir, "$dir$fname", $type); 520 } 521 } 522 closedir($dh); 523 524 return $matches; 525 } 526 527 /** 528 * Move topic(s) 529 */ 530 function move_topics($topic_ids, $forum_id, $auto_sync = true) 531 { 532 global $db, $phpbb_dispatcher; 533 534 if (empty($topic_ids)) 535 { 536 return; 537 } 538 539 $forum_ids = array($forum_id); 540 541 if (!is_array($topic_ids)) 542 { 543 $topic_ids = array($topic_ids); 544 } 545 546 /** 547 * Perform additional actions before topics move 548 * 549 * @event core.move_topics_before 550 * @var array topic_ids Array of the moved topic ids 551 * @var string forum_id The forum id from where the topics are moved 552 * @since 3.2.9-RC1 553 */ 554 $vars = array( 555 'topic_ids', 556 'forum_id', 557 ); 558 extract($phpbb_dispatcher->trigger_event('core.move_topics_before', compact($vars))); 559 560 $sql = 'DELETE FROM ' . TOPICS_TABLE . ' 561 WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids) . ' 562 AND forum_id = ' . $forum_id; 563 $db->sql_query($sql); 564 565 if ($auto_sync) 566 { 567 $sql = 'SELECT DISTINCT forum_id 568 FROM ' . TOPICS_TABLE . ' 569 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 570 $result = $db->sql_query($sql); 571 572 while ($row = $db->sql_fetchrow($result)) 573 { 574 $forum_ids[] = $row['forum_id']; 575 } 576 $db->sql_freeresult($result); 577 } 578 579 $table_ary = array(TOPICS_TABLE, POSTS_TABLE, LOG_TABLE, DRAFTS_TABLE, TOPICS_TRACK_TABLE); 580 581 /** 582 * Perform additional actions before topics move 583 * 584 * @event core.move_topics_before_query 585 * @var array table_ary Array of tables from which forum_id will be updated for all rows that hold the moved topics 586 * @var array topic_ids Array of the moved topic ids 587 * @var string forum_id The forum id from where the topics are moved 588 * @var array forum_ids Array of the forums where the topics are moving (includes also forum_id) 589 * @var bool auto_sync Whether or not to perform auto sync 590 * @since 3.1.5-RC1 591 */ 592 $vars = array( 593 'table_ary', 594 'topic_ids', 595 'forum_id', 596 'forum_ids', 597 'auto_sync', 598 ); 599 extract($phpbb_dispatcher->trigger_event('core.move_topics_before_query', compact($vars))); 600 601 foreach ($table_ary as $table) 602 { 603 $sql = "UPDATE $table 604 SET forum_id = $forum_id 605 WHERE " . $db->sql_in_set('topic_id', $topic_ids); 606 $db->sql_query($sql); 607 } 608 unset($table_ary); 609 610 /** 611 * Perform additional actions after topics move 612 * 613 * @event core.move_topics_after 614 * @var array topic_ids Array of the moved topic ids 615 * @var string forum_id The forum id from where the topics were moved 616 * @var array forum_ids Array of the forums where the topics were moved (includes also forum_id) 617 * @since 3.2.9-RC1 618 */ 619 $vars = array( 620 'topic_ids', 621 'forum_id', 622 'forum_ids', 623 ); 624 extract($phpbb_dispatcher->trigger_event('core.move_topics_after', compact($vars))); 625 626 if ($auto_sync) 627 { 628 sync('forum', 'forum_id', $forum_ids, true, true); 629 unset($forum_ids); 630 } 631 } 632 633 /** 634 * Move post(s) 635 */ 636 function move_posts($post_ids, $topic_id, $auto_sync = true) 637 { 638 global $db, $phpbb_dispatcher; 639 640 if (!is_array($post_ids)) 641 { 642 $post_ids = array($post_ids); 643 } 644 645 $forum_ids = array(); 646 $topic_ids = array($topic_id); 647 648 $sql = 'SELECT DISTINCT topic_id, forum_id 649 FROM ' . POSTS_TABLE . ' 650 WHERE ' . $db->sql_in_set('post_id', $post_ids); 651 $result = $db->sql_query($sql); 652 653 while ($row = $db->sql_fetchrow($result)) 654 { 655 $forum_ids[] = (int) $row['forum_id']; 656 $topic_ids[] = (int) $row['topic_id']; 657 } 658 $db->sql_freeresult($result); 659 660 $sql = 'SELECT forum_id 661 FROM ' . TOPICS_TABLE . ' 662 WHERE topic_id = ' . $topic_id; 663 $result = $db->sql_query($sql); 664 $forum_row = $db->sql_fetchrow($result); 665 $db->sql_freeresult($result); 666 667 if (!$forum_row) 668 { 669 trigger_error('NO_TOPIC'); 670 } 671 672 /** 673 * Perform additional actions before moving posts 674 * 675 * @event core.move_posts_before 676 * @var array post_ids Array of post ids to move 677 * @var int topic_id The topic id the posts are moved to 678 * @var bool auto_sync Whether or not to perform auto sync 679 * @var array forum_ids Array of the forum ids the posts are moved from 680 * @var array topic_ids Array of the topic ids the posts are moved from 681 * @var array forum_row Array with the forum id of the topic the posts are moved to 682 * @since 3.1.7-RC1 683 */ 684 $vars = array( 685 'post_ids', 686 'topic_id', 687 'auto_sync', 688 'forum_ids', 689 'topic_ids', 690 'forum_row', 691 ); 692 extract($phpbb_dispatcher->trigger_event('core.move_posts_before', compact($vars))); 693 694 $sql = 'UPDATE ' . POSTS_TABLE . ' 695 SET forum_id = ' . (int) $forum_row['forum_id'] . ", topic_id = $topic_id 696 WHERE " . $db->sql_in_set('post_id', $post_ids); 697 $db->sql_query($sql); 698 699 $sql = 'UPDATE ' . ATTACHMENTS_TABLE . " 700 SET topic_id = $topic_id, in_message = 0 701 WHERE " . $db->sql_in_set('post_msg_id', $post_ids); 702 $db->sql_query($sql); 703 704 /** 705 * Perform additional actions after moving posts 706 * 707 * @event core.move_posts_after 708 * @var array post_ids Array of the moved post ids 709 * @var int topic_id The topic id the posts are moved to 710 * @var bool auto_sync Whether or not to perform auto sync 711 * @var array forum_ids Array of the forum ids the posts are moved from 712 * @var array topic_ids Array of the topic ids the posts are moved from 713 * @var array forum_row Array with the forum id of the topic the posts are moved to 714 * @since 3.1.7-RC1 715 */ 716 $vars = array( 717 'post_ids', 718 'topic_id', 719 'auto_sync', 720 'forum_ids', 721 'topic_ids', 722 'forum_row', 723 ); 724 extract($phpbb_dispatcher->trigger_event('core.move_posts_after', compact($vars))); 725 726 if ($auto_sync) 727 { 728 $forum_ids[] = (int) $forum_row['forum_id']; 729 730 sync('topic_reported', 'topic_id', $topic_ids); 731 sync('topic_attachment', 'topic_id', $topic_ids); 732 sync('topic', 'topic_id', $topic_ids, true); 733 sync('forum', 'forum_id', $forum_ids, true, true); 734 735 /** 736 * Perform additional actions after move post sync 737 * 738 * @event core.move_posts_sync_after 739 * @var array post_ids Array of the moved post ids 740 * @var int topic_id The topic id the posts are moved to 741 * @var bool auto_sync Whether or not to perform auto sync 742 * @var array forum_ids Array of the forum ids the posts are moved from 743 * @var array topic_ids Array of the topic ids the posts are moved from 744 * @var array forum_row Array with the forum id of the topic the posts are moved to 745 * @since 3.1.11-RC1 746 */ 747 $vars = array( 748 'post_ids', 749 'topic_id', 750 'auto_sync', 751 'forum_ids', 752 'topic_ids', 753 'forum_row', 754 ); 755 extract($phpbb_dispatcher->trigger_event('core.move_posts_sync_after', compact($vars))); 756 } 757 758 // Update posted information 759 update_posted_info($topic_ids); 760 } 761 762 /** 763 * Remove topic(s) 764 */ 765 function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_sync = true, $call_delete_posts = true) 766 { 767 global $db, $config, $phpbb_container, $phpbb_dispatcher; 768 769 $approved_topics = 0; 770 $forum_ids = $topic_ids = array(); 771 772 if ($where_type === 'range') 773 { 774 $where_clause = $where_ids; 775 } 776 else 777 { 778 $where_ids = (is_array($where_ids)) ? array_unique($where_ids) : array($where_ids); 779 780 if (!count($where_ids)) 781 { 782 return array('topics' => 0, 'posts' => 0); 783 } 784 785 $where_clause = $db->sql_in_set($where_type, $where_ids); 786 } 787 788 // Making sure that delete_posts does not call delete_topics again... 789 $return = array( 790 'posts' => ($call_delete_posts) ? delete_posts($where_type, $where_ids, false, true, $post_count_sync, false) : 0, 791 ); 792 793 $sql = 'SELECT topic_id, forum_id, topic_visibility, topic_moved_id 794 FROM ' . TOPICS_TABLE . ' 795 WHERE ' . $where_clause; 796 $result = $db->sql_query($sql); 797 798 while ($row = $db->sql_fetchrow($result)) 799 { 800 $forum_ids[] = $row['forum_id']; 801 $topic_ids[] = $row['topic_id']; 802 803 if ($row['topic_visibility'] == ITEM_APPROVED && !$row['topic_moved_id']) 804 { 805 $approved_topics++; 806 } 807 } 808 $db->sql_freeresult($result); 809 810 $return['topics'] = count($topic_ids); 811 812 if (!count($topic_ids)) 813 { 814 return $return; 815 } 816 817 $db->sql_transaction('begin'); 818 819 $table_ary = array(BOOKMARKS_TABLE, TOPICS_TRACK_TABLE, TOPICS_POSTED_TABLE, POLL_VOTES_TABLE, POLL_OPTIONS_TABLE, TOPICS_WATCH_TABLE, TOPICS_TABLE); 820 821 /** 822 * Perform additional actions before topic(s) deletion 823 * 824 * @event core.delete_topics_before_query 825 * @var array table_ary Array of tables from which all rows will be deleted that hold a topic_id occuring in topic_ids 826 * @var array topic_ids Array of topic ids to delete 827 * @since 3.1.4-RC1 828 */ 829 $vars = array( 830 'table_ary', 831 'topic_ids', 832 ); 833 extract($phpbb_dispatcher->trigger_event('core.delete_topics_before_query', compact($vars))); 834 835 foreach ($table_ary as $table) 836 { 837 $sql = "DELETE FROM $table 838 WHERE " . $db->sql_in_set('topic_id', $topic_ids); 839 $db->sql_query($sql); 840 } 841 unset($table_ary); 842 843 /** 844 * Perform additional actions after topic(s) deletion 845 * 846 * @event core.delete_topics_after_query 847 * @var array topic_ids Array of topic ids that were deleted 848 * @since 3.1.4-RC1 849 */ 850 $vars = array( 851 'topic_ids', 852 ); 853 extract($phpbb_dispatcher->trigger_event('core.delete_topics_after_query', compact($vars))); 854 855 $moved_topic_ids = array(); 856 857 // update the other forums 858 $sql = 'SELECT topic_id, forum_id 859 FROM ' . TOPICS_TABLE . ' 860 WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids); 861 $result = $db->sql_query($sql); 862 863 while ($row = $db->sql_fetchrow($result)) 864 { 865 $forum_ids[] = $row['forum_id']; 866 $moved_topic_ids[] = $row['topic_id']; 867 } 868 $db->sql_freeresult($result); 869 870 if (count($moved_topic_ids)) 871 { 872 $sql = 'DELETE FROM ' . TOPICS_TABLE . ' 873 WHERE ' . $db->sql_in_set('topic_id', $moved_topic_ids); 874 $db->sql_query($sql); 875 } 876 877 $db->sql_transaction('commit'); 878 879 if ($auto_sync) 880 { 881 sync('forum', 'forum_id', array_unique($forum_ids), true, true); 882 sync('topic_reported', $where_type, $where_ids); 883 } 884 885 if ($approved_topics) 886 { 887 $config->increment('num_topics', $approved_topics * (-1), false); 888 } 889 890 /* @var $phpbb_notifications \phpbb\notification\manager */ 891 $phpbb_notifications = $phpbb_container->get('notification_manager'); 892 893 $phpbb_notifications->delete_notifications(array( 894 'notification.type.topic', 895 'notification.type.approve_topic', 896 'notification.type.topic_in_queue', 897 ), $topic_ids); 898 899 return $return; 900 } 901 902 /** 903 * Remove post(s) 904 */ 905 function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync = true, $post_count_sync = true, $call_delete_topics = true) 906 { 907 global $db, $config, $phpbb_root_path, $phpEx, $auth, $user, $phpbb_container, $phpbb_dispatcher; 908 909 // Notifications types to delete 910 $delete_notifications_types = array( 911 'notification.type.quote', 912 'notification.type.approve_post', 913 'notification.type.post_in_queue', 914 'notification.type.report_post', 915 ); 916 917 /** 918 * Perform additional actions before post(s) deletion 919 * 920 * @event core.delete_posts_before 921 * @var string where_type Variable containing posts deletion mode 922 * @var mixed where_ids Array or comma separated list of posts ids to delete 923 * @var bool auto_sync Flag indicating if topics/forums should be synchronized 924 * @var bool posted_sync Flag indicating if topics_posted table should be resynchronized 925 * @var bool post_count_sync Flag indicating if posts count should be resynchronized 926 * @var bool call_delete_topics Flag indicating if topics having no posts should be deleted 927 * @var array delete_notifications_types Array with notifications types to delete 928 * @since 3.1.0-a4 929 */ 930 $vars = array( 931 'where_type', 932 'where_ids', 933 'auto_sync', 934 'posted_sync', 935 'post_count_sync', 936 'call_delete_topics', 937 'delete_notifications_types', 938 ); 939 extract($phpbb_dispatcher->trigger_event('core.delete_posts_before', compact($vars))); 940 941 if ($where_type === 'range') 942 { 943 $where_clause = $where_ids; 944 } 945 else 946 { 947 if (is_array($where_ids)) 948 { 949 $where_ids = array_unique($where_ids); 950 } 951 else 952 { 953 $where_ids = array($where_ids); 954 } 955 956 if (!count($where_ids)) 957 { 958 return false; 959 } 960 961 $where_ids = array_map('intval', $where_ids); 962 963 /* Possible code for splitting post deletion 964 if (count($where_ids) >= 1001) 965 { 966 // Split into chunks of 1000 967 $chunks = array_chunk($where_ids, 1000); 968 969 foreach ($chunks as $_where_ids) 970 { 971 delete_posts($where_type, $_where_ids, $auto_sync, $posted_sync, $post_count_sync, $call_delete_topics); 972 } 973 974 return; 975 }*/ 976 977 $where_clause = $db->sql_in_set($where_type, $where_ids); 978 } 979 980 $approved_posts = 0; 981 $post_ids = $topic_ids = $forum_ids = $post_counts = $remove_topics = array(); 982 983 $sql = 'SELECT post_id, poster_id, post_visibility, post_postcount, topic_id, forum_id 984 FROM ' . POSTS_TABLE . ' 985 WHERE ' . $where_clause; 986 $result = $db->sql_query($sql); 987 988 while ($row = $db->sql_fetchrow($result)) 989 { 990 $post_ids[] = (int) $row['post_id']; 991 $poster_ids[] = (int) $row['poster_id']; 992 $topic_ids[] = (int) $row['topic_id']; 993 $forum_ids[] = (int) $row['forum_id']; 994 995 if ($row['post_postcount'] && $post_count_sync && $row['post_visibility'] == ITEM_APPROVED) 996 { 997 $post_counts[$row['poster_id']] = (!empty($post_counts[$row['poster_id']])) ? $post_counts[$row['poster_id']] + 1 : 1; 998 } 999 1000 if ($row['post_visibility'] == ITEM_APPROVED) 1001 { 1002 $approved_posts++; 1003 } 1004 } 1005 $db->sql_freeresult($result); 1006 1007 if (!count($post_ids)) 1008 { 1009 return false; 1010 } 1011 1012 $db->sql_transaction('begin'); 1013 1014 $table_ary = array(POSTS_TABLE, REPORTS_TABLE); 1015 1016 /** 1017 * Perform additional actions during post(s) deletion before running the queries 1018 * 1019 * @event core.delete_posts_in_transaction_before 1020 * @var array post_ids Array with deleted posts' ids 1021 * @var array poster_ids Array with deleted posts' author ids 1022 * @var array topic_ids Array with deleted posts' topic ids 1023 * @var array forum_ids Array with deleted posts' forum ids 1024 * @var string where_type Variable containing posts deletion mode 1025 * @var mixed where_ids Array or comma separated list of post ids to delete 1026 * @var array delete_notifications_types Array with notifications types to delete 1027 * @var array table_ary Array with table names to delete data from 1028 * @since 3.1.7-RC1 1029 */ 1030 $vars = array( 1031 'post_ids', 1032 'poster_ids', 1033 'topic_ids', 1034 'forum_ids', 1035 'where_type', 1036 'where_ids', 1037 'delete_notifications_types', 1038 'table_ary', 1039 ); 1040 extract($phpbb_dispatcher->trigger_event('core.delete_posts_in_transaction_before', compact($vars))); 1041 1042 foreach ($table_ary as $table) 1043 { 1044 $sql = "DELETE FROM $table 1045 WHERE " . $db->sql_in_set('post_id', $post_ids); 1046 $db->sql_query($sql); 1047 } 1048 unset($table_ary); 1049 1050 // Adjust users post counts 1051 if (count($post_counts) && $post_count_sync) 1052 { 1053 foreach ($post_counts as $poster_id => $substract) 1054 { 1055 $sql = 'UPDATE ' . USERS_TABLE . ' 1056 SET user_posts = 0 1057 WHERE user_id = ' . $poster_id . ' 1058 AND user_posts < ' . $substract; 1059 $db->sql_query($sql); 1060 1061 $sql = 'UPDATE ' . USERS_TABLE . ' 1062 SET user_posts = user_posts - ' . $substract . ' 1063 WHERE user_id = ' . $poster_id . ' 1064 AND user_posts >= ' . $substract; 1065 $db->sql_query($sql); 1066 } 1067 } 1068 1069 // Remove topics now having no posts? 1070 if (count($topic_ids)) 1071 { 1072 $sql = 'SELECT topic_id 1073 FROM ' . POSTS_TABLE . ' 1074 WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . ' 1075 GROUP BY topic_id'; 1076 $result = $db->sql_query($sql); 1077 1078 while ($row = $db->sql_fetchrow($result)) 1079 { 1080 $remove_topics[] = $row['topic_id']; 1081 } 1082 $db->sql_freeresult($result); 1083 1084 // Actually, those not within remove_topics should be removed. ;) 1085 $remove_topics = array_diff($topic_ids, $remove_topics); 1086 } 1087 1088 // Remove the message from the search index 1089 $search_type = $config['search_type']; 1090 1091 if (!class_exists($search_type)) 1092 { 1093 trigger_error('NO_SUCH_SEARCH_MODULE'); 1094 } 1095 1096 $error = false; 1097 $search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher); 1098 1099 if ($error) 1100 { 1101 trigger_error($error); 1102 } 1103 1104 $search->index_remove($post_ids, $poster_ids, $forum_ids); 1105 1106 /** @var \phpbb\attachment\manager $attachment_manager */ 1107 $attachment_manager = $phpbb_container->get('attachment.manager'); 1108 $attachment_manager->delete('post', $post_ids, false); 1109 unset($attachment_manager); 1110 1111 /** 1112 * Perform additional actions during post(s) deletion 1113 * 1114 * @event core.delete_posts_in_transaction 1115 * @var array post_ids Array with deleted posts' ids 1116 * @var array poster_ids Array with deleted posts' author ids 1117 * @var array topic_ids Array with deleted posts' topic ids 1118 * @var array forum_ids Array with deleted posts' forum ids 1119 * @var string where_type Variable containing posts deletion mode 1120 * @var mixed where_ids Array or comma separated list of posts ids to delete 1121 * @var array delete_notifications_types Array with notifications types to delete 1122 * @since 3.1.0-a4 1123 */ 1124 $vars = array( 1125 'post_ids', 1126 'poster_ids', 1127 'topic_ids', 1128 'forum_ids', 1129 'where_type', 1130 'where_ids', 1131 'delete_notifications_types', 1132 ); 1133 extract($phpbb_dispatcher->trigger_event('core.delete_posts_in_transaction', compact($vars))); 1134 1135 $db->sql_transaction('commit'); 1136 1137 /** 1138 * Perform additional actions after post(s) deletion 1139 * 1140 * @event core.delete_posts_after 1141 * @var array post_ids Array with deleted posts' ids 1142 * @var array poster_ids Array with deleted posts' author ids 1143 * @var array topic_ids Array with deleted posts' topic ids 1144 * @var array forum_ids Array with deleted posts' forum ids 1145 * @var string where_type Variable containing posts deletion mode 1146 * @var mixed where_ids Array or comma separated list of posts ids to delete 1147 * @var array delete_notifications_types Array with notifications types to delete 1148 * @since 3.1.0-a4 1149 */ 1150 $vars = array( 1151 'post_ids', 1152 'poster_ids', 1153 'topic_ids', 1154 'forum_ids', 1155 'where_type', 1156 'where_ids', 1157 'delete_notifications_types', 1158 ); 1159 extract($phpbb_dispatcher->trigger_event('core.delete_posts_after', compact($vars))); 1160 1161 // Resync topics_posted table 1162 if ($posted_sync) 1163 { 1164 update_posted_info($topic_ids); 1165 } 1166 1167 if ($auto_sync) 1168 { 1169 sync('topic_reported', 'topic_id', $topic_ids); 1170 sync('topic', 'topic_id', $topic_ids, true); 1171 sync('forum', 'forum_id', $forum_ids, true, true); 1172 } 1173 1174 if ($approved_posts && $post_count_sync) 1175 { 1176 $config->increment('num_posts', $approved_posts * (-1), false); 1177 } 1178 1179 // We actually remove topics now to not be inconsistent (the delete_topics function calls this function too) 1180 if (count($remove_topics) && $call_delete_topics) 1181 { 1182 delete_topics('topic_id', $remove_topics, $auto_sync, $post_count_sync, false); 1183 } 1184 1185 /* @var $phpbb_notifications \phpbb\notification\manager */ 1186 $phpbb_notifications = $phpbb_container->get('notification_manager'); 1187 1188 $phpbb_notifications->delete_notifications($delete_notifications_types, $post_ids); 1189 1190 return count($post_ids); 1191 } 1192 1193 /** 1194 * Deletes shadow topics pointing to a specified forum. 1195 * 1196 * @param int $forum_id The forum id 1197 * @param string $sql_more Additional WHERE statement, e.g. t.topic_time < (time() - 1234) 1198 * @param bool $auto_sync Will call sync() if this is true 1199 * 1200 * @return array Array with affected forums 1201 */ 1202 function delete_topic_shadows($forum_id, $sql_more = '', $auto_sync = true) 1203 { 1204 global $db; 1205 1206 if (!$forum_id) 1207 { 1208 // Nothing to do. 1209 return; 1210 } 1211 1212 // Set of affected forums we have to resync 1213 $sync_forum_ids = array(); 1214 1215 // Amount of topics we select and delete at once. 1216 $batch_size = 500; 1217 1218 do 1219 { 1220 $sql = 'SELECT t2.forum_id, t2.topic_id 1221 FROM ' . TOPICS_TABLE . ' t2, ' . TOPICS_TABLE . ' t 1222 WHERE t2.topic_moved_id = t.topic_id 1223 AND t.forum_id = ' . (int) $forum_id . ' 1224 ' . (($sql_more) ? 'AND ' . $sql_more : ''); 1225 $result = $db->sql_query_limit($sql, $batch_size); 1226 1227 $topic_ids = array(); 1228 while ($row = $db->sql_fetchrow($result)) 1229 { 1230 $topic_ids[] = (int) $row['topic_id']; 1231 1232 $sync_forum_ids[(int) $row['forum_id']] = (int) $row['forum_id']; 1233 } 1234 $db->sql_freeresult($result); 1235 1236 if (!empty($topic_ids)) 1237 { 1238 $sql = 'DELETE FROM ' . TOPICS_TABLE . ' 1239 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 1240 $db->sql_query($sql); 1241 } 1242 } 1243 while (count($topic_ids) == $batch_size); 1244 1245 if ($auto_sync) 1246 { 1247 sync('forum', 'forum_id', $sync_forum_ids, true, true); 1248 } 1249 1250 return $sync_forum_ids; 1251 } 1252 1253 /** 1254 * Update/Sync posted information for topics 1255 */ 1256 function update_posted_info(&$topic_ids) 1257 { 1258 global $db, $config; 1259 1260 if (empty($topic_ids) || !$config['load_db_track']) 1261 { 1262 return; 1263 } 1264 1265 // First of all, let us remove any posted information for these topics 1266 $sql = 'DELETE FROM ' . TOPICS_POSTED_TABLE . ' 1267 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 1268 $db->sql_query($sql); 1269 1270 // Now, let us collect the user/topic combos for rebuilding the information 1271 $sql = 'SELECT poster_id, topic_id 1272 FROM ' . POSTS_TABLE . ' 1273 WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . ' 1274 AND poster_id <> ' . ANONYMOUS . ' 1275 GROUP BY poster_id, topic_id'; 1276 $result = $db->sql_query($sql); 1277 1278 $posted = array(); 1279 while ($row = $db->sql_fetchrow($result)) 1280 { 1281 // Add as key to make them unique (grouping by) and circumvent empty keys on array_unique 1282 $posted[$row['poster_id']][] = $row['topic_id']; 1283 } 1284 $db->sql_freeresult($result); 1285 1286 // Now add the information... 1287 $sql_ary = array(); 1288 foreach ($posted as $user_id => $topic_row) 1289 { 1290 foreach ($topic_row as $topic_id) 1291 { 1292 $sql_ary[] = array( 1293 'user_id' => (int) $user_id, 1294 'topic_id' => (int) $topic_id, 1295 'topic_posted' => 1, 1296 ); 1297 } 1298 } 1299 unset($posted); 1300 1301 $db->sql_multi_insert(TOPICS_POSTED_TABLE, $sql_ary); 1302 } 1303 1304 /** 1305 * All-encompasing sync function 1306 * 1307 * Exaples: 1308 * <code> 1309 * sync('topic', 'topic_id', 123); // resync topic #123 1310 * sync('topic', 'forum_id', array(2, 3)); // resync topics from forum #2 and #3 1311 * sync('topic'); // resync all topics 1312 * sync('topic', 'range', 'topic_id BETWEEN 1 AND 60'); // resync a range of topics/forums (only available for 'topic' and 'forum' modes) 1313 * </code> 1314 * 1315 * Modes: 1316 * - forum Resync complete forum 1317 * - topic Resync topics 1318 * - topic_moved Removes topic shadows that would be in the same forum as the topic they link to 1319 * - topic_visibility Resyncs the topic_visibility flag according to the status of the first post 1320 * - post_reported Resyncs the post_reported flag, relying on actual reports 1321 * - topic_reported Resyncs the topic_reported flag, relying on post_reported flags 1322 * - post_attachement Same as post_reported, but with attachment flags 1323 * - topic_attachement Same as topic_reported, but with attachment flags 1324 */ 1325 function sync($mode, $where_type = '', $where_ids = '', $resync_parents = false, $sync_extra = false) 1326 { 1327 global $db, $phpbb_dispatcher; 1328 1329 if (is_array($where_ids)) 1330 { 1331 $where_ids = array_unique($where_ids); 1332 $where_ids = array_map('intval', $where_ids); 1333 } 1334 else if ($where_type != 'range') 1335 { 1336 $where_ids = ($where_ids) ? array((int) $where_ids) : array(); 1337 } 1338 1339 if ($mode == 'forum' || $mode == 'topic' || $mode == 'topic_visibility' || $mode == 'topic_reported' || $mode == 'post_reported') 1340 { 1341 if (!$where_type) 1342 { 1343 $where_sql = ''; 1344 $where_sql_and = 'WHERE'; 1345 } 1346 else if ($where_type == 'range') 1347 { 1348 // Only check a range of topics/forums. For instance: 'topic_id BETWEEN 1 AND 60' 1349 $where_sql = 'WHERE (' . $mode[0] . ".$where_ids)"; 1350 $where_sql_and = $where_sql . "\n\tAND"; 1351 } 1352 else 1353 { 1354 // Do not sync the "global forum" 1355 $where_ids = array_diff($where_ids, array(0)); 1356 1357 if (!count($where_ids)) 1358 { 1359 // Empty array with IDs. This means that we don't have any work to do. Just return. 1360 return; 1361 } 1362 1363 // Limit the topics/forums we are syncing, use specific topic/forum IDs. 1364 // $where_type contains the field for the where clause (forum_id, topic_id) 1365 $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids); 1366 $where_sql_and = $where_sql . "\n\tAND"; 1367 } 1368 } 1369 else 1370 { 1371 if (!count($where_ids)) 1372 { 1373 return; 1374 } 1375 1376 // $where_type contains the field for the where clause (forum_id, topic_id) 1377 $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids); 1378 $where_sql_and = $where_sql . "\n\tAND"; 1379 } 1380 1381 switch ($mode) 1382 { 1383 case 'topic_moved': 1384 $db->sql_transaction('begin'); 1385 switch ($db->get_sql_layer()) 1386 { 1387 case 'mysqli': 1388 $sql = 'DELETE FROM ' . TOPICS_TABLE . ' 1389 USING ' . TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2 1390 WHERE t1.topic_moved_id = t2.topic_id 1391 AND t1.forum_id = t2.forum_id"; 1392 $db->sql_query($sql); 1393 break; 1394 1395 default: 1396 $sql = 'SELECT t1.topic_id 1397 FROM ' .TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2 1398 WHERE t1.topic_moved_id = t2.topic_id 1399 AND t1.forum_id = t2.forum_id"; 1400 $result = $db->sql_query($sql); 1401 1402 $topic_id_ary = array(); 1403 while ($row = $db->sql_fetchrow($result)) 1404 { 1405 $topic_id_ary[] = $row['topic_id']; 1406 } 1407 $db->sql_freeresult($result); 1408 1409 if (!count($topic_id_ary)) 1410 { 1411 return; 1412 } 1413 1414 $sql = 'DELETE FROM ' . TOPICS_TABLE . ' 1415 WHERE ' . $db->sql_in_set('topic_id', $topic_id_ary); 1416 $db->sql_query($sql); 1417 1418 break; 1419 } 1420 1421 $db->sql_transaction('commit'); 1422 break; 1423 1424 case 'topic_visibility': 1425 1426 $db->sql_transaction('begin'); 1427 1428 $sql = 'SELECT t.topic_id, p.post_visibility 1429 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p 1430 $where_sql_and p.topic_id = t.topic_id 1431 AND p.post_visibility = " . ITEM_APPROVED; 1432 $result = $db->sql_query($sql); 1433 1434 $topics_approved = array(); 1435 while ($row = $db->sql_fetchrow($result)) 1436 { 1437 $topics_approved[] = (int) $row['topic_id']; 1438 } 1439 $db->sql_freeresult($result); 1440 1441 $sql = 'SELECT t.topic_id, p.post_visibility 1442 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p 1443 $where_sql_and " . $db->sql_in_set('t.topic_id', $topics_approved, true, true) . ' 1444 AND p.topic_id = t.topic_id 1445 AND p.post_visibility = ' . ITEM_DELETED; 1446 $result = $db->sql_query($sql); 1447 1448 $topics_softdeleted = array(); 1449 while ($row = $db->sql_fetchrow($result)) 1450 { 1451 $topics_softdeleted[] = (int) $row['topic_id']; 1452 } 1453 $db->sql_freeresult($result); 1454 1455 $topics_softdeleted = array_diff($topics_softdeleted, $topics_approved); 1456 $topics_not_unapproved = array_merge($topics_softdeleted, $topics_approved); 1457 1458 $update_ary = array( 1459 ITEM_UNAPPROVED => (!empty($topics_not_unapproved)) ? $where_sql_and . ' ' . $db->sql_in_set('topic_id', $topics_not_unapproved, true) : '', 1460 ITEM_APPROVED => (!empty($topics_approved)) ? ' WHERE ' . $db->sql_in_set('topic_id', $topics_approved) : '', 1461 ITEM_DELETED => (!empty($topics_softdeleted)) ? ' WHERE ' . $db->sql_in_set('topic_id', $topics_softdeleted) : '', 1462 ); 1463 1464 foreach ($update_ary as $visibility => $sql_where) 1465 { 1466 if ($sql_where) 1467 { 1468 $sql = 'UPDATE ' . TOPICS_TABLE . ' 1469 SET topic_visibility = ' . $visibility . ' 1470 ' . $sql_where; 1471 $db->sql_query($sql); 1472 } 1473 } 1474 1475 $db->sql_transaction('commit'); 1476 break; 1477 1478 case 'post_reported': 1479 $post_ids = $post_reported = array(); 1480 1481 $db->sql_transaction('begin'); 1482 1483 $sql = 'SELECT p.post_id, p.post_reported 1484 FROM ' . POSTS_TABLE . " p 1485 $where_sql 1486 GROUP BY p.post_id, p.post_reported"; 1487 $result = $db->sql_query($sql); 1488 1489 while ($row = $db->sql_fetchrow($result)) 1490 { 1491 $post_ids[$row['post_id']] = $row['post_id']; 1492 if ($row['post_reported']) 1493 { 1494 $post_reported[$row['post_id']] = 1; 1495 } 1496 } 1497 $db->sql_freeresult($result); 1498 1499 $sql = 'SELECT DISTINCT(post_id) 1500 FROM ' . REPORTS_TABLE . ' 1501 WHERE ' . $db->sql_in_set('post_id', $post_ids) . ' 1502 AND report_closed = 0'; 1503 $result = $db->sql_query($sql); 1504 1505 $post_ids = array(); 1506 while ($row = $db->sql_fetchrow($result)) 1507 { 1508 if (!isset($post_reported[$row['post_id']])) 1509 { 1510 $post_ids[] = $row['post_id']; 1511 } 1512 else 1513 { 1514 unset($post_reported[$row['post_id']]); 1515 } 1516 } 1517 $db->sql_freeresult($result); 1518 1519 // $post_reported should be empty by now, if it's not it contains 1520 // posts that are falsely flagged as reported 1521 foreach ($post_reported as $post_id => $void) 1522 { 1523 $post_ids[] = $post_id; 1524 } 1525 1526 if (count($post_ids)) 1527 { 1528 $sql = 'UPDATE ' . POSTS_TABLE . ' 1529 SET post_reported = 1 - post_reported 1530 WHERE ' . $db->sql_in_set('post_id', $post_ids); 1531 $db->sql_query($sql); 1532 } 1533 1534 $db->sql_transaction('commit'); 1535 break; 1536 1537 case 'topic_reported': 1538 if ($sync_extra) 1539 { 1540 sync('post_reported', $where_type, $where_ids); 1541 } 1542 1543 $topic_ids = $topic_reported = array(); 1544 1545 $db->sql_transaction('begin'); 1546 1547 $sql = 'SELECT DISTINCT(t.topic_id) 1548 FROM ' . POSTS_TABLE . " t 1549 $where_sql_and t.post_reported = 1"; 1550 $result = $db->sql_query($sql); 1551 1552 while ($row = $db->sql_fetchrow($result)) 1553 { 1554 $topic_reported[$row['topic_id']] = 1; 1555 } 1556 $db->sql_freeresult($result); 1557 1558 $sql = 'SELECT t.topic_id, t.topic_reported 1559 FROM ' . TOPICS_TABLE . " t 1560 $where_sql"; 1561 $result = $db->sql_query($sql); 1562 1563 while ($row = $db->sql_fetchrow($result)) 1564 { 1565 if ($row['topic_reported'] ^ isset($topic_reported[$row['topic_id']])) 1566 { 1567 $topic_ids[] = $row['topic_id']; 1568 } 1569 } 1570 $db->sql_freeresult($result); 1571 1572 if (count($topic_ids)) 1573 { 1574 $sql = 'UPDATE ' . TOPICS_TABLE . ' 1575 SET topic_reported = 1 - topic_reported 1576 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 1577 $db->sql_query($sql); 1578 } 1579 1580 $db->sql_transaction('commit'); 1581 break; 1582 1583 case 'post_attachment': 1584 $post_ids = $post_attachment = array(); 1585 1586 $db->sql_transaction('begin'); 1587 1588 $sql = 'SELECT p.post_id, p.post_attachment 1589 FROM ' . POSTS_TABLE . " p 1590 $where_sql 1591 GROUP BY p.post_id, p.post_attachment"; 1592 $result = $db->sql_query($sql); 1593 1594 while ($row = $db->sql_fetchrow($result)) 1595 { 1596 $post_ids[$row['post_id']] = $row['post_id']; 1597 if ($row['post_attachment']) 1598 { 1599 $post_attachment[$row['post_id']] = 1; 1600 } 1601 } 1602 $db->sql_freeresult($result); 1603 1604 $sql = 'SELECT DISTINCT(post_msg_id) 1605 FROM ' . ATTACHMENTS_TABLE . ' 1606 WHERE ' . $db->sql_in_set('post_msg_id', $post_ids) . ' 1607 AND in_message = 0'; 1608 $result = $db->sql_query($sql); 1609 1610 $post_ids = array(); 1611 while ($row = $db->sql_fetchrow($result)) 1612 { 1613 if (!isset($post_attachment[$row['post_msg_id']])) 1614 { 1615 $post_ids[] = $row['post_msg_id']; 1616 } 1617 else 1618 { 1619 unset($post_attachment[$row['post_msg_id']]); 1620 } 1621 } 1622 $db->sql_freeresult($result); 1623 1624 // $post_attachment should be empty by now, if it's not it contains 1625 // posts that are falsely flagged as having attachments 1626 foreach ($post_attachment as $post_id => $void) 1627 { 1628 $post_ids[] = $post_id; 1629 } 1630 1631 if (count($post_ids)) 1632 { 1633 $sql = 'UPDATE ' . POSTS_TABLE . ' 1634 SET post_attachment = 1 - post_attachment 1635 WHERE ' . $db->sql_in_set('post_id', $post_ids); 1636 $db->sql_query($sql); 1637 } 1638 1639 $db->sql_transaction('commit'); 1640 break; 1641 1642 case 'topic_attachment': 1643 if ($sync_extra) 1644 { 1645 sync('post_attachment', $where_type, $where_ids); 1646 } 1647 1648 $topic_ids = $topic_attachment = array(); 1649 1650 $db->sql_transaction('begin'); 1651 1652 $sql = 'SELECT DISTINCT(t.topic_id) 1653 FROM ' . POSTS_TABLE . " t 1654 $where_sql_and t.post_attachment = 1"; 1655 $result = $db->sql_query($sql); 1656 1657 while ($row = $db->sql_fetchrow($result)) 1658 { 1659 $topic_attachment[$row['topic_id']] = 1; 1660 } 1661 $db->sql_freeresult($result); 1662 1663 $sql = 'SELECT t.topic_id, t.topic_attachment 1664 FROM ' . TOPICS_TABLE . " t 1665 $where_sql"; 1666 $result = $db->sql_query($sql); 1667 1668 while ($row = $db->sql_fetchrow($result)) 1669 { 1670 if ($row['topic_attachment'] ^ isset($topic_attachment[$row['topic_id']])) 1671 { 1672 $topic_ids[] = $row['topic_id']; 1673 } 1674 } 1675 $db->sql_freeresult($result); 1676 1677 if (count($topic_ids)) 1678 { 1679 $sql = 'UPDATE ' . TOPICS_TABLE . ' 1680 SET topic_attachment = 1 - topic_attachment 1681 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 1682 $db->sql_query($sql); 1683 } 1684 1685 $db->sql_transaction('commit'); 1686 1687 break; 1688 1689 case 'forum': 1690 1691 $db->sql_transaction('begin'); 1692 1693 // 1: Get the list of all forums 1694 $sql = 'SELECT f.* 1695 FROM ' . FORUMS_TABLE . " f 1696 $where_sql"; 1697 $result = $db->sql_query($sql); 1698 1699 $forum_data = $forum_ids = $post_ids = $last_post_id = $post_info = array(); 1700 while ($row = $db->sql_fetchrow($result)) 1701 { 1702 if ($row['forum_type'] == FORUM_LINK) 1703 { 1704 continue; 1705 } 1706 1707 $forum_id = (int) $row['forum_id']; 1708 $forum_ids[$forum_id] = $forum_id; 1709 1710 $forum_data[$forum_id] = $row; 1711 if ($sync_extra) 1712 { 1713 $forum_data[$forum_id]['posts_approved'] = 0; 1714 $forum_data[$forum_id]['posts_unapproved'] = 0; 1715 $forum_data[$forum_id]['posts_softdeleted'] = 0; 1716 $forum_data[$forum_id]['topics_approved'] = 0; 1717 $forum_data[$forum_id]['topics_unapproved'] = 0; 1718 $forum_data[$forum_id]['topics_softdeleted'] = 0; 1719 } 1720 $forum_data[$forum_id]['last_post_id'] = 0; 1721 $forum_data[$forum_id]['last_post_subject'] = ''; 1722 $forum_data[$forum_id]['last_post_time'] = 0; 1723 $forum_data[$forum_id]['last_poster_id'] = 0; 1724 $forum_data[$forum_id]['last_poster_name'] = ''; 1725 $forum_data[$forum_id]['last_poster_colour'] = ''; 1726 } 1727 $db->sql_freeresult($result); 1728 1729 if (!count($forum_ids)) 1730 { 1731 break; 1732 } 1733 1734 $forum_ids = array_values($forum_ids); 1735 1736 // 2: Get topic counts for each forum (optional) 1737 if ($sync_extra) 1738 { 1739 $sql = 'SELECT forum_id, topic_visibility, COUNT(topic_id) AS total_topics 1740 FROM ' . TOPICS_TABLE . ' 1741 WHERE ' . $db->sql_in_set('forum_id', $forum_ids) . ' 1742 GROUP BY forum_id, topic_visibility'; 1743 $result = $db->sql_query($sql); 1744 1745 while ($row = $db->sql_fetchrow($result)) 1746 { 1747 $forum_id = (int) $row['forum_id']; 1748 1749 if ($row['topic_visibility'] == ITEM_APPROVED) 1750 { 1751 $forum_data[$forum_id]['topics_approved'] = $row['total_topics']; 1752 } 1753 else if ($row['topic_visibility'] == ITEM_UNAPPROVED || $row['topic_visibility'] == ITEM_REAPPROVE) 1754 { 1755 $forum_data[$forum_id]['topics_unapproved'] = $row['total_topics']; 1756 } 1757 else if ($row['topic_visibility'] == ITEM_DELETED) 1758 { 1759 $forum_data[$forum_id]['topics_softdeleted'] = $row['total_topics']; 1760 } 1761 } 1762 $db->sql_freeresult($result); 1763 } 1764 1765 // 3: Get post count for each forum (optional) 1766 if ($sync_extra) 1767 { 1768 if (count($forum_ids) == 1) 1769 { 1770 $sql = 'SELECT SUM(t.topic_posts_approved) AS forum_posts_approved, SUM(t.topic_posts_unapproved) AS forum_posts_unapproved, SUM(t.topic_posts_softdeleted) AS forum_posts_softdeleted 1771 FROM ' . TOPICS_TABLE . ' t 1772 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . ' 1773 AND t.topic_status <> ' . ITEM_MOVED; 1774 } 1775 else 1776 { 1777 $sql = 'SELECT t.forum_id, SUM(t.topic_posts_approved) AS forum_posts_approved, SUM(t.topic_posts_unapproved) AS forum_posts_unapproved, SUM(t.topic_posts_softdeleted) AS forum_posts_softdeleted 1778 FROM ' . TOPICS_TABLE . ' t 1779 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . ' 1780 AND t.topic_status <> ' . ITEM_MOVED . ' 1781 GROUP BY t.forum_id'; 1782 } 1783 1784 $result = $db->sql_query($sql); 1785 1786 while ($row = $db->sql_fetchrow($result)) 1787 { 1788 $forum_id = (count($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id']; 1789 1790 $forum_data[$forum_id]['posts_approved'] = (int) $row['forum_posts_approved']; 1791 $forum_data[$forum_id]['posts_unapproved'] = (int) $row['forum_posts_unapproved']; 1792 $forum_data[$forum_id]['posts_softdeleted'] = (int) $row['forum_posts_softdeleted']; 1793 } 1794 $db->sql_freeresult($result); 1795 } 1796 1797 // 4: Get last_post_id for each forum 1798 if (count($forum_ids) == 1) 1799 { 1800 $sql = 'SELECT MAX(t.topic_last_post_id) as last_post_id 1801 FROM ' . TOPICS_TABLE . ' t 1802 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . ' 1803 AND t.topic_visibility = ' . ITEM_APPROVED; 1804 } 1805 else 1806 { 1807 $sql = 'SELECT t.forum_id, MAX(t.topic_last_post_id) as last_post_id 1808 FROM ' . TOPICS_TABLE . ' t 1809 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . ' 1810 AND t.topic_visibility = ' . ITEM_APPROVED . ' 1811 GROUP BY t.forum_id'; 1812 } 1813 1814 $result = $db->sql_query($sql); 1815 1816 while ($row = $db->sql_fetchrow($result)) 1817 { 1818 $forum_id = (count($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id']; 1819 1820 $forum_data[$forum_id]['last_post_id'] = (int) $row['last_post_id']; 1821 1822 $post_ids[] = $row['last_post_id']; 1823 } 1824 $db->sql_freeresult($result); 1825 1826 // 5: Retrieve last_post infos 1827 if (count($post_ids)) 1828 { 1829 $sql_ary = array( 1830 'SELECT' => 'p.post_id, p.poster_id, p.post_subject, p.post_time, p.post_username, u.username, u.user_colour', 1831 'FROM' => array( 1832 POSTS_TABLE => 'p', 1833 USERS_TABLE => 'u', 1834 ), 1835 'WHERE' => $db->sql_in_set('p.post_id', $post_ids) . ' 1836 AND p.poster_id = u.user_id', 1837 ); 1838 1839 /** 1840 * Event to modify the SQL array to get the post and user data from all forums' last posts 1841 * 1842 * @event core.sync_forum_last_post_info_sql 1843 * @var array sql_ary SQL array with some post and user data from the last posts list 1844 * @since 3.3.5-RC1 1845 */ 1846 $vars = ['sql_ary']; 1847 extract($phpbb_dispatcher->trigger_event('core.sync_forum_last_post_info_sql', compact($vars))); 1848 $result = $db->sql_query($db->sql_build_query('SELECT', $sql_ary)); 1849 1850 while ($row = $db->sql_fetchrow($result)) 1851 { 1852 $post_info[$row['post_id']] = $row; 1853 } 1854 $db->sql_freeresult($result); 1855 1856 foreach ($forum_data as $forum_id => $data) 1857 { 1858 if ($data['last_post_id']) 1859 { 1860 if (isset($post_info[$data['last_post_id']])) 1861 { 1862 $forum_data[$forum_id]['last_post_subject'] = $post_info[$data['last_post_id']]['post_subject']; 1863 $forum_data[$forum_id]['last_post_time'] = $post_info[$data['last_post_id']]['post_time']; 1864 $forum_data[$forum_id]['last_poster_id'] = $post_info[$data['last_post_id']]['poster_id']; 1865 $forum_data[$forum_id]['last_poster_name'] = ($post_info[$data['last_post_id']]['poster_id'] != ANONYMOUS) ? $post_info[$data['last_post_id']]['username'] : $post_info[$data['last_post_id']]['post_username']; 1866 $forum_data[$forum_id]['last_poster_colour'] = $post_info[$data['last_post_id']]['user_colour']; 1867 } 1868 else 1869 { 1870 // For some reason we did not find the post in the db 1871 $forum_data[$forum_id]['last_post_id'] = 0; 1872 $forum_data[$forum_id]['last_post_subject'] = ''; 1873 $forum_data[$forum_id]['last_post_time'] = 0; 1874 $forum_data[$forum_id]['last_poster_id'] = 0; 1875 $forum_data[$forum_id]['last_poster_name'] = ''; 1876 $forum_data[$forum_id]['last_poster_colour'] = ''; 1877 } 1878 } 1879 } 1880 } 1881 1882 // 6: Now do that thing 1883 $fieldnames = array('last_post_id', 'last_post_subject', 'last_post_time', 'last_poster_id', 'last_poster_name', 'last_poster_colour'); 1884 1885 if ($sync_extra) 1886 { 1887 array_push($fieldnames, 'posts_approved', 'posts_unapproved', 'posts_softdeleted', 'topics_approved', 'topics_unapproved', 'topics_softdeleted'); 1888 } 1889 1890 /** 1891 * Event to modify the SQL array to get the post and user data from all forums' last posts 1892 * 1893 * @event core.sync_modify_forum_data 1894 * @var array forum_data Array with data to update for all forum ids 1895 * @var array post_info Array with some post and user data from the last posts list 1896 * @var array fieldnames Array with the partial column names that are being updated 1897 * @since 3.3.5-RC1 1898 */ 1899 $vars = [ 1900 'forum_data', 1901 'post_info', 1902 'fieldnames', 1903 ]; 1904 extract($phpbb_dispatcher->trigger_event('core.sync_modify_forum_data', compact($vars))); 1905 unset($post_info); 1906 1907 foreach ($forum_data as $forum_id => $row) 1908 { 1909 $sql_ary = array(); 1910 1911 foreach ($fieldnames as $fieldname) 1912 { 1913 if ($row['forum_' . $fieldname] != $row[$fieldname]) 1914 { 1915 if (preg_match('#(name|colour|subject)$#', $fieldname)) 1916 { 1917 $sql_ary['forum_' . $fieldname] = (string) $row[$fieldname]; 1918 } 1919 else 1920 { 1921 $sql_ary['forum_' . $fieldname] = (int) $row[$fieldname]; 1922 } 1923 } 1924 } 1925 1926 if (count($sql_ary)) 1927 { 1928 $sql = 'UPDATE ' . FORUMS_TABLE . ' 1929 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' 1930 WHERE forum_id = ' . $forum_id; 1931 $db->sql_query($sql); 1932 } 1933 } 1934 1935 $db->sql_transaction('commit'); 1936 break; 1937 1938 case 'topic': 1939 $topic_data = $post_ids = $resync_forums = $delete_topics = $delete_posts = $moved_topics = array(); 1940 1941 $db->sql_transaction('begin'); 1942 1943 $sql = 'SELECT t.topic_id, t.forum_id, t.topic_moved_id, t.topic_visibility, ' . (($sync_extra) ? 't.topic_attachment, t.topic_reported, ' : '') . 't.topic_poster, t.topic_time, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_first_post_id, t.topic_first_poster_name, t.topic_first_poster_colour, t.topic_last_post_id, t.topic_last_post_subject, t.topic_last_poster_id, t.topic_last_poster_name, t.topic_last_poster_colour, t.topic_last_post_time 1944 FROM ' . TOPICS_TABLE . " t 1945 $where_sql"; 1946 $result = $db->sql_query($sql); 1947 1948 while ($row = $db->sql_fetchrow($result)) 1949 { 1950 if ($row['topic_moved_id']) 1951 { 1952 $moved_topics[] = $row['topic_id']; 1953 continue; 1954 } 1955 1956 $topic_id = (int) $row['topic_id']; 1957 $topic_data[$topic_id] = $row; 1958 $topic_data[$topic_id]['visibility'] = ITEM_UNAPPROVED; 1959 $topic_data[$topic_id]['posts_approved'] = 0; 1960 $topic_data[$topic_id]['posts_unapproved'] = 0; 1961 $topic_data[$topic_id]['posts_softdeleted'] = 0; 1962 $topic_data[$topic_id]['first_post_id'] = 0; 1963 $topic_data[$topic_id]['last_post_id'] = 0; 1964 unset($topic_data[$topic_id]['topic_id']); 1965 1966 // This array holds all topic_ids 1967 $delete_topics[$topic_id] = ''; 1968 1969 if ($sync_extra) 1970 { 1971 $topic_data[$topic_id]['reported'] = 0; 1972 $topic_data[$topic_id]['attachment'] = 0; 1973 } 1974 } 1975 $db->sql_freeresult($result); 1976 1977 // Use "t" as table alias because of the $where_sql clause 1978 // NOTE: 't.post_visibility' in the GROUP BY is causing a major slowdown. 1979 $sql = 'SELECT t.topic_id, t.post_visibility, COUNT(t.post_id) AS total_posts, MIN(t.post_id) AS first_post_id, MAX(t.post_id) AS last_post_id 1980 FROM ' . POSTS_TABLE . " t 1981 $where_sql 1982 GROUP BY t.topic_id, t.post_visibility"; 1983 $result = $db->sql_query($sql); 1984 1985 while ($row = $db->sql_fetchrow($result)) 1986 { 1987 $topic_id = (int) $row['topic_id']; 1988 1989 $row['first_post_id'] = (int) $row['first_post_id']; 1990 $row['last_post_id'] = (int) $row['last_post_id']; 1991 1992 if (!isset($topic_data[$topic_id])) 1993 { 1994 // Hey, these posts come from a topic that does not exist 1995 $delete_posts[$topic_id] = ''; 1996 } 1997 else 1998 { 1999 // Unset the corresponding entry in $delete_topics 2000 // When we'll be done, only topics with no posts will remain 2001 unset($delete_topics[$topic_id]); 2002 2003 if ($row['post_visibility'] == ITEM_APPROVED) 2004 { 2005 $topic_data[$topic_id]['posts_approved'] = $row['total_posts']; 2006 } 2007 else if ($row['post_visibility'] == ITEM_UNAPPROVED || $row['post_visibility'] == ITEM_REAPPROVE) 2008 { 2009 $topic_data[$topic_id]['posts_unapproved'] = $row['total_posts']; 2010 } 2011 else if ($row['post_visibility'] == ITEM_DELETED) 2012 { 2013 $topic_data[$topic_id]['posts_softdeleted'] = $row['total_posts']; 2014 } 2015 2016 if ($row['post_visibility'] == ITEM_APPROVED) 2017 { 2018 $topic_data[$topic_id]['visibility'] = ITEM_APPROVED; 2019 $topic_data[$topic_id]['first_post_id'] = $row['first_post_id']; 2020 $topic_data[$topic_id]['last_post_id'] = $row['last_post_id']; 2021 } 2022 else if ($topic_data[$topic_id]['visibility'] != ITEM_APPROVED) 2023 { 2024 // If there is no approved post, we take the min/max of the other visibilities 2025 // for the last and first post info, because it is only visible to moderators anyway 2026 $topic_data[$topic_id]['first_post_id'] = (!empty($topic_data[$topic_id]['first_post_id'])) ? min($topic_data[$topic_id]['first_post_id'], $row['first_post_id']) : $row['first_post_id']; 2027 $topic_data[$topic_id]['last_post_id'] = max($topic_data[$topic_id]['last_post_id'], $row['last_post_id']); 2028 2029 if ($topic_data[$topic_id]['visibility'] == ITEM_UNAPPROVED || $topic_data[$topic_id]['visibility'] == ITEM_REAPPROVE) 2030 { 2031 // Soft delete status is stronger than unapproved. 2032 $topic_data[$topic_id]['visibility'] = $row['post_visibility']; 2033 } 2034 } 2035 } 2036 } 2037 $db->sql_freeresult($result); 2038 2039 foreach ($topic_data as $topic_id => $row) 2040 { 2041 $post_ids[] = $row['first_post_id']; 2042 if ($row['first_post_id'] != $row['last_post_id']) 2043 { 2044 $post_ids[] = $row['last_post_id']; 2045 } 2046 } 2047 2048 // Now we delete empty topics and orphan posts 2049 if (count($delete_posts)) 2050 { 2051 delete_posts('topic_id', array_keys($delete_posts), false); 2052 unset($delete_posts); 2053 } 2054 2055 if (!count($topic_data)) 2056 { 2057 // If we get there, topic ids were invalid or topics did not contain any posts 2058 delete_topics($where_type, $where_ids, true); 2059 return; 2060 } 2061 2062 if (count($delete_topics)) 2063 { 2064 $delete_topic_ids = array(); 2065 foreach ($delete_topics as $topic_id => $void) 2066 { 2067 unset($topic_data[$topic_id]); 2068 $delete_topic_ids[] = $topic_id; 2069 } 2070 2071 delete_topics('topic_id', $delete_topic_ids, false); 2072 unset($delete_topics, $delete_topic_ids); 2073 } 2074 2075 $sql_ary = array( 2076 'SELECT' => 'p.post_id, p.topic_id, p.post_visibility, p.poster_id, p.post_subject, p.post_username, p.post_time, u.username, u.user_colour', 2077 'FROM' => array( 2078 POSTS_TABLE => 'p', 2079 USERS_TABLE => 'u', 2080 ), 2081 'WHERE' => $db->sql_in_set('p.post_id', $post_ids) . ' 2082 AND u.user_id = p.poster_id', 2083 ); 2084 2085 $custom_fieldnames = []; 2086 /** 2087 * Event to modify the SQL array to get the post and user data from all topics' last posts 2088 * 2089 * @event core.sync_topic_last_post_info_sql 2090 * @var array sql_ary SQL array with some post and user data from the last posts list 2091 * @var array custom_fieldnames Empty array for custom fieldnames to update the topics_table with 2092 * @since 3.3.5-RC1 2093 */ 2094 $vars = [ 2095 'sql_ary', 2096 'custom_fieldnames', 2097 ]; 2098 extract($phpbb_dispatcher->trigger_event('core.sync_topic_last_post_info_sql', compact($vars))); 2099 $result = $db->sql_query($db->sql_build_query('SELECT', $sql_ary)); 2100 2101 while ($row = $db->sql_fetchrow($result)) 2102 { 2103 $topic_id = intval($row['topic_id']); 2104 2105 if ($row['post_id'] == $topic_data[$topic_id]['first_post_id']) 2106 { 2107 $topic_data[$topic_id]['time'] = $row['post_time']; 2108 $topic_data[$topic_id]['poster'] = $row['poster_id']; 2109 $topic_data[$topic_id]['first_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username']; 2110 $topic_data[$topic_id]['first_poster_colour'] = $row['user_colour']; 2111 } 2112 2113 if ($row['post_id'] == $topic_data[$topic_id]['last_post_id']) 2114 { 2115 $topic_data[$topic_id]['last_poster_id'] = $row['poster_id']; 2116 $topic_data[$topic_id]['last_post_subject'] = $row['post_subject']; 2117 $topic_data[$topic_id]['last_post_time'] = $row['post_time']; 2118 $topic_data[$topic_id]['last_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username']; 2119 $topic_data[$topic_id]['last_poster_colour'] = $row['user_colour']; 2120 } 2121 2122 /** 2123 * Event to modify the topic_data when syncing topics 2124 * 2125 * @event core.sync_modify_topic_data 2126 * @var array topic_data Array with the topics' data we are syncing 2127 * @var array row Array with some of the current user and post data 2128 * @var int topic_id The current topic_id of $row 2129 * @since 3.3.5-RC1 2130 */ 2131 $vars = [ 2132 'topic_data', 2133 'row', 2134 'topic_id', 2135 ]; 2136 extract($phpbb_dispatcher->trigger_event('core.sync_modify_topic_data', compact($vars))); 2137 } 2138 $db->sql_freeresult($result); 2139 2140 // Make sure shadow topics do link to existing topics 2141 if (count($moved_topics)) 2142 { 2143 $delete_topics = array(); 2144 2145 $sql = 'SELECT t1.topic_id, t1.topic_moved_id 2146 FROM ' . TOPICS_TABLE . ' t1 2147 LEFT JOIN ' . TOPICS_TABLE . ' t2 ON (t2.topic_id = t1.topic_moved_id) 2148 WHERE ' . $db->sql_in_set('t1.topic_id', $moved_topics) . ' 2149 AND t2.topic_id IS NULL'; 2150 $result = $db->sql_query($sql); 2151 2152 while ($row = $db->sql_fetchrow($result)) 2153 { 2154 $delete_topics[] = $row['topic_id']; 2155 } 2156 $db->sql_freeresult($result); 2157 2158 if (count($delete_topics)) 2159 { 2160 delete_topics('topic_id', $delete_topics, false); 2161 } 2162 unset($delete_topics); 2163 2164 // Make sure shadow topics having no last post data being updated (this only rarely happens...) 2165 $sql = 'SELECT topic_id, topic_moved_id, topic_last_post_id, topic_first_post_id 2166 FROM ' . TOPICS_TABLE . ' 2167 WHERE ' . $db->sql_in_set('topic_id', $moved_topics) . ' 2168 AND topic_last_post_time = 0'; 2169 $result = $db->sql_query($sql); 2170 2171 $shadow_topic_data = $post_ids = array(); 2172 while ($row = $db->sql_fetchrow($result)) 2173 { 2174 $shadow_topic_data[$row['topic_moved_id']] = $row; 2175 $post_ids[] = $row['topic_last_post_id']; 2176 $post_ids[] = $row['topic_first_post_id']; 2177 } 2178 $db->sql_freeresult($result); 2179 2180 $sync_shadow_topics = array(); 2181 if (count($post_ids)) 2182 { 2183 $sql = 'SELECT p.post_id, p.topic_id, p.post_visibility, p.poster_id, p.post_subject, p.post_username, p.post_time, u.username, u.user_colour 2184 FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u 2185 WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . ' 2186 AND u.user_id = p.poster_id'; 2187 $result = $db->sql_query($sql); 2188 2189 while ($row = $db->sql_fetchrow($result)) 2190 { 2191 $topic_id = (int) $row['topic_id']; 2192 2193 // Ok, there should be a shadow topic. If there isn't, then there's something wrong with the db. 2194 // However, there's not much we can do about it. 2195 if (!empty($shadow_topic_data[$topic_id])) 2196 { 2197 if ($row['post_id'] == $shadow_topic_data[$topic_id]['topic_first_post_id']) 2198 { 2199 $orig_topic_id = $shadow_topic_data[$topic_id]['topic_id']; 2200 2201 if (!isset($sync_shadow_topics[$orig_topic_id])) 2202 { 2203 $sync_shadow_topics[$orig_topic_id] = array(); 2204 } 2205 2206 $sync_shadow_topics[$orig_topic_id]['topic_time'] = $row['post_time']; 2207 $sync_shadow_topics[$orig_topic_id]['topic_poster'] = $row['poster_id']; 2208 $sync_shadow_topics[$orig_topic_id]['topic_first_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username']; 2209 $sync_shadow_topics[$orig_topic_id]['topic_first_poster_colour'] = $row['user_colour']; 2210 } 2211 2212 if ($row['post_id'] == $shadow_topic_data[$topic_id]['topic_last_post_id']) 2213 { 2214 $orig_topic_id = $shadow_topic_data[$topic_id]['topic_id']; 2215 2216 if (!isset($sync_shadow_topics[$orig_topic_id])) 2217 { 2218 $sync_shadow_topics[$orig_topic_id] = array(); 2219 } 2220 2221 $sync_shadow_topics[$orig_topic_id]['topic_last_poster_id'] = $row['poster_id']; 2222 $sync_shadow_topics[$orig_topic_id]['topic_last_post_subject'] = $row['post_subject']; 2223 $sync_shadow_topics[$orig_topic_id]['topic_last_post_time'] = $row['post_time']; 2224 $sync_shadow_topics[$orig_topic_id]['topic_last_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username']; 2225 $sync_shadow_topics[$orig_topic_id]['topic_last_poster_colour'] = $row['user_colour']; 2226 } 2227 } 2228 } 2229 $db->sql_freeresult($result); 2230 2231 $shadow_topic_data = array(); 2232 2233 // Update the information we collected 2234 if (count($sync_shadow_topics)) 2235 { 2236 foreach ($sync_shadow_topics as $sync_topic_id => $sql_ary) 2237 { 2238 $sql = 'UPDATE ' . TOPICS_TABLE . ' 2239 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' 2240 WHERE topic_id = ' . $sync_topic_id; 2241 $db->sql_query($sql); 2242 } 2243 } 2244 } 2245 2246 unset($sync_shadow_topics, $shadow_topic_data); 2247 } 2248 2249 // These are fields that will be synchronised 2250 $fieldnames = array('time', 'visibility', 'posts_approved', 'posts_unapproved', 'posts_softdeleted', 'poster', 'first_post_id', 'first_poster_name', 'first_poster_colour', 'last_post_id', 'last_post_subject', 'last_post_time', 'last_poster_id', 'last_poster_name', 'last_poster_colour'); 2251 2252 // Add custom fieldnames 2253 $fieldnames = array_merge($fieldnames, $custom_fieldnames); 2254 unset($custom_fieldnames); 2255 2256 if ($sync_extra) 2257 { 2258 // This routine assumes that post_reported values are correct 2259 // if they are not, use sync('post_reported') first 2260 $sql = 'SELECT t.topic_id, p.post_id 2261 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p 2262 $where_sql_and p.topic_id = t.topic_id 2263 AND p.post_reported = 1 2264 GROUP BY t.topic_id, p.post_id"; 2265 $result = $db->sql_query($sql); 2266 2267 $fieldnames[] = 'reported'; 2268 while ($row = $db->sql_fetchrow($result)) 2269 { 2270 $topic_data[intval($row['topic_id'])]['reported'] = 1; 2271 } 2272 $db->sql_freeresult($result); 2273 2274 // This routine assumes that post_attachment values are correct 2275 // if they are not, use sync('post_attachment') first 2276 $sql = 'SELECT t.topic_id, p.post_id 2277 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p 2278 $where_sql_and p.topic_id = t.topic_id 2279 AND p.post_attachment = 1 2280 GROUP BY t.topic_id, p.post_id"; 2281 $result = $db->sql_query($sql); 2282 2283 $fieldnames[] = 'attachment'; 2284 while ($row = $db->sql_fetchrow($result)) 2285 { 2286 $topic_data[intval($row['topic_id'])]['attachment'] = 1; 2287 } 2288 $db->sql_freeresult($result); 2289 } 2290 2291 foreach ($topic_data as $topic_id => $row) 2292 { 2293 $sql_ary = array(); 2294 2295 foreach ($fieldnames as $fieldname) 2296 { 2297 if (isset($row[$fieldname]) && isset($row['topic_' . $fieldname]) && $row['topic_' . $fieldname] != $row[$fieldname]) 2298 { 2299 $sql_ary['topic_' . $fieldname] = $row[$fieldname]; 2300 } 2301 } 2302 2303 if (count($sql_ary)) 2304 { 2305 $sql = 'UPDATE ' . TOPICS_TABLE . ' 2306 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' 2307 WHERE topic_id = ' . $topic_id; 2308 $db->sql_query($sql); 2309 2310 $resync_forums[$row['forum_id']] = $row['forum_id']; 2311 } 2312 } 2313 unset($topic_data); 2314 2315 $db->sql_transaction('commit'); 2316 2317 // if some topics have been resync'ed then resync parent forums 2318 // except when we're only syncing a range, we don't want to sync forums during 2319 // batch processing. 2320 if ($resync_parents && count($resync_forums) && $where_type != 'range') 2321 { 2322 sync('forum', 'forum_id', array_values($resync_forums), true, true); 2323 } 2324 break; 2325 } 2326 2327 return; 2328 } 2329 2330 /** 2331 * Prune function 2332 */ 2333 function prune($forum_id, $prune_mode, $prune_date, $prune_flags = 0, $auto_sync = true, $prune_limit = 0) 2334 { 2335 global $db, $phpbb_dispatcher; 2336 2337 if (!is_array($forum_id)) 2338 { 2339 $forum_id = array($forum_id); 2340 } 2341 2342 if (!count($forum_id)) 2343 { 2344 return; 2345 } 2346 2347 $sql_and = ''; 2348 2349 if (!($prune_flags & FORUM_FLAG_PRUNE_ANNOUNCE)) 2350 { 2351 $sql_and .= ' AND topic_type <> ' . POST_ANNOUNCE; 2352 $sql_and .= ' AND topic_type <> ' . POST_GLOBAL; 2353 } 2354 2355 if (!($prune_flags & FORUM_FLAG_PRUNE_STICKY)) 2356 { 2357 $sql_and .= ' AND topic_type <> ' . POST_STICKY; 2358 } 2359 2360 if ($prune_mode == 'posted') 2361 { 2362 $sql_and .= " AND topic_last_post_time < $prune_date"; 2363 } 2364 2365 if ($prune_mode == 'viewed') 2366 { 2367 $sql_and .= " AND topic_last_view_time < $prune_date"; 2368 } 2369 2370 if ($prune_mode == 'shadow') 2371 { 2372 $sql_and .= ' AND topic_status = ' . ITEM_MOVED . " AND topic_last_post_time < $prune_date"; 2373 } 2374 2375 /** 2376 * Use this event to modify the SQL that selects topics to be pruned 2377 * 2378 * @event core.prune_sql 2379 * @var string forum_id The forum id 2380 * @var string prune_mode The prune mode 2381 * @var string prune_date The prune date 2382 * @var int prune_flags The prune flags 2383 * @var bool auto_sync Whether or not to perform auto sync 2384 * @var string sql_and SQL text appended to where clause 2385 * @var int prune_limit The prune limit 2386 * @since 3.1.3-RC1 2387 * @changed 3.1.10-RC1 Added prune_limit 2388 */ 2389 $vars = array( 2390 'forum_id', 2391 'prune_mode', 2392 'prune_date', 2393 'prune_flags', 2394 'auto_sync', 2395 'sql_and', 2396 'prune_limit', 2397 ); 2398 extract($phpbb_dispatcher->trigger_event('core.prune_sql', compact($vars))); 2399 2400 $sql = 'SELECT topic_id 2401 FROM ' . TOPICS_TABLE . ' 2402 WHERE ' . $db->sql_in_set('forum_id', $forum_id) . " 2403 AND poll_start = 0 2404 $sql_and"; 2405 $result = $db->sql_query_limit($sql, $prune_limit); 2406 2407 $topic_list = array(); 2408 while ($row = $db->sql_fetchrow($result)) 2409 { 2410 $topic_list[] = $row['topic_id']; 2411 } 2412 $db->sql_freeresult($result); 2413 2414 if ($prune_flags & FORUM_FLAG_PRUNE_POLL) 2415 { 2416 $sql = 'SELECT topic_id 2417 FROM ' . TOPICS_TABLE . ' 2418 WHERE ' . $db->sql_in_set('forum_id', $forum_id) . " 2419 AND poll_start > 0 2420 AND poll_last_vote < $prune_date 2421 $sql_and"; 2422 $result = $db->sql_query_limit($sql, $prune_limit); 2423 2424 while ($row = $db->sql_fetchrow($result)) 2425 { 2426 $topic_list[] = $row['topic_id']; 2427 } 2428 $db->sql_freeresult($result); 2429 2430 $topic_list = array_unique($topic_list); 2431 } 2432 2433 /** 2434 * Perform additional actions before topic deletion via pruning 2435 * 2436 * @event core.prune_delete_before 2437 * @var int[] topic_list The IDs of the topics to be deleted 2438 * @since 3.2.2-RC1 2439 */ 2440 $vars = array('topic_list'); 2441 extract($phpbb_dispatcher->trigger_event('core.prune_delete_before', compact($vars))); 2442 2443 return delete_topics('topic_id', $topic_list, $auto_sync, false); 2444 } 2445 2446 /** 2447 * Function auto_prune(), this function now relies on passed vars 2448 */ 2449 function auto_prune($forum_id, $prune_mode, $prune_flags, $prune_days, $prune_freq, $log_prune = true) 2450 { 2451 global $db, $user, $phpbb_log; 2452 2453 $sql = 'SELECT forum_name 2454 FROM ' . FORUMS_TABLE . " 2455 WHERE forum_id = $forum_id"; 2456 $result = $db->sql_query($sql, 3600); 2457 $row = $db->sql_fetchrow($result); 2458 $db->sql_freeresult($result); 2459 2460 if ($row) 2461 { 2462 $prune_date = time() - ($prune_days * 86400); 2463 $next_prune = time() + ($prune_freq * 86400); 2464 2465 $result = prune($forum_id, $prune_mode, $prune_date, $prune_flags, true, 300); 2466 2467 if ($result['topics'] == 0 && $result['posts'] == 0) 2468 { 2469 $column = $prune_mode === 'shadow' ? 'prune_shadow_next' : 'prune_next'; 2470 2471 $sql = 'UPDATE ' . FORUMS_TABLE . " 2472 SET $column = $next_prune 2473 WHERE forum_id = $forum_id"; 2474 $db->sql_query($sql); 2475 } 2476 2477 if ($log_prune) 2478 { 2479 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_AUTO_PRUNE', false, [$row['forum_name']]); 2480 } 2481 } 2482 2483 return; 2484 } 2485 2486 /** 2487 * Cache moderators. Called whenever permissions are changed 2488 * via admin_permissions. Changes of usernames and group names 2489 * must be carried through for the moderators table. 2490 * 2491 * @param \phpbb\db\driver\driver_interface $db Database connection 2492 * @param \phpbb\cache\driver\driver_interface $cache Cache driver 2493 * @param \phpbb\auth\auth $auth Authentication object 2494 * @return null 2495 */ 2496 function phpbb_cache_moderators($db, $cache, $auth) 2497 { 2498 // Remove cached sql results 2499 $cache->destroy('sql', MODERATOR_CACHE_TABLE); 2500 2501 // Clear table 2502 switch ($db->get_sql_layer()) 2503 { 2504 case 'sqlite3': 2505 $db->sql_query('DELETE FROM ' . MODERATOR_CACHE_TABLE); 2506 break; 2507 2508 default: 2509 $db->sql_query('TRUNCATE TABLE ' . MODERATOR_CACHE_TABLE); 2510 break; 2511 } 2512 2513 // We add moderators who have forum moderator permissions without an explicit ACL_NEVER setting 2514 $sql_ary = array(); 2515 2516 // Grab all users having moderative options... 2517 $hold_ary = $auth->acl_user_raw_data(false, 'm_%', false); 2518 2519 // Add users? 2520 if (!empty($hold_ary)) 2521 { 2522 // At least one moderative option warrants a display 2523 $ug_id_ary = array_keys($hold_ary); 2524 2525 // Remove users who have group memberships with DENY moderator permissions 2526 $sql_ary_deny = array( 2527 'SELECT' => 'a.forum_id, ug.user_id, g.group_id', 2528 2529 'FROM' => array( 2530 ACL_OPTIONS_TABLE => 'o', 2531 USER_GROUP_TABLE => 'ug', 2532 GROUPS_TABLE => 'g', 2533 ACL_GROUPS_TABLE => 'a', 2534 ), 2535 2536 'LEFT_JOIN' => array( 2537 array( 2538 'FROM' => array(ACL_ROLES_DATA_TABLE => 'r'), 2539 'ON' => 'a.auth_role_id = r.role_id', 2540 ), 2541 ), 2542 2543 'WHERE' => '(o.auth_option_id = a.auth_option_id OR o.auth_option_id = r.auth_option_id) 2544 AND ((a.auth_setting = ' . ACL_NEVER . ' AND r.auth_setting IS NULL) 2545 OR r.auth_setting = ' . ACL_NEVER . ') 2546 AND a.group_id = ug.group_id 2547 AND g.group_id = ug.group_id 2548 AND NOT (ug.group_leader = 1 AND g.group_skip_auth = 1) 2549 AND ' . $db->sql_in_set('ug.user_id', $ug_id_ary) . " 2550 AND ug.user_pending = 0 2551 AND o.auth_option " . $db->sql_like_expression('m_' . $db->get_any_char()), 2552 ); 2553 $sql = $db->sql_build_query('SELECT', $sql_ary_deny); 2554 $result = $db->sql_query($sql); 2555 2556 while ($row = $db->sql_fetchrow($result)) 2557 { 2558 if (isset($hold_ary[$row['user_id']][$row['forum_id']])) 2559 { 2560 unset($hold_ary[$row['user_id']][$row['forum_id']]); 2561 } 2562 } 2563 $db->sql_freeresult($result); 2564 2565 if (count($hold_ary)) 2566 { 2567 // Get usernames... 2568 $sql = 'SELECT user_id, username 2569 FROM ' . USERS_TABLE . ' 2570 WHERE ' . $db->sql_in_set('user_id', array_keys($hold_ary)); 2571 $result = $db->sql_query($sql); 2572 2573 $usernames_ary = array(); 2574 while ($row = $db->sql_fetchrow($result)) 2575 { 2576 $usernames_ary[$row['user_id']] = $row['username']; 2577 } 2578 $db->sql_freeresult($result); 2579 2580 foreach ($hold_ary as $user_id => $forum_id_ary) 2581 { 2582 // Do not continue if user does not exist 2583 if (!isset($usernames_ary[$user_id])) 2584 { 2585 continue; 2586 } 2587 2588 foreach ($forum_id_ary as $forum_id => $auth_ary) 2589 { 2590 $sql_ary[] = array( 2591 'forum_id' => (int) $forum_id, 2592 'user_id' => (int) $user_id, 2593 'username' => (string) $usernames_ary[$user_id], 2594 'group_id' => 0, 2595 'group_name' => '' 2596 ); 2597 } 2598 } 2599 } 2600 } 2601 2602 // Now to the groups... 2603 $hold_ary = $auth->acl_group_raw_data(false, 'm_%', false); 2604 2605 if (!empty($hold_ary)) 2606 { 2607 $ug_id_ary = array_keys($hold_ary); 2608 2609 // Make sure not hidden or special groups are involved... 2610 $sql = 'SELECT group_name, group_id, group_type 2611 FROM ' . GROUPS_TABLE . ' 2612 WHERE ' . $db->sql_in_set('group_id', $ug_id_ary); 2613 $result = $db->sql_query($sql); 2614 2615 $groupnames_ary = array(); 2616 while ($row = $db->sql_fetchrow($result)) 2617 { 2618 if ($row['group_type'] == GROUP_HIDDEN || $row['group_type'] == GROUP_SPECIAL) 2619 { 2620 unset($hold_ary[$row['group_id']]); 2621 } 2622 2623 $groupnames_ary[$row['group_id']] = $row['group_name']; 2624 } 2625 $db->sql_freeresult($result); 2626 2627 foreach ($hold_ary as $group_id => $forum_id_ary) 2628 { 2629 // If there is no group, we do not assign it... 2630 if (!isset($groupnames_ary[$group_id])) 2631 { 2632 continue; 2633 } 2634 2635 foreach ($forum_id_ary as $forum_id => $auth_ary) 2636 { 2637 $flag = false; 2638 foreach ($auth_ary as $auth_option => $setting) 2639 { 2640 // Make sure at least one ACL_YES option is set... 2641 if ($setting == ACL_YES) 2642 { 2643 $flag = true; 2644 break; 2645 } 2646 } 2647 2648 if (!$flag) 2649 { 2650 continue; 2651 } 2652 2653 $sql_ary[] = array( 2654 'forum_id' => (int) $forum_id, 2655 'user_id' => 0, 2656 'username' => '', 2657 'group_id' => (int) $group_id, 2658 'group_name' => (string) $groupnames_ary[$group_id] 2659 ); 2660 } 2661 } 2662 } 2663 2664 $db->sql_multi_insert(MODERATOR_CACHE_TABLE, $sql_ary); 2665 } 2666 2667 /** 2668 * View log 2669 * 2670 * @param string $mode The mode defines which log_type is used and from which log the entry is retrieved 2671 * @param array &$log The result array with the logs 2672 * @param mixed &$log_count If $log_count is set to false, we will skip counting all entries in the database. 2673 * Otherwise an integer with the number of total matching entries is returned. 2674 * @param int $limit Limit the number of entries that are returned 2675 * @param int $offset Offset when fetching the log entries, f.e. when paginating 2676 * @param mixed $forum_id Restrict the log entries to the given forum_id (can also be an array of forum_ids) 2677 * @param int $topic_id Restrict the log entries to the given topic_id 2678 * @param int $user_id Restrict the log entries to the given user_id 2679 * @param int $limit_days Only get log entries newer than the given timestamp 2680 * @param string $sort_by SQL order option, e.g. 'l.log_time DESC' 2681 * @param string $keywords Will only return log entries that have the keywords in log_operation or log_data 2682 * 2683 * @return int Returns the offset of the last valid page, if the specified offset was invalid (too high) 2684 */ 2685 function view_log($mode, &$log, &$log_count, $limit = 0, $offset = 0, $forum_id = 0, $topic_id = 0, $user_id = 0, $limit_days = 0, $sort_by = 'l.log_time DESC', $keywords = '') 2686 { 2687 global $phpbb_log; 2688 2689 $count_logs = ($log_count !== false); 2690 2691 $log = $phpbb_log->get_logs($mode, $count_logs, $limit, $offset, $forum_id, $topic_id, $user_id, $limit_days, $sort_by, $keywords); 2692 $log_count = $phpbb_log->get_log_count(); 2693 2694 return $phpbb_log->get_valid_offset(); 2695 } 2696 2697 /** 2698 * Removes moderators and administrators from foe lists. 2699 * 2700 * @param \phpbb\db\driver\driver_interface $db Database connection 2701 * @param \phpbb\auth\auth $auth Authentication object 2702 * @param array|bool $group_id If an array, remove all members of this group from foe lists, or false to ignore 2703 * @param array|bool $user_id If an array, remove this user from foe lists, or false to ignore 2704 * @return null 2705 */ 2706 function phpbb_update_foes($db, $auth, $group_id = false, $user_id = false) 2707 { 2708 // update foes for some user 2709 if (is_array($user_id) && count($user_id)) 2710 { 2711 $sql = 'DELETE FROM ' . ZEBRA_TABLE . ' 2712 WHERE ' . $db->sql_in_set('zebra_id', $user_id) . ' 2713 AND foe = 1'; 2714 $db->sql_query($sql); 2715 return; 2716 } 2717 2718 // update foes for some group 2719 if (is_array($group_id) && count($group_id)) 2720 { 2721 // Grab group settings... 2722 $sql_ary = array( 2723 'SELECT' => 'a.group_id', 2724 2725 'FROM' => array( 2726 ACL_OPTIONS_TABLE => 'ao', 2727 ACL_GROUPS_TABLE => 'a', 2728 ), 2729 2730 'LEFT_JOIN' => array( 2731 array( 2732 'FROM' => array(ACL_ROLES_DATA_TABLE => 'r'), 2733 'ON' => 'a.auth_role_id = r.role_id', 2734 ), 2735 ), 2736 2737 'WHERE' => '(ao.auth_option_id = a.auth_option_id OR ao.auth_option_id = r.auth_option_id) 2738 AND ' . $db->sql_in_set('a.group_id', $group_id) . " 2739 AND ao.auth_option IN ('a_', 'm_')", 2740 2741 'GROUP_BY' => 'a.group_id', 2742 ); 2743 $sql = $db->sql_build_query('SELECT', $sql_ary); 2744 $result = $db->sql_query($sql); 2745 2746 $groups = array(); 2747 while ($row = $db->sql_fetchrow($result)) 2748 { 2749 $groups[] = (int) $row['group_id']; 2750 } 2751 $db->sql_freeresult($result); 2752 2753 if (!count($groups)) 2754 { 2755 return; 2756 } 2757 2758 switch ($db->get_sql_layer()) 2759 { 2760 case 'mysqli': 2761 $sql = 'DELETE z.* 2762 FROM ' . ZEBRA_TABLE . ' z, ' . USER_GROUP_TABLE . ' ug 2763 WHERE z.zebra_id = ug.user_id 2764 AND z.foe = 1 2765 AND ' . $db->sql_in_set('ug.group_id', $groups); 2766 $db->sql_query($sql); 2767 break; 2768 2769 default: 2770 $sql = 'SELECT user_id 2771 FROM ' . USER_GROUP_TABLE . ' 2772 WHERE ' . $db->sql_in_set('group_id', $groups); 2773 $result = $db->sql_query($sql); 2774 2775 $users = array(); 2776 while ($row = $db->sql_fetchrow($result)) 2777 { 2778 $users[] = (int) $row['user_id']; 2779 } 2780 $db->sql_freeresult($result); 2781 2782 if (count($users)) 2783 { 2784 $sql = 'DELETE FROM ' . ZEBRA_TABLE . ' 2785 WHERE ' . $db->sql_in_set('zebra_id', $users) . ' 2786 AND foe = 1'; 2787 $db->sql_query($sql); 2788 } 2789 break; 2790 } 2791 2792 return; 2793 } 2794 2795 // update foes for everyone 2796 $perms = array(); 2797 foreach ($auth->acl_get_list(false, array('a_', 'm_'), false) as $forum_id => $forum_ary) 2798 { 2799 foreach ($forum_ary as $auth_option => $user_ary) 2800 { 2801 $perms = array_merge($perms, $user_ary); 2802 } 2803 } 2804 2805 if (count($perms)) 2806 { 2807 $sql = 'DELETE FROM ' . ZEBRA_TABLE . ' 2808 WHERE ' . $db->sql_in_set('zebra_id', array_unique($perms)) . ' 2809 AND foe = 1'; 2810 $db->sql_query($sql); 2811 } 2812 unset($perms); 2813 } 2814 2815 /** 2816 * Lists inactive users 2817 */ 2818 function view_inactive_users(&$users, &$user_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'user_inactive_time DESC') 2819 { 2820 global $db, $user; 2821 2822 $sql = 'SELECT COUNT(user_id) AS user_count 2823 FROM ' . USERS_TABLE . ' 2824 WHERE user_type = ' . USER_INACTIVE . 2825 (($limit_days) ? " AND user_inactive_time >= $limit_days" : ''); 2826 $result = $db->sql_query($sql); 2827 $user_count = (int) $db->sql_fetchfield('user_count'); 2828 $db->sql_freeresult($result); 2829 2830 if ($user_count == 0) 2831 { 2832 // Save the queries, because there are no users to display 2833 return 0; 2834 } 2835 2836 if ($offset >= $user_count) 2837 { 2838 $offset = ($offset - $limit < 0) ? 0 : $offset - $limit; 2839 } 2840 2841 $sql = 'SELECT * 2842 FROM ' . USERS_TABLE . ' 2843 WHERE user_type = ' . USER_INACTIVE . 2844 (($limit_days) ? " AND user_inactive_time >= $limit_days" : '') . " 2845 ORDER BY $sort_by"; 2846 $result = $db->sql_query_limit($sql, $limit, $offset); 2847 2848 while ($row = $db->sql_fetchrow($result)) 2849 { 2850 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_UNKNOWN']; 2851 switch ($row['user_inactive_reason']) 2852 { 2853 case INACTIVE_REGISTER: 2854 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_REGISTER']; 2855 break; 2856 2857 case INACTIVE_PROFILE: 2858 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_PROFILE']; 2859 break; 2860 2861 case INACTIVE_MANUAL: 2862 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_MANUAL']; 2863 break; 2864 2865 case INACTIVE_REMIND: 2866 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_REMIND']; 2867 break; 2868 } 2869 2870 $users[] = $row; 2871 } 2872 $db->sql_freeresult($result); 2873 2874 return $offset; 2875 } 2876 2877 /** 2878 * Lists warned users 2879 */ 2880 function view_warned_users(&$users, &$user_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'user_warnings DESC') 2881 { 2882 global $db; 2883 2884 $sql = 'SELECT user_id, username, user_colour, user_warnings, user_last_warning 2885 FROM ' . USERS_TABLE . ' 2886 WHERE user_warnings > 0 2887 ' . (($limit_days) ? "AND user_last_warning >= $limit_days" : '') . " 2888 ORDER BY $sort_by"; 2889 $result = $db->sql_query_limit($sql, $limit, $offset); 2890 $users = $db->sql_fetchrowset($result); 2891 $db->sql_freeresult($result); 2892 2893 $sql = 'SELECT count(user_id) AS user_count 2894 FROM ' . USERS_TABLE . ' 2895 WHERE user_warnings > 0 2896 ' . (($limit_days) ? "AND user_last_warning >= $limit_days" : ''); 2897 $result = $db->sql_query($sql); 2898 $user_count = (int) $db->sql_fetchfield('user_count'); 2899 $db->sql_freeresult($result); 2900 2901 return; 2902 } 2903 2904 /** 2905 * Get database size 2906 */ 2907 function get_database_size() 2908 { 2909 global $db, $user; 2910 2911 $database_size = false; 2912 2913 switch ($db->get_sql_layer()) 2914 { 2915 case 'mysqli': 2916 $mysql_engine = ['MyISAM', 'InnoDB', 'Aria']; 2917 $db_name = $db->get_db_name(); 2918 $database_size = 0; 2919 2920 $sql = 'SHOW TABLE STATUS 2921 FROM ' . $db->sql_quote($db_name); 2922 $result = $db->sql_query($sql, 7200); 2923 2924 while ($row = $db->sql_fetchrow($result)) 2925 { 2926 if (isset($row['Engine']) && in_array($row['Engine'], $mysql_engine)) 2927 { 2928 $database_size += $row['Data_length'] + $row['Index_length']; 2929 } 2930 } 2931 2932 $db->sql_freeresult($result); 2933 2934 $database_size = $database_size ? $database_size : false; 2935 2936 break; 2937 2938 case 'sqlite3': 2939 global $dbhost; 2940 2941 if (file_exists($dbhost)) 2942 { 2943 $database_size = filesize($dbhost); 2944 } 2945 2946 break; 2947 2948 case 'mssql_odbc': 2949 case 'mssqlnative': 2950 $sql = 'SELECT @@VERSION AS mssql_version'; 2951 $result = $db->sql_query($sql); 2952 $row = $db->sql_fetchrow($result); 2953 $db->sql_freeresult($result); 2954 2955 $sql = 'SELECT ((SUM(size) * 8.0) * 1024.0) as dbsize 2956 FROM sysfiles'; 2957 2958 if ($row) 2959 { 2960 // Azure stats are stored elsewhere 2961 if (strpos($row['mssql_version'], 'SQL Azure') !== false) 2962 { 2963 $sql = 'SELECT ((SUM(reserved_page_count) * 8.0) * 1024.0) as dbsize 2964 FROM sys.dm_db_partition_stats'; 2965 } 2966 } 2967 2968 $result = $db->sql_query($sql, 7200); 2969 $database_size = ($row = $db->sql_fetchrow($result)) ? $row['dbsize'] : false; 2970 $db->sql_freeresult($result); 2971 break; 2972 2973 case 'postgres': 2974 $database = $db->get_db_name(); 2975 2976 if (strpos($database, '.') !== false) 2977 { 2978 $database = explode('.', $database)[0]; 2979 } 2980 2981 $sql = "SELECT pg_database_size('" . $database . "') AS dbsize"; 2982 $result = $db->sql_query($sql, 7200); 2983 $row = $db->sql_fetchrow($result); 2984 $database_size = !empty($row['dbsize']) ? $row['dbsize'] : false; 2985 $db->sql_freeresult($result); 2986 break; 2987 2988 case 'oracle': 2989 $sql = 'SELECT SUM(bytes) as dbsize 2990 FROM user_segments'; 2991 $result = $db->sql_query($sql, 7200); 2992 $database_size = ($row = $db->sql_fetchrow($result)) ? $row['dbsize'] : false; 2993 $db->sql_freeresult($result); 2994 break; 2995 } 2996 2997 $database_size = ($database_size !== false) ? get_formatted_filesize($database_size) : $user->lang['NOT_AVAILABLE']; 2998 2999 return $database_size; 3000 } 3001 3002 /* 3003 * Tidy Warnings 3004 * Remove all warnings which have now expired from the database 3005 * The duration of a warning can be defined by the administrator 3006 * This only removes the warning and reduces the associated count, 3007 * it does not remove the user note recording the contents of the warning 3008 */ 3009 function tidy_warnings() 3010 { 3011 global $db, $config; 3012 3013 $expire_date = time() - ($config['warnings_expire_days'] * 86400); 3014 $warning_list = $user_list = array(); 3015 3016 $sql = 'SELECT * FROM ' . WARNINGS_TABLE . " 3017 WHERE warning_time < $expire_date"; 3018 $result = $db->sql_query($sql); 3019 3020 while ($row = $db->sql_fetchrow($result)) 3021 { 3022 $warning_list[] = $row['warning_id']; 3023 $user_list[$row['user_id']] = isset($user_list[$row['user_id']]) ? ++$user_list[$row['user_id']] : 1; 3024 } 3025 $db->sql_freeresult($result); 3026 3027 if (count($warning_list)) 3028 { 3029 $db->sql_transaction('begin'); 3030 3031 $sql = 'DELETE FROM ' . WARNINGS_TABLE . ' 3032 WHERE ' . $db->sql_in_set('warning_id', $warning_list); 3033 $db->sql_query($sql); 3034 3035 foreach ($user_list as $user_id => $value) 3036 { 3037 $sql = 'UPDATE ' . USERS_TABLE . " SET user_warnings = user_warnings - $value 3038 WHERE user_id = $user_id"; 3039 $db->sql_query($sql); 3040 } 3041 3042 $db->sql_transaction('commit'); 3043 } 3044 3045 $config->set('warnings_last_gc', time(), false); 3046 } 3047 3048 /** 3049 * Tidy database, doing some maintanance tasks 3050 */ 3051 function tidy_database() 3052 { 3053 global $config, $db; 3054 3055 // Here we check permission consistency 3056 3057 // Sometimes, it can happen permission tables having forums listed which do not exist 3058 $sql = 'SELECT forum_id 3059 FROM ' . FORUMS_TABLE; 3060 $result = $db->sql_query($sql); 3061 3062 $forum_ids = array(0); 3063 while ($row = $db->sql_fetchrow($result)) 3064 { 3065 $forum_ids[] = $row['forum_id']; 3066 } 3067 $db->sql_freeresult($result); 3068 3069 $db->sql_transaction('begin'); 3070 3071 // Delete those rows from the acl tables not having listed the forums above 3072 $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . ' 3073 WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true); 3074 $db->sql_query($sql); 3075 3076 $sql = 'DELETE FROM ' . ACL_USERS_TABLE . ' 3077 WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true); 3078 $db->sql_query($sql); 3079 3080 $db->sql_transaction('commit'); 3081 3082 $config->set('database_last_gc', time(), false); 3083 } 3084 3085 /** 3086 * Add permission language - this will make sure custom files will be included 3087 */ 3088 function add_permission_language() 3089 { 3090 global $user, $phpEx, $phpbb_extension_manager; 3091 3092 // add permission language files from extensions 3093 $finder = $phpbb_extension_manager->get_finder(); 3094 3095 $lang_files = $finder 3096 ->prefix('permissions_') 3097 ->suffix(".$phpEx") 3098 ->core_path('language/') 3099 ->extension_directory('/language') 3100 ->find(); 3101 3102 foreach ($lang_files as $lang_file => $ext_name) 3103 { 3104 if ($ext_name === '/') 3105 { 3106 $user->add_lang($lang_file); 3107 } 3108 else 3109 { 3110 $user->add_lang_ext($ext_name, $lang_file); 3111 } 3112 } 3113 } 3114 3115 /** 3116 * Enables a particular flag in a bitfield column of a given table. 3117 * 3118 * @param string $table_name The table to update 3119 * @param string $column_name The column containing a bitfield to update 3120 * @param int $flag The binary flag which is OR-ed with the current column value 3121 * @param string $sql_more This string is attached to the sql query generated to update the table. 3122 * 3123 * @return null 3124 */ 3125 function enable_bitfield_column_flag($table_name, $column_name, $flag, $sql_more = '') 3126 { 3127 global $db; 3128 3129 $sql = 'UPDATE ' . $table_name . ' 3130 SET ' . $column_name . ' = ' . $db->sql_bit_or($column_name, $flag) . ' 3131 ' . $sql_more; 3132 $db->sql_query($sql); 3133 } 3134 3135 function display_ban_end_options() 3136 { 3137 global $user, $template; 3138 3139 // Ban length options 3140 $ban_end_text = array(0 => $user->lang['PERMANENT'], 30 => $user->lang['30_MINS'], 60 => $user->lang['1_HOUR'], 360 => $user->lang['6_HOURS'], 1440 => $user->lang['1_DAY'], 10080 => $user->lang['7_DAYS'], 20160 => $user->lang['2_WEEKS'], 40320 => $user->lang['1_MONTH'], -1 => $user->lang['UNTIL'] . ' -> '); 3141 3142 $ban_end_options = ''; 3143 foreach ($ban_end_text as $length => $text) 3144 { 3145 $ban_end_options .= '<option value="' . $length . '">' . $text . '</option>'; 3146 } 3147 3148 $template->assign_vars(array( 3149 'S_BAN_END_OPTIONS' => $ban_end_options 3150 )); 3151 } 3152 3153 /** 3154 * Display ban options 3155 */ 3156 function display_ban_options($mode) 3157 { 3158 global $user, $db, $template; 3159 3160 switch ($mode) 3161 { 3162 case 'user': 3163 3164 $field = 'username'; 3165 3166 $sql = 'SELECT b.*, u.user_id, u.username, u.username_clean 3167 FROM ' . BANLIST_TABLE . ' b, ' . USERS_TABLE . ' u 3168 WHERE (b.ban_end >= ' . time() . ' 3169 OR b.ban_end = 0) 3170 AND u.user_id = b.ban_userid 3171 ORDER BY u.username_clean ASC'; 3172 break; 3173 3174 case 'ip': 3175 3176 $field = 'ban_ip'; 3177 3178 $sql = 'SELECT * 3179 FROM ' . BANLIST_TABLE . ' 3180 WHERE (ban_end >= ' . time() . " 3181 OR ban_end = 0) 3182 AND ban_ip <> '' 3183 ORDER BY ban_ip"; 3184 break; 3185 3186 case 'email': 3187 3188 $field = 'ban_email'; 3189 3190 $sql = 'SELECT * 3191 FROM ' . BANLIST_TABLE . ' 3192 WHERE (ban_end >= ' . time() . " 3193 OR ban_end = 0) 3194 AND ban_email <> '' 3195 ORDER BY ban_email"; 3196 break; 3197 } 3198 $result = $db->sql_query($sql); 3199 3200 $banned_options = $excluded_options = array(); 3201 while ($row = $db->sql_fetchrow($result)) 3202 { 3203 $option = '<option value="' . $row['ban_id'] . '">' . $row[$field] . '</option>'; 3204 3205 if ($row['ban_exclude']) 3206 { 3207 $excluded_options[] = $option; 3208 } 3209 else 3210 { 3211 $banned_options[] = $option; 3212 } 3213 3214 $time_length = ($row['ban_end']) ? ($row['ban_end'] - $row['ban_start']) / 60 : 0; 3215 3216 if ($time_length == 0) 3217 { 3218 // Banned permanently 3219 $ban_length = $user->lang['PERMANENT']; 3220 } 3221 else if (isset($ban_end_text[$time_length])) 3222 { 3223 // Banned for a given duration 3224 $ban_length = $user->lang('BANNED_UNTIL_DURATION', $ban_end_text[$time_length], $user->format_date($row['ban_end'], false, true)); 3225 } 3226 else 3227 { 3228 // Banned until given date 3229 $ban_length = $user->lang('BANNED_UNTIL_DATE', $user->format_date($row['ban_end'], false, true)); 3230 } 3231 3232 $template->assign_block_vars('bans', array( 3233 'BAN_ID' => (int) $row['ban_id'], 3234 'LENGTH' => $ban_length, 3235 'A_LENGTH' => addslashes($ban_length), 3236 'REASON' => $row['ban_reason'], 3237 'A_REASON' => addslashes($row['ban_reason']), 3238 'GIVE_REASON' => $row['ban_give_reason'], 3239 'A_GIVE_REASON' => addslashes($row['ban_give_reason']), 3240 )); 3241 } 3242 $db->sql_freeresult($result); 3243 3244 $options = ''; 3245 if ($excluded_options) 3246 { 3247 $options .= '<optgroup label="' . $user->lang['OPTIONS_EXCLUDED'] . '">'; 3248 $options .= implode('', $excluded_options); 3249 $options .= '</optgroup>'; 3250 } 3251 3252 if ($banned_options) 3253 { 3254 $options .= '<optgroup label="' . $user->lang['OPTIONS_BANNED'] . '">'; 3255 $options .= implode('', $banned_options); 3256 $options .= '</optgroup>'; 3257 } 3258 3259 $template->assign_vars(array( 3260 'S_BANNED_OPTIONS' => ($banned_options || $excluded_options) ? true : false, 3261 'BANNED_OPTIONS' => $options, 3262 )); 3263 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |