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