[ Index ] |
PHP Cross Reference of phpBB-3.2.2-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * This file is part of the phpBB Forum Software package. 5 * 6 * @copyright (c) phpBB Limited <https://www.phpbb.com> 7 * @license GNU General Public License, version 2 (GPL-2.0) 8 * 9 * For full copyright and license information, please see 10 * the docs/CREDITS.txt file. 11 * 12 */ 13 14 /** 15 * @ignore 16 */ 17 if (!defined('IN_PHPBB')) 18 { 19 exit; 20 } 21 22 /** 23 * Recalculate Nested Sets 24 * 25 * @param int $new_id first left_id (should start with 1) 26 * @param string $pkey primary key-column (containing the id for the parent_id of the children) 27 * @param string $table constant or fullname of the table 28 * @param int $parent_id parent_id of the current set (default = 0) 29 * @param array $where contains strings to compare closer on the where statement (additional) 30 */ 31 function recalc_nested_sets(&$new_id, $pkey, $table, $parent_id = 0, $where = array()) 32 { 33 global $db; 34 35 $sql = 'SELECT * 36 FROM ' . $table . ' 37 WHERE parent_id = ' . (int) $parent_id . 38 ((!empty($where)) ? ' AND ' . implode(' AND ', $where) : '') . ' 39 ORDER BY left_id ASC'; 40 $result = $db->sql_query($sql); 41 while ($row = $db->sql_fetchrow($result)) 42 { 43 // First we update the left_id for this module 44 if ($row['left_id'] != $new_id) 45 { 46 $db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('left_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}"); 47 } 48 $new_id++; 49 50 // Then we go through any children and update their left/right id's 51 recalc_nested_sets($new_id, $pkey, $table, $row[$pkey], $where); 52 53 // Then we come back and update the right_id for this module 54 if ($row['right_id'] != $new_id) 55 { 56 $db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('right_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}"); 57 } 58 $new_id++; 59 } 60 $db->sql_freeresult($result); 61 } 62 63 /** 64 * Simple version of jumpbox, just lists authed forums 65 */ 66 function make_forum_select($select_id = false, $ignore_id = false, $ignore_acl = false, $ignore_nonpost = false, $ignore_emptycat = true, $only_acl_post = false, $return_array = false) 67 { 68 global $db, $auth, $phpbb_dispatcher; 69 70 // This query is identical to the jumpbox one 71 $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, forum_flags, forum_options, left_id, right_id 72 FROM ' . FORUMS_TABLE . ' 73 ORDER BY left_id ASC'; 74 $result = $db->sql_query($sql, 600); 75 76 $rowset = array(); 77 while ($row = $db->sql_fetchrow($result)) 78 { 79 $rowset[(int) $row['forum_id']] = $row; 80 } 81 $db->sql_freeresult($result); 82 83 $right = 0; 84 $padding_store = array('0' => ''); 85 $padding = ''; 86 $forum_list = ($return_array) ? array() : ''; 87 88 /** 89 * Modify the forum list data 90 * 91 * @event core.make_forum_select_modify_forum_list 92 * @var array rowset Array with the forums list data 93 * @since 3.1.10-RC1 94 */ 95 $vars = array('rowset'); 96 extract($phpbb_dispatcher->trigger_event('core.make_forum_select_modify_forum_list', compact($vars))); 97 98 // Sometimes it could happen that forums will be displayed here not be displayed within the index page 99 // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions. 100 // If this happens, the padding could be "broken" 101 102 foreach ($rowset as $row) 103 { 104 if ($row['left_id'] < $right) 105 { 106 $padding .= ' '; 107 $padding_store[$row['parent_id']] = $padding; 108 } 109 else if ($row['left_id'] > $right + 1) 110 { 111 $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : ''; 112 } 113 114 $right = $row['right_id']; 115 $disabled = false; 116 117 if (!$ignore_acl && $auth->acl_gets(array('f_list', 'a_forum', 'a_forumadd', 'a_forumdel'), $row['forum_id'])) 118 { 119 if ($only_acl_post && !$auth->acl_get('f_post', $row['forum_id']) || (!$auth->acl_get('m_approve', $row['forum_id']) && !$auth->acl_get('f_noapprove', $row['forum_id']))) 120 { 121 $disabled = true; 122 } 123 } 124 else if (!$ignore_acl) 125 { 126 continue; 127 } 128 129 if ( 130 ((is_array($ignore_id) && in_array($row['forum_id'], $ignore_id)) || $row['forum_id'] == $ignore_id) 131 || 132 // Non-postable forum with no subforums, don't display 133 ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']) && $ignore_emptycat) 134 || 135 ($row['forum_type'] != FORUM_POST && $ignore_nonpost) 136 ) 137 { 138 $disabled = true; 139 } 140 141 if ($return_array) 142 { 143 // Include some more information... 144 $selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? true : false) : (($row['forum_id'] == $select_id) ? true : false); 145 $forum_list[$row['forum_id']] = array_merge(array('padding' => $padding, 'selected' => ($selected && !$disabled), 'disabled' => $disabled), $row); 146 } 147 else 148 { 149 $selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? ' selected="selected"' : '') : (($row['forum_id'] == $select_id) ? ' selected="selected"' : ''); 150 $forum_list .= '<option value="' . $row['forum_id'] . '"' . (($disabled) ? ' disabled="disabled" class="disabled-option"' : $selected) . '>' . $padding . $row['forum_name'] . '</option>'; 151 } 152 } 153 unset($padding_store, $rowset); 154 155 return $forum_list; 156 } 157 158 /** 159 * Generate size select options 160 */ 161 function size_select_options($size_compare) 162 { 163 global $user; 164 165 $size_types_text = array($user->lang['BYTES'], $user->lang['KIB'], $user->lang['MIB']); 166 $size_types = array('b', 'kb', 'mb'); 167 168 $s_size_options = ''; 169 170 for ($i = 0, $size = count($size_types_text); $i < $size; $i++) 171 { 172 $selected = ($size_compare == $size_types[$i]) ? ' selected="selected"' : ''; 173 $s_size_options .= '<option value="' . $size_types[$i] . '"' . $selected . '>' . $size_types_text[$i] . '</option>'; 174 } 175 176 return $s_size_options; 177 } 178 179 /** 180 * Generate list of groups (option fields without select) 181 * 182 * @param int $group_id The default group id to mark as selected 183 * @param array $exclude_ids The group ids to exclude from the list, false (default) if you whish to exclude no id 184 * @param int $manage_founder If set to false (default) all groups are returned, if 0 only those groups returned not being managed by founders only, if 1 only those groups returned managed by founders only. 185 * 186 * @return string The list of options. 187 */ 188 function group_select_options($group_id, $exclude_ids = false, $manage_founder = false) 189 { 190 global $db, $config, $phpbb_container; 191 192 /** @var \phpbb\group\helper $group_helper */ 193 $group_helper = $phpbb_container->get('group_helper'); 194 195 $exclude_sql = ($exclude_ids !== false && count($exclude_ids)) ? 'WHERE ' . $db->sql_in_set('group_id', array_map('intval', $exclude_ids), true) : ''; 196 $sql_and = (!$config['coppa_enable']) ? (($exclude_sql) ? ' AND ' : ' WHERE ') . "group_name <> 'REGISTERED_COPPA'" : ''; 197 $sql_founder = ($manage_founder !== false) ? (($exclude_sql || $sql_and) ? ' AND ' : ' WHERE ') . 'group_founder_manage = ' . (int) $manage_founder : ''; 198 199 $sql = 'SELECT group_id, group_name, group_type 200 FROM ' . GROUPS_TABLE . " 201 $exclude_sql 202 $sql_and 203 $sql_founder 204 ORDER BY group_type DESC, group_name ASC"; 205 $result = $db->sql_query($sql); 206 207 $s_group_options = ''; 208 while ($row = $db->sql_fetchrow($result)) 209 { 210 $selected = ($row['group_id'] == $group_id) ? ' selected="selected"' : ''; 211 $s_group_options .= '<option' . (($row['group_type'] == GROUP_SPECIAL) ? ' class="sep"' : '') . ' value="' . $row['group_id'] . '"' . $selected . '>' . $group_helper->get_name($row['group_name']) . '</option>'; 212 } 213 $db->sql_freeresult($result); 214 215 return $s_group_options; 216 } 217 218 /** 219 * Obtain authed forums list 220 */ 221 function get_forum_list($acl_list = 'f_list', $id_only = true, $postable_only = false, $no_cache = false) 222 { 223 global $db, $auth, $phpbb_dispatcher; 224 static $forum_rows; 225 226 if (!isset($forum_rows)) 227 { 228 // This query is identical to the jumpbox one 229 $expire_time = ($no_cache) ? 0 : 600; 230 231 $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id 232 FROM ' . FORUMS_TABLE . ' 233 ORDER BY left_id ASC'; 234 $result = $db->sql_query($sql, $expire_time); 235 236 $forum_rows = array(); 237 238 $right = $padding = 0; 239 $padding_store = array('0' => 0); 240 241 while ($row = $db->sql_fetchrow($result)) 242 { 243 if ($row['left_id'] < $right) 244 { 245 $padding++; 246 $padding_store[$row['parent_id']] = $padding; 247 } 248 else if ($row['left_id'] > $right + 1) 249 { 250 // Ok, if the $padding_store for this parent is empty there is something wrong. For now we will skip over it. 251 // @todo digging deep to find out "how" this can happen. 252 $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : $padding; 253 } 254 255 $right = $row['right_id']; 256 $row['padding'] = $padding; 257 258 $forum_rows[] = $row; 259 } 260 $db->sql_freeresult($result); 261 unset($padding_store); 262 } 263 264 $rowset = array(); 265 foreach ($forum_rows as $row) 266 { 267 if ($postable_only && $row['forum_type'] != FORUM_POST) 268 { 269 continue; 270 } 271 272 if ($acl_list == '' || ($acl_list != '' && $auth->acl_gets($acl_list, $row['forum_id']))) 273 { 274 $rowset[] = ($id_only) ? (int) $row['forum_id'] : $row; 275 } 276 } 277 278 /** 279 * Modify the forum list data 280 * 281 * @event core.get_forum_list_modify_data 282 * @var array rowset Array with the forum list data 283 * @since 3.1.10-RC1 284 */ 285 $vars = array('rowset'); 286 extract($phpbb_dispatcher->trigger_event('core.get_forum_list_modify_data', compact($vars))); 287 288 return $rowset; 289 } 290 291 /** 292 * Get forum branch 293 */ 294 function get_forum_branch($forum_id, $type = 'all', $order = 'descending', $include_forum = true) 295 { 296 global $db; 297 298 switch ($type) 299 { 300 case 'parents': 301 $condition = 'f1.left_id BETWEEN f2.left_id AND f2.right_id'; 302 break; 303 304 case 'children': 305 $condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id'; 306 break; 307 308 default: 309 $condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id OR f1.left_id BETWEEN f2.left_id AND f2.right_id'; 310 break; 311 } 312 313 $rows = array(); 314 315 $sql = 'SELECT f2.* 316 FROM ' . FORUMS_TABLE . ' f1 317 LEFT JOIN ' . FORUMS_TABLE . " f2 ON ($condition) 318 WHERE f1.forum_id = $forum_id 319 ORDER BY f2.left_id " . (($order == 'descending') ? 'ASC' : 'DESC'); 320 $result = $db->sql_query($sql); 321 322 while ($row = $db->sql_fetchrow($result)) 323 { 324 if (!$include_forum && $row['forum_id'] == $forum_id) 325 { 326 continue; 327 } 328 329 $rows[] = $row; 330 } 331 $db->sql_freeresult($result); 332 333 return $rows; 334 } 335 336 /** 337 * Copies permissions from one forum to others 338 * 339 * @param int $src_forum_id The source forum we want to copy permissions from 340 * @param array $dest_forum_ids The destination forum(s) we want to copy to 341 * @param bool $clear_dest_perms True if destination permissions should be deleted 342 * @param bool $add_log True if log entry should be added 343 * 344 * @return bool False on error 345 */ 346 function copy_forum_permissions($src_forum_id, $dest_forum_ids, $clear_dest_perms = true, $add_log = true) 347 { 348 global $db, $user, $phpbb_log; 349 350 // Only one forum id specified 351 if (!is_array($dest_forum_ids)) 352 { 353 $dest_forum_ids = array($dest_forum_ids); 354 } 355 356 // Make sure forum ids are integers 357 $src_forum_id = (int) $src_forum_id; 358 $dest_forum_ids = array_map('intval', $dest_forum_ids); 359 360 // No source forum or no destination forums specified 361 if (empty($src_forum_id) || empty($dest_forum_ids)) 362 { 363 return false; 364 } 365 366 // Check if source forum exists 367 $sql = 'SELECT forum_name 368 FROM ' . FORUMS_TABLE . ' 369 WHERE forum_id = ' . $src_forum_id; 370 $result = $db->sql_query($sql); 371 $src_forum_name = $db->sql_fetchfield('forum_name'); 372 $db->sql_freeresult($result); 373 374 // Source forum doesn't exist 375 if (empty($src_forum_name)) 376 { 377 return false; 378 } 379 380 // Check if destination forums exists 381 $sql = 'SELECT forum_id, forum_name 382 FROM ' . FORUMS_TABLE . ' 383 WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids); 384 $result = $db->sql_query($sql); 385 386 $dest_forum_ids = $dest_forum_names = array(); 387 while ($row = $db->sql_fetchrow($result)) 388 { 389 $dest_forum_ids[] = (int) $row['forum_id']; 390 $dest_forum_names[] = $row['forum_name']; 391 } 392 $db->sql_freeresult($result); 393 394 // No destination forum exists 395 if (empty($dest_forum_ids)) 396 { 397 return false; 398 } 399 400 // From the mysql documentation: 401 // Prior to MySQL 4.0.14, the target table of the INSERT statement cannot appear 402 // in the FROM clause of the SELECT part of the query. This limitation is lifted in 4.0.14. 403 // Due to this we stay on the safe side if we do the insertion "the manual way" 404 405 // Rowsets we're going to insert 406 $users_sql_ary = $groups_sql_ary = array(); 407 408 // Query acl users table for source forum data 409 $sql = 'SELECT user_id, auth_option_id, auth_role_id, auth_setting 410 FROM ' . ACL_USERS_TABLE . ' 411 WHERE forum_id = ' . $src_forum_id; 412 $result = $db->sql_query($sql); 413 414 while ($row = $db->sql_fetchrow($result)) 415 { 416 $row = array( 417 'user_id' => (int) $row['user_id'], 418 'auth_option_id' => (int) $row['auth_option_id'], 419 'auth_role_id' => (int) $row['auth_role_id'], 420 'auth_setting' => (int) $row['auth_setting'], 421 ); 422 423 foreach ($dest_forum_ids as $dest_forum_id) 424 { 425 $users_sql_ary[] = $row + array('forum_id' => $dest_forum_id); 426 } 427 } 428 $db->sql_freeresult($result); 429 430 // Query acl groups table for source forum data 431 $sql = 'SELECT group_id, auth_option_id, auth_role_id, auth_setting 432 FROM ' . ACL_GROUPS_TABLE . ' 433 WHERE forum_id = ' . $src_forum_id; 434 $result = $db->sql_query($sql); 435 436 while ($row = $db->sql_fetchrow($result)) 437 { 438 $row = array( 439 'group_id' => (int) $row['group_id'], 440 'auth_option_id' => (int) $row['auth_option_id'], 441 'auth_role_id' => (int) $row['auth_role_id'], 442 'auth_setting' => (int) $row['auth_setting'], 443 ); 444 445 foreach ($dest_forum_ids as $dest_forum_id) 446 { 447 $groups_sql_ary[] = $row + array('forum_id' => $dest_forum_id); 448 } 449 } 450 $db->sql_freeresult($result); 451 452 $db->sql_transaction('begin'); 453 454 // Clear current permissions of destination forums 455 if ($clear_dest_perms) 456 { 457 $sql = 'DELETE FROM ' . ACL_USERS_TABLE . ' 458 WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids); 459 $db->sql_query($sql); 460 461 $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . ' 462 WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids); 463 $db->sql_query($sql); 464 } 465 466 $db->sql_multi_insert(ACL_USERS_TABLE, $users_sql_ary); 467 $db->sql_multi_insert(ACL_GROUPS_TABLE, $groups_sql_ary); 468 469 if ($add_log) 470 { 471 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_FORUM_COPIED_PERMISSIONS', false, array($src_forum_name, implode(', ', $dest_forum_names))); 472 } 473 474 $db->sql_transaction('commit'); 475 476 return true; 477 } 478 479 /** 480 * Get physical file listing 481 */ 482 function filelist($rootdir, $dir = '', $type = 'gif|jpg|jpeg|png') 483 { 484 $matches = array($dir => array()); 485 486 // Remove initial / if present 487 $rootdir = (substr($rootdir, 0, 1) == '/') ? substr($rootdir, 1) : $rootdir; 488 // Add closing / if not present 489 $rootdir = ($rootdir && substr($rootdir, -1) != '/') ? $rootdir . '/' : $rootdir; 490 491 // Remove initial / if present 492 $dir = (substr($dir, 0, 1) == '/') ? substr($dir, 1) : $dir; 493 // Add closing / if not present 494 $dir = ($dir && substr($dir, -1) != '/') ? $dir . '/' : $dir; 495 496 if (!is_dir($rootdir . $dir)) 497 { 498 return $matches; 499 } 500 501 $dh = @opendir($rootdir . $dir); 502 503 if (!$dh) 504 { 505 return $matches; 506 } 507 508 while (($fname = readdir($dh)) !== false) 509 { 510 if (is_file("$rootdir$dir$fname")) 511 { 512 if (filesize("$rootdir$dir$fname") && preg_match('#\.' . $type . '$#i', $fname)) 513 { 514 $matches[$dir][] = $fname; 515 } 516 } 517 else if ($fname[0] != '.' && is_dir("$rootdir$dir$fname")) 518 { 519 $matches += filelist($rootdir, "$dir$fname", $type); 520 } 521 } 522 closedir($dh); 523 524 return $matches; 525 } 526 527 /** 528 * Move topic(s) 529 */ 530 function move_topics($topic_ids, $forum_id, $auto_sync = true) 531 { 532 global $db, $phpbb_dispatcher; 533 534 if (empty($topic_ids)) 535 { 536 return; 537 } 538 539 $forum_ids = array($forum_id); 540 541 if (!is_array($topic_ids)) 542 { 543 $topic_ids = array($topic_ids); 544 } 545 546 $sql = 'DELETE FROM ' . TOPICS_TABLE . ' 547 WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids) . ' 548 AND forum_id = ' . $forum_id; 549 $db->sql_query($sql); 550 551 if ($auto_sync) 552 { 553 $sql = 'SELECT DISTINCT forum_id 554 FROM ' . TOPICS_TABLE . ' 555 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 556 $result = $db->sql_query($sql); 557 558 while ($row = $db->sql_fetchrow($result)) 559 { 560 $forum_ids[] = $row['forum_id']; 561 } 562 $db->sql_freeresult($result); 563 } 564 565 $table_ary = array(TOPICS_TABLE, POSTS_TABLE, LOG_TABLE, DRAFTS_TABLE, TOPICS_TRACK_TABLE); 566 567 /** 568 * Perform additional actions before topics move 569 * 570 * @event core.move_topics_before_query 571 * @var array table_ary Array of tables from which forum_id will be updated for all rows that hold the moved topics 572 * @var array topic_ids Array of the moved topic ids 573 * @var string forum_id The forum id from where the topics are moved 574 * @var array forum_ids Array of the forums where the topics are moving (includes also forum_id) 575 * @var bool auto_sync Whether or not to perform auto sync 576 * @since 3.1.5-RC1 577 */ 578 $vars = array( 579 'table_ary', 580 'topic_ids', 581 'forum_id', 582 'forum_ids', 583 'auto_sync', 584 ); 585 extract($phpbb_dispatcher->trigger_event('core.move_topics_before_query', compact($vars))); 586 587 foreach ($table_ary as $table) 588 { 589 $sql = "UPDATE $table 590 SET forum_id = $forum_id 591 WHERE " . $db->sql_in_set('topic_id', $topic_ids); 592 $db->sql_query($sql); 593 } 594 unset($table_ary); 595 596 if ($auto_sync) 597 { 598 sync('forum', 'forum_id', $forum_ids, true, true); 599 unset($forum_ids); 600 } 601 } 602 603 /** 604 * Move post(s) 605 */ 606 function move_posts($post_ids, $topic_id, $auto_sync = true) 607 { 608 global $db, $phpbb_dispatcher; 609 610 if (!is_array($post_ids)) 611 { 612 $post_ids = array($post_ids); 613 } 614 615 $forum_ids = array(); 616 $topic_ids = array($topic_id); 617 618 $sql = 'SELECT DISTINCT topic_id, forum_id 619 FROM ' . POSTS_TABLE . ' 620 WHERE ' . $db->sql_in_set('post_id', $post_ids); 621 $result = $db->sql_query($sql); 622 623 while ($row = $db->sql_fetchrow($result)) 624 { 625 $forum_ids[] = (int) $row['forum_id']; 626 $topic_ids[] = (int) $row['topic_id']; 627 } 628 $db->sql_freeresult($result); 629 630 $sql = 'SELECT forum_id 631 FROM ' . TOPICS_TABLE . ' 632 WHERE topic_id = ' . $topic_id; 633 $result = $db->sql_query($sql); 634 $forum_row = $db->sql_fetchrow($result); 635 $db->sql_freeresult($result); 636 637 if (!$forum_row) 638 { 639 trigger_error('NO_TOPIC'); 640 } 641 642 /** 643 * Perform additional actions before moving posts 644 * 645 * @event core.move_posts_before 646 * @var array post_ids Array of post ids to move 647 * @var int topic_id The topic id the posts are moved to 648 * @var bool auto_sync Whether or not to perform auto sync 649 * @var array forum_ids Array of the forum ids the posts are moved from 650 * @var array topic_ids Array of the topic ids the posts are moved from 651 * @var array forum_row Array with the forum id of the topic the posts are moved to 652 * @since 3.1.7-RC1 653 */ 654 $vars = array( 655 'post_ids', 656 'topic_id', 657 'auto_sync', 658 'forum_ids', 659 'topic_ids', 660 'forum_row', 661 ); 662 extract($phpbb_dispatcher->trigger_event('core.move_posts_before', compact($vars))); 663 664 $sql = 'UPDATE ' . POSTS_TABLE . ' 665 SET forum_id = ' . (int) $forum_row['forum_id'] . ", topic_id = $topic_id 666 WHERE " . $db->sql_in_set('post_id', $post_ids); 667 $db->sql_query($sql); 668 669 $sql = 'UPDATE ' . ATTACHMENTS_TABLE . " 670 SET topic_id = $topic_id, in_message = 0 671 WHERE " . $db->sql_in_set('post_msg_id', $post_ids); 672 $db->sql_query($sql); 673 674 /** 675 * Perform additional actions after moving posts 676 * 677 * @event core.move_posts_after 678 * @var array post_ids Array of the moved post ids 679 * @var int topic_id The topic id the posts are moved to 680 * @var bool auto_sync Whether or not to perform auto sync 681 * @var array forum_ids Array of the forum ids the posts are moved from 682 * @var array topic_ids Array of the topic ids the posts are moved from 683 * @var array forum_row Array with the forum id of the topic the posts are moved to 684 * @since 3.1.7-RC1 685 */ 686 $vars = array( 687 'post_ids', 688 'topic_id', 689 'auto_sync', 690 'forum_ids', 691 'topic_ids', 692 'forum_row', 693 ); 694 extract($phpbb_dispatcher->trigger_event('core.move_posts_after', compact($vars))); 695 696 if ($auto_sync) 697 { 698 $forum_ids[] = (int) $forum_row['forum_id']; 699 700 sync('topic_reported', 'topic_id', $topic_ids); 701 sync('topic_attachment', 'topic_id', $topic_ids); 702 sync('topic', 'topic_id', $topic_ids, true); 703 sync('forum', 'forum_id', $forum_ids, true, true); 704 705 /** 706 * Perform additional actions after move post sync 707 * 708 * @event core.move_posts_sync_after 709 * @var array post_ids Array of the moved post ids 710 * @var int topic_id The topic id the posts are moved to 711 * @var bool auto_sync Whether or not to perform auto sync 712 * @var array forum_ids Array of the forum ids the posts are moved from 713 * @var array topic_ids Array of the topic ids the posts are moved from 714 * @var array forum_row Array with the forum id of the topic the posts are moved to 715 * @since 3.1.11-RC1 716 */ 717 $vars = array( 718 'post_ids', 719 'topic_id', 720 'auto_sync', 721 'forum_ids', 722 'topic_ids', 723 'forum_row', 724 ); 725 extract($phpbb_dispatcher->trigger_event('core.move_posts_sync_after', compact($vars))); 726 } 727 728 // Update posted information 729 update_posted_info($topic_ids); 730 } 731 732 /** 733 * Remove topic(s) 734 */ 735 function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_sync = true, $call_delete_posts = true) 736 { 737 global $db, $config, $phpbb_container, $phpbb_dispatcher; 738 739 $approved_topics = 0; 740 $forum_ids = $topic_ids = array(); 741 742 if ($where_type === 'range') 743 { 744 $where_clause = $where_ids; 745 } 746 else 747 { 748 $where_ids = (is_array($where_ids)) ? array_unique($where_ids) : array($where_ids); 749 750 if (!count($where_ids)) 751 { 752 return array('topics' => 0, 'posts' => 0); 753 } 754 755 $where_clause = $db->sql_in_set($where_type, $where_ids); 756 } 757 758 // Making sure that delete_posts does not call delete_topics again... 759 $return = array( 760 'posts' => ($call_delete_posts) ? delete_posts($where_type, $where_ids, false, true, $post_count_sync, false) : 0, 761 ); 762 763 $sql = 'SELECT topic_id, forum_id, topic_visibility, topic_moved_id 764 FROM ' . TOPICS_TABLE . ' 765 WHERE ' . $where_clause; 766 $result = $db->sql_query($sql); 767 768 while ($row = $db->sql_fetchrow($result)) 769 { 770 $forum_ids[] = $row['forum_id']; 771 $topic_ids[] = $row['topic_id']; 772 773 if ($row['topic_visibility'] == ITEM_APPROVED && !$row['topic_moved_id']) 774 { 775 $approved_topics++; 776 } 777 } 778 $db->sql_freeresult($result); 779 780 $return['topics'] = count($topic_ids); 781 782 if (!count($topic_ids)) 783 { 784 return $return; 785 } 786 787 $db->sql_transaction('begin'); 788 789 $table_ary = array(BOOKMARKS_TABLE, TOPICS_TRACK_TABLE, TOPICS_POSTED_TABLE, POLL_VOTES_TABLE, POLL_OPTIONS_TABLE, TOPICS_WATCH_TABLE, TOPICS_TABLE); 790 791 /** 792 * Perform additional actions before topic(s) deletion 793 * 794 * @event core.delete_topics_before_query 795 * @var array table_ary Array of tables from which all rows will be deleted that hold a topic_id occuring in topic_ids 796 * @var array topic_ids Array of topic ids to delete 797 * @since 3.1.4-RC1 798 */ 799 $vars = array( 800 'table_ary', 801 'topic_ids', 802 ); 803 extract($phpbb_dispatcher->trigger_event('core.delete_topics_before_query', compact($vars))); 804 805 foreach ($table_ary as $table) 806 { 807 $sql = "DELETE FROM $table 808 WHERE " . $db->sql_in_set('topic_id', $topic_ids); 809 $db->sql_query($sql); 810 } 811 unset($table_ary); 812 813 /** 814 * Perform additional actions after topic(s) deletion 815 * 816 * @event core.delete_topics_after_query 817 * @var array topic_ids Array of topic ids that were deleted 818 * @since 3.1.4-RC1 819 */ 820 $vars = array( 821 'topic_ids', 822 ); 823 extract($phpbb_dispatcher->trigger_event('core.delete_topics_after_query', compact($vars))); 824 825 $moved_topic_ids = array(); 826 827 // update the other forums 828 $sql = 'SELECT topic_id, forum_id 829 FROM ' . TOPICS_TABLE . ' 830 WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids); 831 $result = $db->sql_query($sql); 832 833 while ($row = $db->sql_fetchrow($result)) 834 { 835 $forum_ids[] = $row['forum_id']; 836 $moved_topic_ids[] = $row['topic_id']; 837 } 838 $db->sql_freeresult($result); 839 840 if (count($moved_topic_ids)) 841 { 842 $sql = 'DELETE FROM ' . TOPICS_TABLE . ' 843 WHERE ' . $db->sql_in_set('topic_id', $moved_topic_ids); 844 $db->sql_query($sql); 845 } 846 847 $db->sql_transaction('commit'); 848 849 if ($auto_sync) 850 { 851 sync('forum', 'forum_id', array_unique($forum_ids), true, true); 852 sync('topic_reported', $where_type, $where_ids); 853 } 854 855 if ($approved_topics) 856 { 857 $config->increment('num_topics', $approved_topics * (-1), false); 858 } 859 860 /* @var $phpbb_notifications \phpbb\notification\manager */ 861 $phpbb_notifications = $phpbb_container->get('notification_manager'); 862 863 $phpbb_notifications->delete_notifications(array( 864 'notification.type.topic', 865 'notification.type.approve_topic', 866 'notification.type.topic_in_queue', 867 ), $topic_ids); 868 869 return $return; 870 } 871 872 /** 873 * Remove post(s) 874 */ 875 function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync = true, $post_count_sync = true, $call_delete_topics = true) 876 { 877 global $db, $config, $phpbb_root_path, $phpEx, $auth, $user, $phpbb_container, $phpbb_dispatcher; 878 879 // Notifications types to delete 880 $delete_notifications_types = array( 881 'notification.type.quote', 882 'notification.type.approve_post', 883 'notification.type.post_in_queue', 884 'notification.type.report_post', 885 ); 886 887 /** 888 * Perform additional actions before post(s) deletion 889 * 890 * @event core.delete_posts_before 891 * @var string where_type Variable containing posts deletion mode 892 * @var mixed where_ids Array or comma separated list of posts ids to delete 893 * @var bool auto_sync Flag indicating if topics/forums should be synchronized 894 * @var bool posted_sync Flag indicating if topics_posted table should be resynchronized 895 * @var bool post_count_sync Flag indicating if posts count should be resynchronized 896 * @var bool call_delete_topics Flag indicating if topics having no posts should be deleted 897 * @var array delete_notifications_types Array with notifications types to delete 898 * @since 3.1.0-a4 899 */ 900 $vars = array( 901 'where_type', 902 'where_ids', 903 'auto_sync', 904 'posted_sync', 905 'post_count_sync', 906 'call_delete_topics', 907 'delete_notifications_types', 908 ); 909 extract($phpbb_dispatcher->trigger_event('core.delete_posts_before', compact($vars))); 910 911 if ($where_type === 'range') 912 { 913 $where_clause = $where_ids; 914 } 915 else 916 { 917 if (is_array($where_ids)) 918 { 919 $where_ids = array_unique($where_ids); 920 } 921 else 922 { 923 $where_ids = array($where_ids); 924 } 925 926 if (!count($where_ids)) 927 { 928 return false; 929 } 930 931 $where_ids = array_map('intval', $where_ids); 932 933 /* Possible code for splitting post deletion 934 if (count($where_ids) >= 1001) 935 { 936 // Split into chunks of 1000 937 $chunks = array_chunk($where_ids, 1000); 938 939 foreach ($chunks as $_where_ids) 940 { 941 delete_posts($where_type, $_where_ids, $auto_sync, $posted_sync, $post_count_sync, $call_delete_topics); 942 } 943 944 return; 945 }*/ 946 947 $where_clause = $db->sql_in_set($where_type, $where_ids); 948 } 949 950 $approved_posts = 0; 951 $post_ids = $topic_ids = $forum_ids = $post_counts = $remove_topics = array(); 952 953 $sql = 'SELECT post_id, poster_id, post_visibility, post_postcount, topic_id, forum_id 954 FROM ' . POSTS_TABLE . ' 955 WHERE ' . $where_clause; 956 $result = $db->sql_query($sql); 957 958 while ($row = $db->sql_fetchrow($result)) 959 { 960 $post_ids[] = (int) $row['post_id']; 961 $poster_ids[] = (int) $row['poster_id']; 962 $topic_ids[] = (int) $row['topic_id']; 963 $forum_ids[] = (int) $row['forum_id']; 964 965 if ($row['post_postcount'] && $post_count_sync && $row['post_visibility'] == ITEM_APPROVED) 966 { 967 $post_counts[$row['poster_id']] = (!empty($post_counts[$row['poster_id']])) ? $post_counts[$row['poster_id']] + 1 : 1; 968 } 969 970 if ($row['post_visibility'] == ITEM_APPROVED) 971 { 972 $approved_posts++; 973 } 974 } 975 $db->sql_freeresult($result); 976 977 if (!count($post_ids)) 978 { 979 return false; 980 } 981 982 $db->sql_transaction('begin'); 983 984 $table_ary = array(POSTS_TABLE, REPORTS_TABLE); 985 986 /** 987 * Perform additional actions during post(s) deletion before running the queries 988 * 989 * @event core.delete_posts_in_transaction_before 990 * @var array post_ids Array with deleted posts' ids 991 * @var array poster_ids Array with deleted posts' author ids 992 * @var array topic_ids Array with deleted posts' topic ids 993 * @var array forum_ids Array with deleted posts' forum ids 994 * @var string where_type Variable containing posts deletion mode 995 * @var mixed where_ids Array or comma separated list of post ids to delete 996 * @var array delete_notifications_types Array with notifications types to delete 997 * @var array table_ary Array with table names to delete data from 998 * @since 3.1.7-RC1 999 */ 1000 $vars = array( 1001 'post_ids', 1002 'poster_ids', 1003 'topic_ids', 1004 'forum_ids', 1005 'where_type', 1006 'where_ids', 1007 'delete_notifications_types', 1008 'table_ary', 1009 ); 1010 extract($phpbb_dispatcher->trigger_event('core.delete_posts_in_transaction_before', compact($vars))); 1011 1012 foreach ($table_ary as $table) 1013 { 1014 $sql = "DELETE FROM $table 1015 WHERE " . $db->sql_in_set('post_id', $post_ids); 1016 $db->sql_query($sql); 1017 } 1018 unset($table_ary); 1019 1020 // Adjust users post counts 1021 if (count($post_counts) && $post_count_sync) 1022 { 1023 foreach ($post_counts as $poster_id => $substract) 1024 { 1025 $sql = 'UPDATE ' . USERS_TABLE . ' 1026 SET user_posts = 0 1027 WHERE user_id = ' . $poster_id . ' 1028 AND user_posts < ' . $substract; 1029 $db->sql_query($sql); 1030 1031 $sql = 'UPDATE ' . USERS_TABLE . ' 1032 SET user_posts = user_posts - ' . $substract . ' 1033 WHERE user_id = ' . $poster_id . ' 1034 AND user_posts >= ' . $substract; 1035 $db->sql_query($sql); 1036 } 1037 } 1038 1039 // Remove topics now having no posts? 1040 if (count($topic_ids)) 1041 { 1042 $sql = 'SELECT topic_id 1043 FROM ' . POSTS_TABLE . ' 1044 WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . ' 1045 GROUP BY topic_id'; 1046 $result = $db->sql_query($sql); 1047 1048 while ($row = $db->sql_fetchrow($result)) 1049 { 1050 $remove_topics[] = $row['topic_id']; 1051 } 1052 $db->sql_freeresult($result); 1053 1054 // Actually, those not within remove_topics should be removed. ;) 1055 $remove_topics = array_diff($topic_ids, $remove_topics); 1056 } 1057 1058 // Remove the message from the search index 1059 $search_type = $config['search_type']; 1060 1061 if (!class_exists($search_type)) 1062 { 1063 trigger_error('NO_SUCH_SEARCH_MODULE'); 1064 } 1065 1066 $error = false; 1067 $search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher); 1068 1069 if ($error) 1070 { 1071 trigger_error($error); 1072 } 1073 1074 $search->index_remove($post_ids, $poster_ids, $forum_ids); 1075 1076 /** @var \phpbb\attachment\manager $attachment_manager */ 1077 $attachment_manager = $phpbb_container->get('attachment.manager'); 1078 $attachment_manager->delete('post', $post_ids, false); 1079 unset($attachment_manager); 1080 1081 /** 1082 * Perform additional actions during post(s) deletion 1083 * 1084 * @event core.delete_posts_in_transaction 1085 * @var array post_ids Array with deleted posts' ids 1086 * @var array poster_ids Array with deleted posts' author ids 1087 * @var array topic_ids Array with deleted posts' topic ids 1088 * @var array forum_ids Array with deleted posts' forum ids 1089 * @var string where_type Variable containing posts deletion mode 1090 * @var mixed where_ids Array or comma separated list of posts ids to delete 1091 * @var array delete_notifications_types Array with notifications types to delete 1092 * @since 3.1.0-a4 1093 */ 1094 $vars = array( 1095 'post_ids', 1096 'poster_ids', 1097 'topic_ids', 1098 'forum_ids', 1099 'where_type', 1100 'where_ids', 1101 'delete_notifications_types', 1102 ); 1103 extract($phpbb_dispatcher->trigger_event('core.delete_posts_in_transaction', compact($vars))); 1104 1105 $db->sql_transaction('commit'); 1106 1107 /** 1108 * Perform additional actions after post(s) deletion 1109 * 1110 * @event core.delete_posts_after 1111 * @var array post_ids Array with deleted posts' ids 1112 * @var array poster_ids Array with deleted posts' author ids 1113 * @var array topic_ids Array with deleted posts' topic ids 1114 * @var array forum_ids Array with deleted posts' forum ids 1115 * @var string where_type Variable containing posts deletion mode 1116 * @var mixed where_ids Array or comma separated list of posts ids to delete 1117 * @var array delete_notifications_types Array with notifications types to delete 1118 * @since 3.1.0-a4 1119 */ 1120 $vars = array( 1121 'post_ids', 1122 'poster_ids', 1123 'topic_ids', 1124 'forum_ids', 1125 'where_type', 1126 'where_ids', 1127 'delete_notifications_types', 1128 ); 1129 extract($phpbb_dispatcher->trigger_event('core.delete_posts_after', compact($vars))); 1130 1131 // Resync topics_posted table 1132 if ($posted_sync) 1133 { 1134 update_posted_info($topic_ids); 1135 } 1136 1137 if ($auto_sync) 1138 { 1139 sync('topic_reported', 'topic_id', $topic_ids); 1140 sync('topic', 'topic_id', $topic_ids, true); 1141 sync('forum', 'forum_id', $forum_ids, true, true); 1142 } 1143 1144 if ($approved_posts && $post_count_sync) 1145 { 1146 $config->increment('num_posts', $approved_posts * (-1), false); 1147 } 1148 1149 // We actually remove topics now to not be inconsistent (the delete_topics function calls this function too) 1150 if (count($remove_topics) && $call_delete_topics) 1151 { 1152 delete_topics('topic_id', $remove_topics, $auto_sync, $post_count_sync, false); 1153 } 1154 1155 /* @var $phpbb_notifications \phpbb\notification\manager */ 1156 $phpbb_notifications = $phpbb_container->get('notification_manager'); 1157 1158 $phpbb_notifications->delete_notifications($delete_notifications_types, $post_ids); 1159 1160 return count($post_ids); 1161 } 1162 1163 /** 1164 * Delete Attachments 1165 * 1166 * @deprecated 3.2.0-a1 (To be removed: 3.4.0) 1167 * 1168 * @param string $mode can be: post|message|topic|attach|user 1169 * @param mixed $ids can be: post_ids, message_ids, topic_ids, attach_ids, user_ids 1170 * @param bool $resync set this to false if you are deleting posts or topics 1171 */ 1172 function delete_attachments($mode, $ids, $resync = true) 1173 { 1174 global $phpbb_container; 1175 1176 /** @var \phpbb\attachment\manager $attachment_manager */ 1177 $attachment_manager = $phpbb_container->get('attachment.manager'); 1178 $num_deleted = $attachment_manager->delete($mode, $ids, $resync); 1179 1180 unset($attachment_manager); 1181 1182 return $num_deleted; 1183 } 1184 1185 /** 1186 * Deletes shadow topics pointing to a specified forum. 1187 * 1188 * @param int $forum_id The forum id 1189 * @param string $sql_more Additional WHERE statement, e.g. t.topic_time < (time() - 1234) 1190 * @param bool $auto_sync Will call sync() if this is true 1191 * 1192 * @return array Array with affected forums 1193 */ 1194 function delete_topic_shadows($forum_id, $sql_more = '', $auto_sync = true) 1195 { 1196 global $db; 1197 1198 if (!$forum_id) 1199 { 1200 // Nothing to do. 1201 return; 1202 } 1203 1204 // Set of affected forums we have to resync 1205 $sync_forum_ids = array(); 1206 1207 // Amount of topics we select and delete at once. 1208 $batch_size = 500; 1209 1210 do 1211 { 1212 $sql = 'SELECT t2.forum_id, t2.topic_id 1213 FROM ' . TOPICS_TABLE . ' t2, ' . TOPICS_TABLE . ' t 1214 WHERE t2.topic_moved_id = t.topic_id 1215 AND t.forum_id = ' . (int) $forum_id . ' 1216 ' . (($sql_more) ? 'AND ' . $sql_more : ''); 1217 $result = $db->sql_query_limit($sql, $batch_size); 1218 1219 $topic_ids = array(); 1220 while ($row = $db->sql_fetchrow($result)) 1221 { 1222 $topic_ids[] = (int) $row['topic_id']; 1223 1224 $sync_forum_ids[(int) $row['forum_id']] = (int) $row['forum_id']; 1225 } 1226 $db->sql_freeresult($result); 1227 1228 if (!empty($topic_ids)) 1229 { 1230 $sql = 'DELETE FROM ' . TOPICS_TABLE . ' 1231 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 1232 $db->sql_query($sql); 1233 } 1234 } 1235 while (count($topic_ids) == $batch_size); 1236 1237 if ($auto_sync) 1238 { 1239 sync('forum', 'forum_id', $sync_forum_ids, true, true); 1240 } 1241 1242 return $sync_forum_ids; 1243 } 1244 1245 /** 1246 * Update/Sync posted information for topics 1247 */ 1248 function update_posted_info(&$topic_ids) 1249 { 1250 global $db, $config; 1251 1252 if (empty($topic_ids) || !$config['load_db_track']) 1253 { 1254 return; 1255 } 1256 1257 // First of all, let us remove any posted information for these topics 1258 $sql = 'DELETE FROM ' . TOPICS_POSTED_TABLE . ' 1259 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 1260 $db->sql_query($sql); 1261 1262 // Now, let us collect the user/topic combos for rebuilding the information 1263 $sql = 'SELECT poster_id, topic_id 1264 FROM ' . POSTS_TABLE . ' 1265 WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . ' 1266 AND poster_id <> ' . ANONYMOUS . ' 1267 GROUP BY poster_id, topic_id'; 1268 $result = $db->sql_query($sql); 1269 1270 $posted = array(); 1271 while ($row = $db->sql_fetchrow($result)) 1272 { 1273 // Add as key to make them unique (grouping by) and circumvent empty keys on array_unique 1274 $posted[$row['poster_id']][] = $row['topic_id']; 1275 } 1276 $db->sql_freeresult($result); 1277 1278 // Now add the information... 1279 $sql_ary = array(); 1280 foreach ($posted as $user_id => $topic_row) 1281 { 1282 foreach ($topic_row as $topic_id) 1283 { 1284 $sql_ary[] = array( 1285 'user_id' => (int) $user_id, 1286 'topic_id' => (int) $topic_id, 1287 'topic_posted' => 1, 1288 ); 1289 } 1290 } 1291 unset($posted); 1292 1293 $db->sql_multi_insert(TOPICS_POSTED_TABLE, $sql_ary); 1294 } 1295 1296 /** 1297 * Delete attached file 1298 * 1299 * @deprecated 3.2.0-a1 (To be removed: 3.4.0) 1300 */ 1301 function phpbb_unlink($filename, $mode = 'file', $entry_removed = false) 1302 { 1303 global $phpbb_container; 1304 1305 /** @var \phpbb\attachment\manager $attachment_manager */ 1306 $attachment_manager = $phpbb_container->get('attachment.manager'); 1307 $unlink = $attachment_manager->unlink($filename, $mode, $entry_removed); 1308 unset($attachment_manager); 1309 1310 return $unlink; 1311 } 1312 1313 /** 1314 * All-encompasing sync function 1315 * 1316 * Exaples: 1317 * <code> 1318 * sync('topic', 'topic_id', 123); // resync topic #123 1319 * sync('topic', 'forum_id', array(2, 3)); // resync topics from forum #2 and #3 1320 * sync('topic'); // resync all topics 1321 * sync('topic', 'range', 'topic_id BETWEEN 1 AND 60'); // resync a range of topics/forums (only available for 'topic' and 'forum' modes) 1322 * </code> 1323 * 1324 * Modes: 1325 * - forum Resync complete forum 1326 * - topic Resync topics 1327 * - topic_moved Removes topic shadows that would be in the same forum as the topic they link to 1328 * - topic_visibility Resyncs the topic_visibility flag according to the status of the first post 1329 * - post_reported Resyncs the post_reported flag, relying on actual reports 1330 * - topic_reported Resyncs the topic_reported flag, relying on post_reported flags 1331 * - post_attachement Same as post_reported, but with attachment flags 1332 * - topic_attachement Same as topic_reported, but with attachment flags 1333 */ 1334 function sync($mode, $where_type = '', $where_ids = '', $resync_parents = false, $sync_extra = false) 1335 { 1336 global $db; 1337 1338 if (is_array($where_ids)) 1339 { 1340 $where_ids = array_unique($where_ids); 1341 $where_ids = array_map('intval', $where_ids); 1342 } 1343 else if ($where_type != 'range') 1344 { 1345 $where_ids = ($where_ids) ? array((int) $where_ids) : array(); 1346 } 1347 1348 if ($mode == 'forum' || $mode == 'topic' || $mode == 'topic_visibility' || $mode == 'topic_reported' || $mode == 'post_reported') 1349 { 1350 if (!$where_type) 1351 { 1352 $where_sql = ''; 1353 $where_sql_and = 'WHERE'; 1354 } 1355 else if ($where_type == 'range') 1356 { 1357 // Only check a range of topics/forums. For instance: 'topic_id BETWEEN 1 AND 60' 1358 $where_sql = 'WHERE (' . $mode[0] . ".$where_ids)"; 1359 $where_sql_and = $where_sql . "\n\tAND"; 1360 } 1361 else 1362 { 1363 // Do not sync the "global forum" 1364 $where_ids = array_diff($where_ids, array(0)); 1365 1366 if (!count($where_ids)) 1367 { 1368 // Empty array with IDs. This means that we don't have any work to do. Just return. 1369 return; 1370 } 1371 1372 // Limit the topics/forums we are syncing, use specific topic/forum IDs. 1373 // $where_type contains the field for the where clause (forum_id, topic_id) 1374 $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids); 1375 $where_sql_and = $where_sql . "\n\tAND"; 1376 } 1377 } 1378 else 1379 { 1380 if (!count($where_ids)) 1381 { 1382 return; 1383 } 1384 1385 // $where_type contains the field for the where clause (forum_id, topic_id) 1386 $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids); 1387 $where_sql_and = $where_sql . "\n\tAND"; 1388 } 1389 1390 switch ($mode) 1391 { 1392 case 'topic_moved': 1393 $db->sql_transaction('begin'); 1394 switch ($db->get_sql_layer()) 1395 { 1396 case 'mysql4': 1397 case 'mysqli': 1398 $sql = 'DELETE FROM ' . TOPICS_TABLE . ' 1399 USING ' . TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2 1400 WHERE t1.topic_moved_id = t2.topic_id 1401 AND t1.forum_id = t2.forum_id"; 1402 $db->sql_query($sql); 1403 break; 1404 1405 default: 1406 $sql = 'SELECT t1.topic_id 1407 FROM ' .TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2 1408 WHERE t1.topic_moved_id = t2.topic_id 1409 AND t1.forum_id = t2.forum_id"; 1410 $result = $db->sql_query($sql); 1411 1412 $topic_id_ary = array(); 1413 while ($row = $db->sql_fetchrow($result)) 1414 { 1415 $topic_id_ary[] = $row['topic_id']; 1416 } 1417 $db->sql_freeresult($result); 1418 1419 if (!count($topic_id_ary)) 1420 { 1421 return; 1422 } 1423 1424 $sql = 'DELETE FROM ' . TOPICS_TABLE . ' 1425 WHERE ' . $db->sql_in_set('topic_id', $topic_id_ary); 1426 $db->sql_query($sql); 1427 1428 break; 1429 } 1430 1431 $db->sql_transaction('commit'); 1432 break; 1433 1434 case 'topic_visibility': 1435 1436 $db->sql_transaction('begin'); 1437 1438 $sql = 'SELECT t.topic_id, p.post_visibility 1439 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p 1440 $where_sql_and p.topic_id = t.topic_id 1441 AND p.post_visibility = " . ITEM_APPROVED; 1442 $result = $db->sql_query($sql); 1443 1444 $topics_approved = array(); 1445 while ($row = $db->sql_fetchrow($result)) 1446 { 1447 $topics_approved[] = (int) $row['topic_id']; 1448 } 1449 $db->sql_freeresult($result); 1450 1451 $sql = 'SELECT t.topic_id, p.post_visibility 1452 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p 1453 $where_sql_and " . $db->sql_in_set('t.topic_id', $topics_approved, true, true) . ' 1454 AND p.topic_id = t.topic_id 1455 AND p.post_visibility = ' . ITEM_DELETED; 1456 $result = $db->sql_query($sql); 1457 1458 $topics_softdeleted = array(); 1459 while ($row = $db->sql_fetchrow($result)) 1460 { 1461 $topics_softdeleted[] = (int) $row['topic_id']; 1462 } 1463 $db->sql_freeresult($result); 1464 1465 $topics_softdeleted = array_diff($topics_softdeleted, $topics_approved); 1466 $topics_not_unapproved = array_merge($topics_softdeleted, $topics_approved); 1467 1468 $update_ary = array( 1469 ITEM_UNAPPROVED => (!empty($topics_not_unapproved)) ? $where_sql_and . ' ' . $db->sql_in_set('topic_id', $topics_not_unapproved, true) : '', 1470 ITEM_APPROVED => (!empty($topics_approved)) ? ' WHERE ' . $db->sql_in_set('topic_id', $topics_approved) : '', 1471 ITEM_DELETED => (!empty($topics_softdeleted)) ? ' WHERE ' . $db->sql_in_set('topic_id', $topics_softdeleted) : '', 1472 ); 1473 1474 foreach ($update_ary as $visibility => $sql_where) 1475 { 1476 if ($sql_where) 1477 { 1478 $sql = 'UPDATE ' . TOPICS_TABLE . ' 1479 SET topic_visibility = ' . $visibility . ' 1480 ' . $sql_where; 1481 $db->sql_query($sql); 1482 } 1483 } 1484 1485 $db->sql_transaction('commit'); 1486 break; 1487 1488 case 'post_reported': 1489 $post_ids = $post_reported = array(); 1490 1491 $db->sql_transaction('begin'); 1492 1493 $sql = 'SELECT p.post_id, p.post_reported 1494 FROM ' . POSTS_TABLE . " p 1495 $where_sql 1496 GROUP BY p.post_id, p.post_reported"; 1497 $result = $db->sql_query($sql); 1498 1499 while ($row = $db->sql_fetchrow($result)) 1500 { 1501 $post_ids[$row['post_id']] = $row['post_id']; 1502 if ($row['post_reported']) 1503 { 1504 $post_reported[$row['post_id']] = 1; 1505 } 1506 } 1507 $db->sql_freeresult($result); 1508 1509 $sql = 'SELECT DISTINCT(post_id) 1510 FROM ' . REPORTS_TABLE . ' 1511 WHERE ' . $db->sql_in_set('post_id', $post_ids) . ' 1512 AND report_closed = 0'; 1513 $result = $db->sql_query($sql); 1514 1515 $post_ids = array(); 1516 while ($row = $db->sql_fetchrow($result)) 1517 { 1518 if (!isset($post_reported[$row['post_id']])) 1519 { 1520 $post_ids[] = $row['post_id']; 1521 } 1522 else 1523 { 1524 unset($post_reported[$row['post_id']]); 1525 } 1526 } 1527 $db->sql_freeresult($result); 1528 1529 // $post_reported should be empty by now, if it's not it contains 1530 // posts that are falsely flagged as reported 1531 foreach ($post_reported as $post_id => $void) 1532 { 1533 $post_ids[] = $post_id; 1534 } 1535 1536 if (count($post_ids)) 1537 { 1538 $sql = 'UPDATE ' . POSTS_TABLE . ' 1539 SET post_reported = 1 - post_reported 1540 WHERE ' . $db->sql_in_set('post_id', $post_ids); 1541 $db->sql_query($sql); 1542 } 1543 1544 $db->sql_transaction('commit'); 1545 break; 1546 1547 case 'topic_reported': 1548 if ($sync_extra) 1549 { 1550 sync('post_reported', $where_type, $where_ids); 1551 } 1552 1553 $topic_ids = $topic_reported = array(); 1554 1555 $db->sql_transaction('begin'); 1556 1557 $sql = 'SELECT DISTINCT(t.topic_id) 1558 FROM ' . POSTS_TABLE . " t 1559 $where_sql_and t.post_reported = 1"; 1560 $result = $db->sql_query($sql); 1561 1562 while ($row = $db->sql_fetchrow($result)) 1563 { 1564 $topic_reported[$row['topic_id']] = 1; 1565 } 1566 $db->sql_freeresult($result); 1567 1568 $sql = 'SELECT t.topic_id, t.topic_reported 1569 FROM ' . TOPICS_TABLE . " t 1570 $where_sql"; 1571 $result = $db->sql_query($sql); 1572 1573 while ($row = $db->sql_fetchrow($result)) 1574 { 1575 if ($row['topic_reported'] ^ isset($topic_reported[$row['topic_id']])) 1576 { 1577 $topic_ids[] = $row['topic_id']; 1578 } 1579 } 1580 $db->sql_freeresult($result); 1581 1582 if (count($topic_ids)) 1583 { 1584 $sql = 'UPDATE ' . TOPICS_TABLE . ' 1585 SET topic_reported = 1 - topic_reported 1586 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 1587 $db->sql_query($sql); 1588 } 1589 1590 $db->sql_transaction('commit'); 1591 break; 1592 1593 case 'post_attachment': 1594 $post_ids = $post_attachment = array(); 1595 1596 $db->sql_transaction('begin'); 1597 1598 $sql = 'SELECT p.post_id, p.post_attachment 1599 FROM ' . POSTS_TABLE . " p 1600 $where_sql 1601 GROUP BY p.post_id, p.post_attachment"; 1602 $result = $db->sql_query($sql); 1603 1604 while ($row = $db->sql_fetchrow($result)) 1605 { 1606 $post_ids[$row['post_id']] = $row['post_id']; 1607 if ($row['post_attachment']) 1608 { 1609 $post_attachment[$row['post_id']] = 1; 1610 } 1611 } 1612 $db->sql_freeresult($result); 1613 1614 $sql = 'SELECT DISTINCT(post_msg_id) 1615 FROM ' . ATTACHMENTS_TABLE . ' 1616 WHERE ' . $db->sql_in_set('post_msg_id', $post_ids) . ' 1617 AND in_message = 0'; 1618 $result = $db->sql_query($sql); 1619 1620 $post_ids = array(); 1621 while ($row = $db->sql_fetchrow($result)) 1622 { 1623 if (!isset($post_attachment[$row['post_msg_id']])) 1624 { 1625 $post_ids[] = $row['post_msg_id']; 1626 } 1627 else 1628 { 1629 unset($post_attachment[$row['post_msg_id']]); 1630 } 1631 } 1632 $db->sql_freeresult($result); 1633 1634 // $post_attachment should be empty by now, if it's not it contains 1635 // posts that are falsely flagged as having attachments 1636 foreach ($post_attachment as $post_id => $void) 1637 { 1638 $post_ids[] = $post_id; 1639 } 1640 1641 if (count($post_ids)) 1642 { 1643 $sql = 'UPDATE ' . POSTS_TABLE . ' 1644 SET post_attachment = 1 - post_attachment 1645 WHERE ' . $db->sql_in_set('post_id', $post_ids); 1646 $db->sql_query($sql); 1647 } 1648 1649 $db->sql_transaction('commit'); 1650 break; 1651 1652 case 'topic_attachment': 1653 if ($sync_extra) 1654 { 1655 sync('post_attachment', $where_type, $where_ids); 1656 } 1657 1658 $topic_ids = $topic_attachment = array(); 1659 1660 $db->sql_transaction('begin'); 1661 1662 $sql = 'SELECT DISTINCT(t.topic_id) 1663 FROM ' . POSTS_TABLE . " t 1664 $where_sql_and t.post_attachment = 1"; 1665 $result = $db->sql_query($sql); 1666 1667 while ($row = $db->sql_fetchrow($result)) 1668 { 1669 $topic_attachment[$row['topic_id']] = 1; 1670 } 1671 $db->sql_freeresult($result); 1672 1673 $sql = 'SELECT t.topic_id, t.topic_attachment 1674 FROM ' . TOPICS_TABLE . " t 1675 $where_sql"; 1676 $result = $db->sql_query($sql); 1677 1678 while ($row = $db->sql_fetchrow($result)) 1679 { 1680 if ($row['topic_attachment'] ^ isset($topic_attachment[$row['topic_id']])) 1681 { 1682 $topic_ids[] = $row['topic_id']; 1683 } 1684 } 1685 $db->sql_freeresult($result); 1686 1687 if (count($topic_ids)) 1688 { 1689 $sql = 'UPDATE ' . TOPICS_TABLE . ' 1690 SET topic_attachment = 1 - topic_attachment 1691 WHERE ' . $db->sql_in_set('topic_id', $topic_ids); 1692 $db->sql_query($sql); 1693 } 1694 1695 $db->sql_transaction('commit'); 1696 1697 break; 1698 1699 case 'forum': 1700 1701 $db->sql_transaction('begin'); 1702 1703 // 1: Get the list of all forums 1704 $sql = 'SELECT f.* 1705 FROM ' . FORUMS_TABLE . " f 1706 $where_sql"; 1707 $result = $db->sql_query($sql); 1708 1709 $forum_data = $forum_ids = $post_ids = $last_post_id = $post_info = array(); 1710 while ($row = $db->sql_fetchrow($result)) 1711 { 1712 if ($row['forum_type'] == FORUM_LINK) 1713 { 1714 continue; 1715 } 1716 1717 $forum_id = (int) $row['forum_id']; 1718 $forum_ids[$forum_id] = $forum_id; 1719 1720 $forum_data[$forum_id] = $row; 1721 if ($sync_extra) 1722 { 1723 $forum_data[$forum_id]['posts_approved'] = 0; 1724 $forum_data[$forum_id]['posts_unapproved'] = 0; 1725 $forum_data[$forum_id]['posts_softdeleted'] = 0; 1726 $forum_data[$forum_id]['topics_approved'] = 0; 1727 $forum_data[$forum_id]['topics_unapproved'] = 0; 1728 $forum_data[$forum_id]['topics_softdeleted'] = 0; 1729 } 1730 $forum_data[$forum_id]['last_post_id'] = 0; 1731 $forum_data[$forum_id]['last_post_subject'] = ''; 1732 $forum_data[$forum_id]['last_post_time'] = 0; 1733 $forum_data[$forum_id]['last_poster_id'] = 0; 1734 $forum_data[$forum_id]['last_poster_name'] = ''; 1735 $forum_data[$forum_id]['last_poster_colour'] = ''; 1736 } 1737 $db->sql_freeresult($result); 1738 1739 if (!count($forum_ids)) 1740 { 1741 break; 1742 } 1743 1744 $forum_ids = array_values($forum_ids); 1745 1746 // 2: Get topic counts for each forum (optional) 1747 if ($sync_extra) 1748 { 1749 $sql = 'SELECT forum_id, topic_visibility, COUNT(topic_id) AS total_topics 1750 FROM ' . TOPICS_TABLE . ' 1751 WHERE ' . $db->sql_in_set('forum_id', $forum_ids) . ' 1752 GROUP BY forum_id, topic_visibility'; 1753 $result = $db->sql_query($sql); 1754 1755 while ($row = $db->sql_fetchrow($result)) 1756 { 1757 $forum_id = (int) $row['forum_id']; 1758 1759 if ($row['topic_visibility'] == ITEM_APPROVED) 1760 { 1761 $forum_data[$forum_id]['topics_approved'] = $row['total_topics']; 1762 } 1763 else if ($row['topic_visibility'] == ITEM_UNAPPROVED || $row['topic_visibility'] == ITEM_REAPPROVE) 1764 { 1765 $forum_data[$forum_id]['topics_unapproved'] = $row['total_topics']; 1766 } 1767 else if ($row['topic_visibility'] == ITEM_DELETED) 1768 { 1769 $forum_data[$forum_id]['topics_softdeleted'] = $row['total_topics']; 1770 } 1771 } 1772 $db->sql_freeresult($result); 1773 } 1774 1775 // 3: Get post count for each forum (optional) 1776 if ($sync_extra) 1777 { 1778 if (count($forum_ids) == 1) 1779 { 1780 $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 1781 FROM ' . TOPICS_TABLE . ' t 1782 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . ' 1783 AND t.topic_status <> ' . ITEM_MOVED; 1784 } 1785 else 1786 { 1787 $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 1788 FROM ' . TOPICS_TABLE . ' t 1789 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . ' 1790 AND t.topic_status <> ' . ITEM_MOVED . ' 1791 GROUP BY t.forum_id'; 1792 } 1793 1794 $result = $db->sql_query($sql); 1795 1796 while ($row = $db->sql_fetchrow($result)) 1797 { 1798 $forum_id = (count($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id']; 1799 1800 $forum_data[$forum_id]['posts_approved'] = (int) $row['forum_posts_approved']; 1801 $forum_data[$forum_id]['posts_unapproved'] = (int) $row['forum_posts_unapproved']; 1802 $forum_data[$forum_id]['posts_softdeleted'] = (int) $row['forum_posts_softdeleted']; 1803 } 1804 $db->sql_freeresult($result); 1805 } 1806 1807 // 4: Get last_post_id for each forum 1808 if (count($forum_ids) == 1) 1809 { 1810 $sql = 'SELECT MAX(t.topic_last_post_id) as last_post_id 1811 FROM ' . TOPICS_TABLE . ' t 1812 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . ' 1813 AND t.topic_visibility = ' . ITEM_APPROVED; 1814 } 1815 else 1816 { 1817 $sql = 'SELECT t.forum_id, MAX(t.topic_last_post_id) as last_post_id 1818 FROM ' . TOPICS_TABLE . ' t 1819 WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . ' 1820 AND t.topic_visibility = ' . ITEM_APPROVED . ' 1821 GROUP BY t.forum_id'; 1822 } 1823 1824 $result = $db->sql_query($sql); 1825 1826 while ($row = $db->sql_fetchrow($result)) 1827 { 1828 $forum_id = (count($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id']; 1829 1830 $forum_data[$forum_id]['last_post_id'] = (int) $row['last_post_id']; 1831 1832 $post_ids[] = $row['last_post_id']; 1833 } 1834 $db->sql_freeresult($result); 1835 1836 // 5: Retrieve last_post infos 1837 if (count($post_ids)) 1838 { 1839 $sql = 'SELECT p.post_id, p.poster_id, p.post_subject, p.post_time, p.post_username, u.username, u.user_colour 1840 FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u 1841 WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . ' 1842 AND p.poster_id = u.user_id'; 1843 $result = $db->sql_query($sql); 1844 1845 while ($row = $db->sql_fetchrow($result)) 1846 { 1847 $post_info[$row['post_id']] = $row; 1848 } 1849 $db->sql_freeresult($result); 1850 1851 foreach ($forum_data as $forum_id => $data) 1852 { 1853 if ($data['last_post_id']) 1854 { 1855 if (isset($post_info[$data['last_post_id']])) 1856 { 1857 $forum_data[$forum_id]['last_post_subject'] = $post_info[$data['last_post_id']]['post_subject']; 1858 $forum_data[$forum_id]['last_post_time'] = $post_info[$data['last_post_id']]['post_time']; 1859 $forum_data[$forum_id]['last_poster_id'] = $post_info[$data['last_post_id']]['poster_id']; 1860 $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']; 1861 $forum_data[$forum_id]['last_poster_colour'] = $post_info[$data['last_post_id']]['user_colour']; 1862 } 1863 else 1864 { 1865 // For some reason we did not find the post in the db 1866 $forum_data[$forum_id]['last_post_id'] = 0; 1867 $forum_data[$forum_id]['last_post_subject'] = ''; 1868 $forum_data[$forum_id]['last_post_time'] = 0; 1869 $forum_data[$forum_id]['last_poster_id'] = 0; 1870 $forum_data[$forum_id]['last_poster_name'] = ''; 1871 $forum_data[$forum_id]['last_poster_colour'] = ''; 1872 } 1873 } 1874 } 1875 unset($post_info); 1876 } 1877 1878 // 6: Now do that thing 1879 $fieldnames = array('last_post_id', 'last_post_subject', 'last_post_time', 'last_poster_id', 'last_poster_name', 'last_poster_colour'); 1880 1881 if ($sync_extra) 1882 { 1883 array_push($fieldnames, 'posts_approved', 'posts_unapproved', 'posts_softdeleted', 'topics_approved', 'topics_unapproved', 'topics_softdeleted'); 1884 } 1885 1886 foreach ($forum_data as $forum_id => $row) 1887 { 1888 $sql_ary = array(); 1889 1890 foreach ($fieldnames as $fieldname) 1891 { 1892 if ($row['forum_' . $fieldname] != $row[$fieldname]) 1893 { 1894 if (preg_match('#(name|colour|subject)$#', $fieldname)) 1895 { 1896 $sql_ary['forum_' . $fieldname] = (string) $row[$fieldname]; 1897 } 1898 else 1899 { 1900 $sql_ary['forum_' . $fieldname] = (int) $row[$fieldname]; 1901 } 1902 } 1903 } 1904 1905 if (count($sql_ary)) 1906 { 1907 $sql = 'UPDATE ' . FORUMS_TABLE . ' 1908 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' 1909 WHERE forum_id = ' . $forum_id; 1910 $db->sql_query($sql); 1911 } 1912 } 1913 1914 $db->sql_transaction('commit'); 1915 break; 1916 1917 case 'topic': 1918 $topic_data = $post_ids = $resync_forums = $delete_topics = $delete_posts = $moved_topics = array(); 1919 1920 $db->sql_transaction('begin'); 1921 1922 $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 1923 FROM ' . TOPICS_TABLE . " t 1924 $where_sql"; 1925 $result = $db->sql_query($sql); 1926 1927 while ($row = $db->sql_fetchrow($result)) 1928 { 1929 if ($row['topic_moved_id']) 1930 { 1931 $moved_topics[] = $row['topic_id']; 1932 continue; 1933 } 1934 1935 $topic_id = (int) $row['topic_id']; 1936 $topic_data[$topic_id] = $row; 1937 $topic_data[$topic_id]['visibility'] = ITEM_UNAPPROVED; 1938 $topic_data[$topic_id]['posts_approved'] = 0; 1939 $topic_data[$topic_id]['posts_unapproved'] = 0; 1940 $topic_data[$topic_id]['posts_softdeleted'] = 0; 1941 $topic_data[$topic_id]['first_post_id'] = 0; 1942 $topic_data[$topic_id]['last_post_id'] = 0; 1943 unset($topic_data[$topic_id]['topic_id']); 1944 1945 // This array holds all topic_ids 1946 $delete_topics[$topic_id] = ''; 1947 1948 if ($sync_extra) 1949 { 1950 $topic_data[$topic_id]['reported'] = 0; 1951 $topic_data[$topic_id]['attachment'] = 0; 1952 } 1953 } 1954 $db->sql_freeresult($result); 1955 1956 // Use "t" as table alias because of the $where_sql clause 1957 // NOTE: 't.post_visibility' in the GROUP BY is causing a major slowdown. 1958 $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 1959 FROM ' . POSTS_TABLE . " t 1960 $where_sql 1961 GROUP BY t.topic_id, t.post_visibility"; 1962 $result = $db->sql_query($sql); 1963 1964 while ($row = $db->sql_fetchrow($result)) 1965 { 1966 $topic_id = (int) $row['topic_id']; 1967 1968 $row['first_post_id'] = (int) $row['first_post_id']; 1969 $row['last_post_id'] = (int) $row['last_post_id']; 1970 1971 if (!isset($topic_data[$topic_id])) 1972 { 1973 // Hey, these posts come from a topic that does not exist 1974 $delete_posts[$topic_id] = ''; 1975 } 1976 else 1977 { 1978 // Unset the corresponding entry in $delete_topics 1979 // When we'll be done, only topics with no posts will remain 1980 unset($delete_topics[$topic_id]); 1981 1982 if ($row['post_visibility'] == ITEM_APPROVED) 1983 { 1984 $topic_data[$topic_id]['posts_approved'] = $row['total_posts']; 1985 } 1986 else if ($row['post_visibility'] == ITEM_UNAPPROVED || $row['post_visibility'] == ITEM_REAPPROVE) 1987 { 1988 $topic_data[$topic_id]['posts_unapproved'] = $row['total_posts']; 1989 } 1990 else if ($row['post_visibility'] == ITEM_DELETED) 1991 { 1992 $topic_data[$topic_id]['posts_softdeleted'] = $row['total_posts']; 1993 } 1994 1995 if ($row['post_visibility'] == ITEM_APPROVED) 1996 { 1997 $topic_data[$topic_id]['visibility'] = ITEM_APPROVED; 1998 $topic_data[$topic_id]['first_post_id'] = $row['first_post_id']; 1999 $topic_data[$topic_id]['last_post_id'] = $row['last_post_id']; 2000 } 2001 else if ($topic_data[$topic_id]['visibility'] != ITEM_APPROVED) 2002 { 2003 // If there is no approved post, we take the min/max of the other visibilities 2004 // for the last and first post info, because it is only visible to moderators anyway 2005 $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']; 2006 $topic_data[$topic_id]['last_post_id'] = max($topic_data[$topic_id]['last_post_id'], $row['last_post_id']); 2007 2008 if ($topic_data[$topic_id]['visibility'] == ITEM_UNAPPROVED || $topic_data[$topic_id]['visibility'] == ITEM_REAPPROVE) 2009 { 2010 // Soft delete status is stronger than unapproved. 2011 $topic_data[$topic_id]['visibility'] = $row['post_visibility']; 2012 } 2013 } 2014 } 2015 } 2016 $db->sql_freeresult($result); 2017 2018 foreach ($topic_data as $topic_id => $row) 2019 { 2020 $post_ids[] = $row['first_post_id']; 2021 if ($row['first_post_id'] != $row['last_post_id']) 2022 { 2023 $post_ids[] = $row['last_post_id']; 2024 } 2025 } 2026 2027 // Now we delete empty topics and orphan posts 2028 if (count($delete_posts)) 2029 { 2030 delete_posts('topic_id', array_keys($delete_posts), false); 2031 unset($delete_posts); 2032 } 2033 2034 if (!count($topic_data)) 2035 { 2036 // If we get there, topic ids were invalid or topics did not contain any posts 2037 delete_topics($where_type, $where_ids, true); 2038 return; 2039 } 2040 2041 if (count($delete_topics)) 2042 { 2043 $delete_topic_ids = array(); 2044 foreach ($delete_topics as $topic_id => $void) 2045 { 2046 unset($topic_data[$topic_id]); 2047 $delete_topic_ids[] = $topic_id; 2048 } 2049 2050 delete_topics('topic_id', $delete_topic_ids, false); 2051 unset($delete_topics, $delete_topic_ids); 2052 } 2053 2054 $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 2055 FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u 2056 WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . ' 2057 AND u.user_id = p.poster_id'; 2058 $result = $db->sql_query($sql); 2059 2060 while ($row = $db->sql_fetchrow($result)) 2061 { 2062 $topic_id = intval($row['topic_id']); 2063 2064 if ($row['post_id'] == $topic_data[$topic_id]['first_post_id']) 2065 { 2066 $topic_data[$topic_id]['time'] = $row['post_time']; 2067 $topic_data[$topic_id]['poster'] = $row['poster_id']; 2068 $topic_data[$topic_id]['first_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username']; 2069 $topic_data[$topic_id]['first_poster_colour'] = $row['user_colour']; 2070 } 2071 2072 if ($row['post_id'] == $topic_data[$topic_id]['last_post_id']) 2073 { 2074 $topic_data[$topic_id]['last_poster_id'] = $row['poster_id']; 2075 $topic_data[$topic_id]['last_post_subject'] = $row['post_subject']; 2076 $topic_data[$topic_id]['last_post_time'] = $row['post_time']; 2077 $topic_data[$topic_id]['last_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username']; 2078 $topic_data[$topic_id]['last_poster_colour'] = $row['user_colour']; 2079 } 2080 } 2081 $db->sql_freeresult($result); 2082 2083 // Make sure shadow topics do link to existing topics 2084 if (count($moved_topics)) 2085 { 2086 $delete_topics = array(); 2087 2088 $sql = 'SELECT t1.topic_id, t1.topic_moved_id 2089 FROM ' . TOPICS_TABLE . ' t1 2090 LEFT JOIN ' . TOPICS_TABLE . ' t2 ON (t2.topic_id = t1.topic_moved_id) 2091 WHERE ' . $db->sql_in_set('t1.topic_id', $moved_topics) . ' 2092 AND t2.topic_id IS NULL'; 2093 $result = $db->sql_query($sql); 2094 2095 while ($row = $db->sql_fetchrow($result)) 2096 { 2097 $delete_topics[] = $row['topic_id']; 2098 } 2099 $db->sql_freeresult($result); 2100 2101 if (count($delete_topics)) 2102 { 2103 delete_topics('topic_id', $delete_topics, false); 2104 } 2105 unset($delete_topics); 2106 2107 // Make sure shadow topics having no last post data being updated (this only rarely happens...) 2108 $sql = 'SELECT topic_id, topic_moved_id, topic_last_post_id, topic_first_post_id 2109 FROM ' . TOPICS_TABLE . ' 2110 WHERE ' . $db->sql_in_set('topic_id', $moved_topics) . ' 2111 AND topic_last_post_time = 0'; 2112 $result = $db->sql_query($sql); 2113 2114 $shadow_topic_data = $post_ids = array(); 2115 while ($row = $db->sql_fetchrow($result)) 2116 { 2117 $shadow_topic_data[$row['topic_moved_id']] = $row; 2118 $post_ids[] = $row['topic_last_post_id']; 2119 $post_ids[] = $row['topic_first_post_id']; 2120 } 2121 $db->sql_freeresult($result); 2122 2123 $sync_shadow_topics = array(); 2124 if (count($post_ids)) 2125 { 2126 $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 2127 FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u 2128 WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . ' 2129 AND u.user_id = p.poster_id'; 2130 $result = $db->sql_query($sql); 2131 2132 while ($row = $db->sql_fetchrow($result)) 2133 { 2134 $topic_id = (int) $row['topic_id']; 2135 2136 // Ok, there should be a shadow topic. If there isn't, then there's something wrong with the db. 2137 // However, there's not much we can do about it. 2138 if (!empty($shadow_topic_data[$topic_id])) 2139 { 2140 if ($row['post_id'] == $shadow_topic_data[$topic_id]['topic_first_post_id']) 2141 { 2142 $orig_topic_id = $shadow_topic_data[$topic_id]['topic_id']; 2143 2144 if (!isset($sync_shadow_topics[$orig_topic_id])) 2145 { 2146 $sync_shadow_topics[$orig_topic_id] = array(); 2147 } 2148 2149 $sync_shadow_topics[$orig_topic_id]['topic_time'] = $row['post_time']; 2150 $sync_shadow_topics[$orig_topic_id]['topic_poster'] = $row['poster_id']; 2151 $sync_shadow_topics[$orig_topic_id]['topic_first_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username']; 2152 $sync_shadow_topics[$orig_topic_id]['topic_first_poster_colour'] = $row['user_colour']; 2153 } 2154 2155 if ($row['post_id'] == $shadow_topic_data[$topic_id]['topic_last_post_id']) 2156 { 2157 $orig_topic_id = $shadow_topic_data[$topic_id]['topic_id']; 2158 2159 if (!isset($sync_shadow_topics[$orig_topic_id])) 2160 { 2161 $sync_shadow_topics[$orig_topic_id] = array(); 2162 } 2163 2164 $sync_shadow_topics[$orig_topic_id]['topic_last_poster_id'] = $row['poster_id']; 2165 $sync_shadow_topics[$orig_topic_id]['topic_last_post_subject'] = $row['post_subject']; 2166 $sync_shadow_topics[$orig_topic_id]['topic_last_post_time'] = $row['post_time']; 2167 $sync_shadow_topics[$orig_topic_id]['topic_last_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username']; 2168 $sync_shadow_topics[$orig_topic_id]['topic_last_poster_colour'] = $row['user_colour']; 2169 } 2170 } 2171 } 2172 $db->sql_freeresult($result); 2173 2174 $shadow_topic_data = array(); 2175 2176 // Update the information we collected 2177 if (count($sync_shadow_topics)) 2178 { 2179 foreach ($sync_shadow_topics as $sync_topic_id => $sql_ary) 2180 { 2181 $sql = 'UPDATE ' . TOPICS_TABLE . ' 2182 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' 2183 WHERE topic_id = ' . $sync_topic_id; 2184 $db->sql_query($sql); 2185 } 2186 } 2187 } 2188 2189 unset($sync_shadow_topics, $shadow_topic_data); 2190 } 2191 2192 // These are fields that will be synchronised 2193 $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'); 2194 2195 if ($sync_extra) 2196 { 2197 // This routine assumes that post_reported values are correct 2198 // if they are not, use sync('post_reported') first 2199 $sql = 'SELECT t.topic_id, p.post_id 2200 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p 2201 $where_sql_and p.topic_id = t.topic_id 2202 AND p.post_reported = 1 2203 GROUP BY t.topic_id, p.post_id"; 2204 $result = $db->sql_query($sql); 2205 2206 $fieldnames[] = 'reported'; 2207 while ($row = $db->sql_fetchrow($result)) 2208 { 2209 $topic_data[intval($row['topic_id'])]['reported'] = 1; 2210 } 2211 $db->sql_freeresult($result); 2212 2213 // This routine assumes that post_attachment values are correct 2214 // if they are not, use sync('post_attachment') first 2215 $sql = 'SELECT t.topic_id, p.post_id 2216 FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p 2217 $where_sql_and p.topic_id = t.topic_id 2218 AND p.post_attachment = 1 2219 GROUP BY t.topic_id, p.post_id"; 2220 $result = $db->sql_query($sql); 2221 2222 $fieldnames[] = 'attachment'; 2223 while ($row = $db->sql_fetchrow($result)) 2224 { 2225 $topic_data[intval($row['topic_id'])]['attachment'] = 1; 2226 } 2227 $db->sql_freeresult($result); 2228 } 2229 2230 foreach ($topic_data as $topic_id => $row) 2231 { 2232 $sql_ary = array(); 2233 2234 foreach ($fieldnames as $fieldname) 2235 { 2236 if (isset($row[$fieldname]) && isset($row['topic_' . $fieldname]) && $row['topic_' . $fieldname] != $row[$fieldname]) 2237 { 2238 $sql_ary['topic_' . $fieldname] = $row[$fieldname]; 2239 } 2240 } 2241 2242 if (count($sql_ary)) 2243 { 2244 $sql = 'UPDATE ' . TOPICS_TABLE . ' 2245 SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' 2246 WHERE topic_id = ' . $topic_id; 2247 $db->sql_query($sql); 2248 2249 $resync_forums[$row['forum_id']] = $row['forum_id']; 2250 } 2251 } 2252 unset($topic_data); 2253 2254 $db->sql_transaction('commit'); 2255 2256 // if some topics have been resync'ed then resync parent forums 2257 // except when we're only syncing a range, we don't want to sync forums during 2258 // batch processing. 2259 if ($resync_parents && count($resync_forums) && $where_type != 'range') 2260 { 2261 sync('forum', 'forum_id', array_values($resync_forums), true, true); 2262 } 2263 break; 2264 } 2265 2266 return; 2267 } 2268 2269 /** 2270 * Prune function 2271 */ 2272 function prune($forum_id, $prune_mode, $prune_date, $prune_flags = 0, $auto_sync = true, $prune_limit = 0) 2273 { 2274 global $db, $phpbb_dispatcher; 2275 2276 if (!is_array($forum_id)) 2277 { 2278 $forum_id = array($forum_id); 2279 } 2280 2281 if (!count($forum_id)) 2282 { 2283 return; 2284 } 2285 2286 $sql_and = ''; 2287 2288 if (!($prune_flags & FORUM_FLAG_PRUNE_ANNOUNCE)) 2289 { 2290 $sql_and .= ' AND topic_type <> ' . POST_ANNOUNCE; 2291 $sql_and .= ' AND topic_type <> ' . POST_GLOBAL; 2292 } 2293 2294 if (!($prune_flags & FORUM_FLAG_PRUNE_STICKY)) 2295 { 2296 $sql_and .= ' AND topic_type <> ' . POST_STICKY; 2297 } 2298 2299 if ($prune_mode == 'posted') 2300 { 2301 $sql_and .= " AND topic_last_post_time < $prune_date"; 2302 } 2303 2304 if ($prune_mode == 'viewed') 2305 { 2306 $sql_and .= " AND topic_last_view_time < $prune_date"; 2307 } 2308 2309 if ($prune_mode == 'shadow') 2310 { 2311 $sql_and .= ' AND topic_status = ' . ITEM_MOVED . " AND topic_last_post_time < $prune_date"; 2312 } 2313 2314 /** 2315 * Use this event to modify the SQL that selects topics to be pruned 2316 * 2317 * @event core.prune_sql 2318 * @var string forum_id The forum id 2319 * @var string prune_mode The prune mode 2320 * @var string prune_date The prune date 2321 * @var int prune_flags The prune flags 2322 * @var bool auto_sync Whether or not to perform auto sync 2323 * @var string sql_and SQL text appended to where clause 2324 * @var int prune_limit The prune limit 2325 * @since 3.1.3-RC1 2326 * @changed 3.1.10-RC1 Added prune_limit 2327 */ 2328 $vars = array( 2329 'forum_id', 2330 'prune_mode', 2331 'prune_date', 2332 'prune_flags', 2333 'auto_sync', 2334 'sql_and', 2335 'prune_limit', 2336 ); 2337 extract($phpbb_dispatcher->trigger_event('core.prune_sql', compact($vars))); 2338 2339 $sql = 'SELECT topic_id 2340 FROM ' . TOPICS_TABLE . ' 2341 WHERE ' . $db->sql_in_set('forum_id', $forum_id) . " 2342 AND poll_start = 0 2343 $sql_and"; 2344 $result = $db->sql_query_limit($sql, $prune_limit); 2345 2346 $topic_list = array(); 2347 while ($row = $db->sql_fetchrow($result)) 2348 { 2349 $topic_list[] = $row['topic_id']; 2350 } 2351 $db->sql_freeresult($result); 2352 2353 if ($prune_flags & FORUM_FLAG_PRUNE_POLL) 2354 { 2355 $sql = 'SELECT topic_id 2356 FROM ' . TOPICS_TABLE . ' 2357 WHERE ' . $db->sql_in_set('forum_id', $forum_id) . " 2358 AND poll_start > 0 2359 AND poll_last_vote < $prune_date 2360 $sql_and"; 2361 $result = $db->sql_query_limit($sql, $prune_limit); 2362 2363 while ($row = $db->sql_fetchrow($result)) 2364 { 2365 $topic_list[] = $row['topic_id']; 2366 } 2367 $db->sql_freeresult($result); 2368 2369 $topic_list = array_unique($topic_list); 2370 } 2371 2372 /** 2373 * Perform additional actions before topic deletion via pruning 2374 * 2375 * @event core.prune_delete_before 2376 * @var int[] topic_list The IDs of the topics to be deleted 2377 * @since 3.2.2-RC1 2378 */ 2379 $vars = array('topic_list'); 2380 extract($phpbb_dispatcher->trigger_event('core.prune_delete_before', compact($vars))); 2381 2382 return delete_topics('topic_id', $topic_list, $auto_sync, false); 2383 } 2384 2385 /** 2386 * Function auto_prune(), this function now relies on passed vars 2387 */ 2388 function auto_prune($forum_id, $prune_mode, $prune_flags, $prune_days, $prune_freq) 2389 { 2390 global $db, $user, $phpbb_log; 2391 2392 $sql = 'SELECT forum_name 2393 FROM ' . FORUMS_TABLE . " 2394 WHERE forum_id = $forum_id"; 2395 $result = $db->sql_query($sql, 3600); 2396 $row = $db->sql_fetchrow($result); 2397 $db->sql_freeresult($result); 2398 2399 if ($row) 2400 { 2401 $prune_date = time() - ($prune_days * 86400); 2402 $next_prune = time() + ($prune_freq * 86400); 2403 2404 $result = prune($forum_id, $prune_mode, $prune_date, $prune_flags, true, 300); 2405 2406 if ($result['topics'] == 0 && $result['posts'] == 0) 2407 { 2408 $sql = 'UPDATE ' . FORUMS_TABLE . " 2409 SET prune_next = $next_prune 2410 WHERE forum_id = $forum_id"; 2411 $db->sql_query($sql); 2412 } 2413 2414 $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_AUTO_PRUNE', false, array($row['forum_name'])); 2415 } 2416 2417 return; 2418 } 2419 2420 /** 2421 * Cache moderators. Called whenever permissions are changed 2422 * via admin_permissions. Changes of usernames and group names 2423 * must be carried through for the moderators table. 2424 * 2425 * @param \phpbb\db\driver\driver_interface $db Database connection 2426 * @param \phpbb\cache\driver\driver_interface Cache driver 2427 * @param \phpbb\auth\auth $auth Authentication object 2428 * @return null 2429 */ 2430 function phpbb_cache_moderators($db, $cache, $auth) 2431 { 2432 // Remove cached sql results 2433 $cache->destroy('sql', MODERATOR_CACHE_TABLE); 2434 2435 // Clear table 2436 switch ($db->get_sql_layer()) 2437 { 2438 case 'sqlite3': 2439 $db->sql_query('DELETE FROM ' . MODERATOR_CACHE_TABLE); 2440 break; 2441 2442 default: 2443 $db->sql_query('TRUNCATE TABLE ' . MODERATOR_CACHE_TABLE); 2444 break; 2445 } 2446 2447 // We add moderators who have forum moderator permissions without an explicit ACL_NEVER setting 2448 $sql_ary = array(); 2449 2450 // Grab all users having moderative options... 2451 $hold_ary = $auth->acl_user_raw_data(false, 'm_%', false); 2452 2453 // Add users? 2454 if (!empty($hold_ary)) 2455 { 2456 // At least one moderative option warrants a display 2457 $ug_id_ary = array_keys($hold_ary); 2458 2459 // Remove users who have group memberships with DENY moderator permissions 2460 $sql_ary_deny = array( 2461 'SELECT' => 'a.forum_id, ug.user_id, g.group_id', 2462 2463 'FROM' => array( 2464 ACL_OPTIONS_TABLE => 'o', 2465 USER_GROUP_TABLE => 'ug', 2466 GROUPS_TABLE => 'g', 2467 ACL_GROUPS_TABLE => 'a', 2468 ), 2469 2470 'LEFT_JOIN' => array( 2471 array( 2472 'FROM' => array(ACL_ROLES_DATA_TABLE => 'r'), 2473 'ON' => 'a.auth_role_id = r.role_id', 2474 ), 2475 ), 2476 2477 'WHERE' => '(o.auth_option_id = a.auth_option_id OR o.auth_option_id = r.auth_option_id) 2478 AND ((a.auth_setting = ' . ACL_NEVER . ' AND r.auth_setting IS NULL) 2479 OR r.auth_setting = ' . ACL_NEVER . ') 2480 AND a.group_id = ug.group_id 2481 AND g.group_id = ug.group_id 2482 AND NOT (ug.group_leader = 1 AND g.group_skip_auth = 1) 2483 AND ' . $db->sql_in_set('ug.user_id', $ug_id_ary) . " 2484 AND ug.user_pending = 0 2485 AND o.auth_option " . $db->sql_like_expression('m_' . $db->get_any_char()), 2486 ); 2487 $sql = $db->sql_build_query('SELECT', $sql_ary_deny); 2488 $result = $db->sql_query($sql); 2489 2490 while ($row = $db->sql_fetchrow($result)) 2491 { 2492 if (isset($hold_ary[$row['user_id']][$row['forum_id']])) 2493 { 2494 unset($hold_ary[$row['user_id']][$row['forum_id']]); 2495 } 2496 } 2497 $db->sql_freeresult($result); 2498 2499 if (count($hold_ary)) 2500 { 2501 // Get usernames... 2502 $sql = 'SELECT user_id, username 2503 FROM ' . USERS_TABLE . ' 2504 WHERE ' . $db->sql_in_set('user_id', array_keys($hold_ary)); 2505 $result = $db->sql_query($sql); 2506 2507 $usernames_ary = array(); 2508 while ($row = $db->sql_fetchrow($result)) 2509 { 2510 $usernames_ary[$row['user_id']] = $row['username']; 2511 } 2512 $db->sql_freeresult($result); 2513 2514 foreach ($hold_ary as $user_id => $forum_id_ary) 2515 { 2516 // Do not continue if user does not exist 2517 if (!isset($usernames_ary[$user_id])) 2518 { 2519 continue; 2520 } 2521 2522 foreach ($forum_id_ary as $forum_id => $auth_ary) 2523 { 2524 $sql_ary[] = array( 2525 'forum_id' => (int) $forum_id, 2526 'user_id' => (int) $user_id, 2527 'username' => (string) $usernames_ary[$user_id], 2528 'group_id' => 0, 2529 'group_name' => '' 2530 ); 2531 } 2532 } 2533 } 2534 } 2535 2536 // Now to the groups... 2537 $hold_ary = $auth->acl_group_raw_data(false, 'm_%', false); 2538 2539 if (!empty($hold_ary)) 2540 { 2541 $ug_id_ary = array_keys($hold_ary); 2542 2543 // Make sure not hidden or special groups are involved... 2544 $sql = 'SELECT group_name, group_id, group_type 2545 FROM ' . GROUPS_TABLE . ' 2546 WHERE ' . $db->sql_in_set('group_id', $ug_id_ary); 2547 $result = $db->sql_query($sql); 2548 2549 $groupnames_ary = array(); 2550 while ($row = $db->sql_fetchrow($result)) 2551 { 2552 if ($row['group_type'] == GROUP_HIDDEN || $row['group_type'] == GROUP_SPECIAL) 2553 { 2554 unset($hold_ary[$row['group_id']]); 2555 } 2556 2557 $groupnames_ary[$row['group_id']] = $row['group_name']; 2558 } 2559 $db->sql_freeresult($result); 2560 2561 foreach ($hold_ary as $group_id => $forum_id_ary) 2562 { 2563 // If there is no group, we do not assign it... 2564 if (!isset($groupnames_ary[$group_id])) 2565 { 2566 continue; 2567 } 2568 2569 foreach ($forum_id_ary as $forum_id => $auth_ary) 2570 { 2571 $flag = false; 2572 foreach ($auth_ary as $auth_option => $setting) 2573 { 2574 // Make sure at least one ACL_YES option is set... 2575 if ($setting == ACL_YES) 2576 { 2577 $flag = true; 2578 break; 2579 } 2580 } 2581 2582 if (!$flag) 2583 { 2584 continue; 2585 } 2586 2587 $sql_ary[] = array( 2588 'forum_id' => (int) $forum_id, 2589 'user_id' => 0, 2590 'username' => '', 2591 'group_id' => (int) $group_id, 2592 'group_name' => (string) $groupnames_ary[$group_id] 2593 ); 2594 } 2595 } 2596 } 2597 2598 $db->sql_multi_insert(MODERATOR_CACHE_TABLE, $sql_ary); 2599 } 2600 2601 /** 2602 * View log 2603 * 2604 * @param string $mode The mode defines which log_type is used and from which log the entry is retrieved 2605 * @param array &$log The result array with the logs 2606 * @param mixed &$log_count If $log_count is set to false, we will skip counting all entries in the database. 2607 * Otherwise an integer with the number of total matching entries is returned. 2608 * @param int $limit Limit the number of entries that are returned 2609 * @param int $offset Offset when fetching the log entries, f.e. when paginating 2610 * @param mixed $forum_id Restrict the log entries to the given forum_id (can also be an array of forum_ids) 2611 * @param int $topic_id Restrict the log entries to the given topic_id 2612 * @param int $user_id Restrict the log entries to the given user_id 2613 * @param int $log_time Only get log entries newer than the given timestamp 2614 * @param string $sort_by SQL order option, e.g. 'l.log_time DESC' 2615 * @param string $keywords Will only return log entries that have the keywords in log_operation or log_data 2616 * 2617 * @return int Returns the offset of the last valid page, if the specified offset was invalid (too high) 2618 */ 2619 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 = '') 2620 { 2621 global $phpbb_log; 2622 2623 $count_logs = ($log_count !== false); 2624 2625 $log = $phpbb_log->get_logs($mode, $count_logs, $limit, $offset, $forum_id, $topic_id, $user_id, $limit_days, $sort_by, $keywords); 2626 $log_count = $phpbb_log->get_log_count(); 2627 2628 return $phpbb_log->get_valid_offset(); 2629 } 2630 2631 /** 2632 * Removes moderators and administrators from foe lists. 2633 * 2634 * @param \phpbb\db\driver\driver_interface $db Database connection 2635 * @param \phpbb\auth\auth $auth Authentication object 2636 * @param array|bool $group_id If an array, remove all members of this group from foe lists, or false to ignore 2637 * @param array|bool $user_id If an array, remove this user from foe lists, or false to ignore 2638 * @return null 2639 */ 2640 function phpbb_update_foes($db, $auth, $group_id = false, $user_id = false) 2641 { 2642 // update foes for some user 2643 if (is_array($user_id) && count($user_id)) 2644 { 2645 $sql = 'DELETE FROM ' . ZEBRA_TABLE . ' 2646 WHERE ' . $db->sql_in_set('zebra_id', $user_id) . ' 2647 AND foe = 1'; 2648 $db->sql_query($sql); 2649 return; 2650 } 2651 2652 // update foes for some group 2653 if (is_array($group_id) && count($group_id)) 2654 { 2655 // Grab group settings... 2656 $sql_ary = array( 2657 'SELECT' => 'a.group_id', 2658 2659 'FROM' => array( 2660 ACL_OPTIONS_TABLE => 'ao', 2661 ACL_GROUPS_TABLE => 'a', 2662 ), 2663 2664 'LEFT_JOIN' => array( 2665 array( 2666 'FROM' => array(ACL_ROLES_DATA_TABLE => 'r'), 2667 'ON' => 'a.auth_role_id = r.role_id', 2668 ), 2669 ), 2670 2671 'WHERE' => '(ao.auth_option_id = a.auth_option_id OR ao.auth_option_id = r.auth_option_id) 2672 AND ' . $db->sql_in_set('a.group_id', $group_id) . " 2673 AND ao.auth_option IN ('a_', 'm_')", 2674 2675 'GROUP_BY' => 'a.group_id', 2676 ); 2677 $sql = $db->sql_build_query('SELECT', $sql_ary); 2678 $result = $db->sql_query($sql); 2679 2680 $groups = array(); 2681 while ($row = $db->sql_fetchrow($result)) 2682 { 2683 $groups[] = (int) $row['group_id']; 2684 } 2685 $db->sql_freeresult($result); 2686 2687 if (!count($groups)) 2688 { 2689 return; 2690 } 2691 2692 switch ($db->get_sql_layer()) 2693 { 2694 case 'mysqli': 2695 case 'mysql4': 2696 $sql = 'DELETE ' . (($db->get_sql_layer() === 'mysqli' || version_compare($db->sql_server_info(true), '4.1', '>=')) ? 'z.*' : ZEBRA_TABLE) . ' 2697 FROM ' . ZEBRA_TABLE . ' z, ' . USER_GROUP_TABLE . ' ug 2698 WHERE z.zebra_id = ug.user_id 2699 AND z.foe = 1 2700 AND ' . $db->sql_in_set('ug.group_id', $groups); 2701 $db->sql_query($sql); 2702 break; 2703 2704 default: 2705 $sql = 'SELECT user_id 2706 FROM ' . USER_GROUP_TABLE . ' 2707 WHERE ' . $db->sql_in_set('group_id', $groups); 2708 $result = $db->sql_query($sql); 2709 2710 $users = array(); 2711 while ($row = $db->sql_fetchrow($result)) 2712 { 2713 $users[] = (int) $row['user_id']; 2714 } 2715 $db->sql_freeresult($result); 2716 2717 if (count($users)) 2718 { 2719 $sql = 'DELETE FROM ' . ZEBRA_TABLE . ' 2720 WHERE ' . $db->sql_in_set('zebra_id', $users) . ' 2721 AND foe = 1'; 2722 $db->sql_query($sql); 2723 } 2724 break; 2725 } 2726 2727 return; 2728 } 2729 2730 // update foes for everyone 2731 $perms = array(); 2732 foreach ($auth->acl_get_list(false, array('a_', 'm_'), false) as $forum_id => $forum_ary) 2733 { 2734 foreach ($forum_ary as $auth_option => $user_ary) 2735 { 2736 $perms = array_merge($perms, $user_ary); 2737 } 2738 } 2739 2740 if (count($perms)) 2741 { 2742 $sql = 'DELETE FROM ' . ZEBRA_TABLE . ' 2743 WHERE ' . $db->sql_in_set('zebra_id', array_unique($perms)) . ' 2744 AND foe = 1'; 2745 $db->sql_query($sql); 2746 } 2747 unset($perms); 2748 } 2749 2750 /** 2751 * Lists inactive users 2752 */ 2753 function view_inactive_users(&$users, &$user_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'user_inactive_time DESC') 2754 { 2755 global $db, $user; 2756 2757 $sql = 'SELECT COUNT(user_id) AS user_count 2758 FROM ' . USERS_TABLE . ' 2759 WHERE user_type = ' . USER_INACTIVE . 2760 (($limit_days) ? " AND user_inactive_time >= $limit_days" : ''); 2761 $result = $db->sql_query($sql); 2762 $user_count = (int) $db->sql_fetchfield('user_count'); 2763 $db->sql_freeresult($result); 2764 2765 if ($user_count == 0) 2766 { 2767 // Save the queries, because there are no users to display 2768 return 0; 2769 } 2770 2771 if ($offset >= $user_count) 2772 { 2773 $offset = ($offset - $limit < 0) ? 0 : $offset - $limit; 2774 } 2775 2776 $sql = 'SELECT * 2777 FROM ' . USERS_TABLE . ' 2778 WHERE user_type = ' . USER_INACTIVE . 2779 (($limit_days) ? " AND user_inactive_time >= $limit_days" : '') . " 2780 ORDER BY $sort_by"; 2781 $result = $db->sql_query_limit($sql, $limit, $offset); 2782 2783 while ($row = $db->sql_fetchrow($result)) 2784 { 2785 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_UNKNOWN']; 2786 switch ($row['user_inactive_reason']) 2787 { 2788 case INACTIVE_REGISTER: 2789 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_REGISTER']; 2790 break; 2791 2792 case INACTIVE_PROFILE: 2793 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_PROFILE']; 2794 break; 2795 2796 case INACTIVE_MANUAL: 2797 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_MANUAL']; 2798 break; 2799 2800 case INACTIVE_REMIND: 2801 $row['inactive_reason'] = $user->lang['INACTIVE_REASON_REMIND']; 2802 break; 2803 } 2804 2805 $users[] = $row; 2806 } 2807 $db->sql_freeresult($result); 2808 2809 return $offset; 2810 } 2811 2812 /** 2813 * Lists warned users 2814 */ 2815 function view_warned_users(&$users, &$user_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'user_warnings DESC') 2816 { 2817 global $db; 2818 2819 $sql = 'SELECT user_id, username, user_colour, user_warnings, user_last_warning 2820 FROM ' . USERS_TABLE . ' 2821 WHERE user_warnings > 0 2822 ' . (($limit_days) ? "AND user_last_warning >= $limit_days" : '') . " 2823 ORDER BY $sort_by"; 2824 $result = $db->sql_query_limit($sql, $limit, $offset); 2825 $users = $db->sql_fetchrowset($result); 2826 $db->sql_freeresult($result); 2827 2828 $sql = 'SELECT count(user_id) AS user_count 2829 FROM ' . USERS_TABLE . ' 2830 WHERE user_warnings > 0 2831 ' . (($limit_days) ? "AND user_last_warning >= $limit_days" : ''); 2832 $result = $db->sql_query($sql); 2833 $user_count = (int) $db->sql_fetchfield('user_count'); 2834 $db->sql_freeresult($result); 2835 2836 return; 2837 } 2838 2839 /** 2840 * Get database size 2841 * Currently only mysql and mssql are supported 2842 */ 2843 function get_database_size() 2844 { 2845 global $db, $user, $table_prefix; 2846 2847 $database_size = false; 2848 2849 // This code is heavily influenced by a similar routine in phpMyAdmin 2.2.0 2850 switch ($db->get_sql_layer()) 2851 { 2852 case 'mysql': 2853 case 'mysql4': 2854 case 'mysqli': 2855 $sql = 'SELECT VERSION() AS mysql_version'; 2856 $result = $db->sql_query($sql); 2857 $row = $db->sql_fetchrow($result); 2858 $db->sql_freeresult($result); 2859 2860 if ($row) 2861 { 2862 $version = $row['mysql_version']; 2863 2864 if (preg_match('#(3\.23|[45]\.|10\.[0-9]\.[0-9]{1,2}-+Maria)#', $version)) 2865 { 2866 $db_name = (preg_match('#^(?:3\.23\.(?:[6-9]|[1-9]{2}))|[45]\.|10\.[0-9]\.[0-9]{1,2}-+Maria#', $version)) ? "`{$db->get_db_name()}`" : $db->get_db_name(); 2867 2868 $sql = 'SHOW TABLE STATUS 2869 FROM ' . $db_name; 2870 $result = $db->sql_query($sql, 7200); 2871 2872 $database_size = 0; 2873 while ($row = $db->sql_fetchrow($result)) 2874 { 2875 if ((isset($row['Type']) && $row['Type'] != 'MRG_MyISAM') || (isset($row['Engine']) && ($row['Engine'] == 'MyISAM' || $row['Engine'] == 'InnoDB' || $row['Engine'] == 'Aria'))) 2876 { 2877 if ($table_prefix != '') 2878 { 2879 if (strpos($row['Name'], $table_prefix) !== false) 2880 { 2881 $database_size += $row['Data_length'] + $row['Index_length']; 2882 } 2883 } 2884 else 2885 { 2886 $database_size += $row['Data_length'] + $row['Index_length']; 2887 } 2888 } 2889 } 2890 $db->sql_freeresult($result); 2891 } 2892 } 2893 break; 2894 2895 case 'sqlite3': 2896 global $dbhost; 2897 2898 if (file_exists($dbhost)) 2899 { 2900 $database_size = filesize($dbhost); 2901 } 2902 2903 break; 2904 2905 case 'mssql_odbc': 2906 case 'mssqlnative': 2907 $sql = 'SELECT @@VERSION AS mssql_version'; 2908 $result = $db->sql_query($sql); 2909 $row = $db->sql_fetchrow($result); 2910 $db->sql_freeresult($result); 2911 2912 $sql = 'SELECT ((SUM(size) * 8.0) * 1024.0) as dbsize 2913 FROM sysfiles'; 2914 2915 if ($row) 2916 { 2917 // Azure stats are stored elsewhere 2918 if (strpos($row['mssql_version'], 'SQL Azure') !== false) 2919 { 2920 $sql = 'SELECT ((SUM(reserved_page_count) * 8.0) * 1024.0) as dbsize 2921 FROM sys.dm_db_partition_stats'; 2922 } 2923 } 2924 2925 $result = $db->sql_query($sql, 7200); 2926 $database_size = ($row = $db->sql_fetchrow($result)) ? $row['dbsize'] : false; 2927 $db->sql_freeresult($result); 2928 break; 2929 2930 case 'postgres': 2931 $sql = "SELECT proname 2932 FROM pg_proc 2933 WHERE proname = 'pg_database_size'"; 2934 $result = $db->sql_query($sql); 2935 $row = $db->sql_fetchrow($result); 2936 $db->sql_freeresult($result); 2937 2938 if ($row['proname'] == 'pg_database_size') 2939 { 2940 $database = $db->get_db_name(); 2941 if (strpos($database, '.') !== false) 2942 { 2943 list($database, ) = explode('.', $database); 2944 } 2945 2946 $sql = "SELECT oid 2947 FROM pg_database 2948 WHERE datname = '$database'"; 2949 $result = $db->sql_query($sql); 2950 $row = $db->sql_fetchrow($result); 2951 $db->sql_freeresult($result); 2952 2953 $oid = $row['oid']; 2954 2955 $sql = 'SELECT pg_database_size(' . $oid . ') as size'; 2956 $result = $db->sql_query($sql); 2957 $row = $db->sql_fetchrow($result); 2958 $db->sql_freeresult($result); 2959 2960 $database_size = $row['size']; 2961 } 2962 break; 2963 2964 case 'oracle': 2965 $sql = 'SELECT SUM(bytes) as dbsize 2966 FROM user_segments'; 2967 $result = $db->sql_query($sql, 7200); 2968 $database_size = ($row = $db->sql_fetchrow($result)) ? $row['dbsize'] : false; 2969 $db->sql_freeresult($result); 2970 break; 2971 } 2972 2973 $database_size = ($database_size !== false) ? get_formatted_filesize($database_size) : $user->lang['NOT_AVAILABLE']; 2974 2975 return $database_size; 2976 } 2977 2978 /* 2979 * Tidy Warnings 2980 * Remove all warnings which have now expired from the database 2981 * The duration of a warning can be defined by the administrator 2982 * This only removes the warning and reduces the associated count, 2983 * it does not remove the user note recording the contents of the warning 2984 */ 2985 function tidy_warnings() 2986 { 2987 global $db, $config; 2988 2989 $expire_date = time() - ($config['warnings_expire_days'] * 86400); 2990 $warning_list = $user_list = array(); 2991 2992 $sql = 'SELECT * FROM ' . WARNINGS_TABLE . " 2993 WHERE warning_time < $expire_date"; 2994 $result = $db->sql_query($sql); 2995 2996 while ($row = $db->sql_fetchrow($result)) 2997 { 2998 $warning_list[] = $row['warning_id']; 2999 $user_list[$row['user_id']] = isset($user_list[$row['user_id']]) ? ++$user_list[$row['user_id']] : 1; 3000 } 3001 $db->sql_freeresult($result); 3002 3003 if (count($warning_list)) 3004 { 3005 $db->sql_transaction('begin'); 3006 3007 $sql = 'DELETE FROM ' . WARNINGS_TABLE . ' 3008 WHERE ' . $db->sql_in_set('warning_id', $warning_list); 3009 $db->sql_query($sql); 3010 3011 foreach ($user_list as $user_id => $value) 3012 { 3013 $sql = 'UPDATE ' . USERS_TABLE . " SET user_warnings = user_warnings - $value 3014 WHERE user_id = $user_id"; 3015 $db->sql_query($sql); 3016 } 3017 3018 $db->sql_transaction('commit'); 3019 } 3020 3021 $config->set('warnings_last_gc', time(), false); 3022 } 3023 3024 /** 3025 * Tidy database, doing some maintanance tasks 3026 */ 3027 function tidy_database() 3028 { 3029 global $config, $db; 3030 3031 // Here we check permission consistency 3032 3033 // Sometimes, it can happen permission tables having forums listed which do not exist 3034 $sql = 'SELECT forum_id 3035 FROM ' . FORUMS_TABLE; 3036 $result = $db->sql_query($sql); 3037 3038 $forum_ids = array(0); 3039 while ($row = $db->sql_fetchrow($result)) 3040 { 3041 $forum_ids[] = $row['forum_id']; 3042 } 3043 $db->sql_freeresult($result); 3044 3045 // Delete those rows from the acl tables not having listed the forums above 3046 $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . ' 3047 WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true); 3048 $db->sql_query($sql); 3049 3050 $sql = 'DELETE FROM ' . ACL_USERS_TABLE . ' 3051 WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true); 3052 $db->sql_query($sql); 3053 3054 $config->set('database_last_gc', time(), false); 3055 } 3056 3057 /** 3058 * Add permission language - this will make sure custom files will be included 3059 */ 3060 function add_permission_language() 3061 { 3062 global $user, $phpEx, $phpbb_extension_manager; 3063 3064 // add permission language files from extensions 3065 $finder = $phpbb_extension_manager->get_finder(); 3066 3067 $lang_files = $finder 3068 ->prefix('permissions_') 3069 ->suffix(".$phpEx") 3070 ->core_path('language/') 3071 ->extension_directory('/language') 3072 ->find(); 3073 3074 foreach ($lang_files as $lang_file => $ext_name) 3075 { 3076 if ($ext_name === '/') 3077 { 3078 $user->add_lang($lang_file); 3079 } 3080 else 3081 { 3082 $user->add_lang_ext($ext_name, $lang_file); 3083 } 3084 } 3085 } 3086 3087 /** 3088 * Enables a particular flag in a bitfield column of a given table. 3089 * 3090 * @param string $table_name The table to update 3091 * @param string $column_name The column containing a bitfield to update 3092 * @param int $flag The binary flag which is OR-ed with the current column value 3093 * @param string $sql_more This string is attached to the sql query generated to update the table. 3094 * 3095 * @return null 3096 */ 3097 function enable_bitfield_column_flag($table_name, $column_name, $flag, $sql_more = '') 3098 { 3099 global $db; 3100 3101 $sql = 'UPDATE ' . $table_name . ' 3102 SET ' . $column_name . ' = ' . $db->sql_bit_or($column_name, $flag) . ' 3103 ' . $sql_more; 3104 $db->sql_query($sql); 3105 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jan 11 23:14:31 2018 | Cross-referenced by PHPXref 0.7.1 |