/forum/Sources/ManageMaintenance.php
PHP | 1858 lines | 1333 code | 239 blank | 286 comment | 168 complexity | 74a2f2e8265b5e0587d59296f59d2903 MD5 | raw file
1<?php 2/********************************************************************************** 3* ManageMaintenance.php * 4*********************************************************************************** 5* SMF: Simple Machines Forum * 6* Open-Source Project Inspired by Zef Hemel (zef@zefhemel.com) * 7* =============================================================================== * 8* Software Version: SMF 2.0 RC2 * 9* Software by: Simple Machines (http://www.simplemachines.org) * 10* Copyright 2006-2009 by: Simple Machines LLC (http://www.simplemachines.org) * 11* 2001-2006 by: Lewis Media (http://www.lewismedia.com) * 12* Support, News, Updates at: http://www.simplemachines.org * 13*********************************************************************************** 14* This program is free software; you may redistribute it and/or modify it under * 15* the terms of the provided license as published by Simple Machines LLC. * 16* * 17* This program is distributed in the hope that it is and will be useful, but * 18* WITHOUT ANY WARRANTIES; without even any implied warranty of MERCHANTABILITY * 19* or FITNESS FOR A PARTICULAR PURPOSE. * 20* * 21* See the "license.txt" file for details of the Simple Machines license. * 22* The latest version can always be found at http://www.simplemachines.org. * 23**********************************************************************************/ 24 25if (!defined('SMF')) 26 die('Hacking attempt...'); 27 28/* /!!! 29 30 void ManageMaintenance() 31 // !!! 32 33 void MaintainDatabase() 34 // !!! 35 36 void MaintainMembers() 37 // !!! 38 39 void MaintainTopics() 40 // !!! 41 42 void MaintainCleanCache() 43 // !!! 44 45 void MaintainFindFixErrors() 46 // !!! 47 48 void MaintainEmptyUnimportantLogs() 49 // !!! 50 51 void ConvertUtf8() 52 - converts the data and database tables to UTF-8 character set. 53 - requires the admin_forum permission. 54 - uses the convert_utf8 sub template of the Admin template. 55 - only works if UTF-8 is not the global character set. 56 - supports all character sets used by SMF's language files. 57 - redirects to ?action=admin;area=maintain after finishing. 58 - is linked from the maintenance screen (if applicable). 59 - accessed by ?action=admin;area=maintain;sa=database;activity=convertutf8. 60 61 void ConvertEntities() 62 - converts HTML-entities to UTF-8 characters. 63 - requires the admin_forum permission. 64 - uses the convert_entities sub template of the Admin template. 65 - only works if UTF-8 has been set as database and global character set. 66 - is divided in steps of 10 seconds. 67 - is linked from the maintenance screen (if applicable). 68 - accessed by ?action=admin;area=maintain;sa=database;activity=convertentities. 69 70 void OptimizeTables() 71 - optimizes all tables in the database and lists how much was saved. 72 - requires the admin_forum permission. 73 - uses the rawdata sub template (built in.) 74 - shows as the maintain_forum admin area. 75 - updates the optimize scheduled task such that the tables are not 76 automatically optimized again too soon. 77 - accessed from ?action=admin;area=maintain;sa=database;activity=optimize. 78 79 void AdminBoardRecount() 80 - recounts many forum totals that can be recounted automatically 81 without harm. 82 - requires the admin_forum permission. 83 - shows the maintain_forum admin area. 84 - fixes topics with wrong num_replies. 85 - updates the num_posts and num_topics of all boards. 86 - recounts instant_messages but not unread_messages. 87 - repairs messages pointing to boards with topics pointing to 88 other boards. 89 - updates the last message posted in boards and children. 90 - updates member count, latest member, topic count, and message count. 91 - redirects back to ?action=admin;area=maintain when complete. 92 - accessed via ?action=admin;area=maintain;sa=database;activity=recount. 93 94 void VersionDetail() 95 - parses the comment headers in all files for their version information 96 and outputs that for some javascript to check with simplemacines.org. 97 - does not connect directly with simplemachines.org, but rather 98 expects the client to. 99 - requires the admin_forum permission. 100 - uses the view_versions admin area. 101 - loads the view_versions sub template (in the Admin template.) 102 - accessed through ?action=admin;area=maintain;sa=routine;activity=version. 103 104 bool cacheLanguage(string template_name, string language, bool fatal, string theme_name) 105 // !!! 106 107 void MaintainReattributePosts() 108 // !!! 109 110 void MaintainDownloadBackup() 111 // !!! 112 113 void MaintainPurgeInactiveMembers() 114 // !!! 115 116 void MaintainRemoveOldPosts(bool do_action = true) 117 // !!! 118 119 mixed MaintainMassMoveTopics() 120 - Moves topics from one board to another. 121 - User the not_done template to pause the process. 122*/ 123 124// The maintenance access point. 125function ManageMaintenance() 126{ 127 global $txt, $modSettings, $scripturl, $context, $options; 128 129 // You absolutely must be an admin by here! 130 isAllowedTo('admin_forum'); 131 132 // Need something to talk about? 133 loadLanguage('ManageMaintenance'); 134 loadTemplate('ManageMaintenance'); 135 136 // This uses admin tabs - as it should! 137 $context[$context['admin_menu_name']]['tab_data'] = array( 138 'title' => $txt['maintain_title'], 139 'description' => $txt['maintain_info'], 140 'tabs' => array( 141 'routine' => array(), 142 'database' => array(), 143 'members' => array(), 144 'topics' => array(), 145 ), 146 ); 147 148 // So many things you can do - but frankly I won't let you - just these! 149 $subActions = array( 150 'routine' => array( 151 'function' => 'MaintainRoutine', 152 'template' => 'maintain_routine', 153 'activities' => array( 154 'version' => 'VersionDetail', 155 'repair' => 'MaintainFindFixErrors', 156 'recount' => 'AdminBoardRecount', 157 'logs' => 'MaintainEmptyUnimportantLogs', 158 'cleancache' => 'MaintainCleanCache', 159 ), 160 ), 161 'database' => array( 162 'function' => 'MaintainDatabase', 163 'template' => 'maintain_database', 164 'activities' => array( 165 'optimize' => 'OptimizeTables', 166 'backup' => 'MaintainDownloadBackup', 167 'convertentities' => 'ConvertEntities', 168 'convertutf8' => 'ConvertUtf8', 169 ), 170 ), 171 'members' => array( 172 'function' => 'MaintainMembers', 173 'template' => 'maintain_members', 174 'activities' => array( 175 'reattribute' => 'MaintainReattributePosts', 176 'purgeinactive' => 'MaintainPurgeInactiveMembers', 177 ), 178 ), 179 'topics' => array( 180 'function' => 'MaintainTopics', 181 'template' => 'maintain_topics', 182 'activities' => array( 183 'massmove' => 'MaintainMassMoveTopics', 184 'pruneold' => 'MaintainRemoveOldPosts', 185 ), 186 ), 187 'destroy' => array( 188 'function' => 'Destroy', 189 'activities' => array(), 190 ), 191 ); 192 193 // Yep, sub-action time! 194 if (isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']])) 195 $subAction = $_REQUEST['sa']; 196 else 197 $subAction = 'routine'; 198 199 // Doing something special? 200 if (isset($_REQUEST['activity']) && isset($subActions[$subAction]['activities'][$_REQUEST['activity']])) 201 $activity = $_REQUEST['activity']; 202 203 // Set a few things. 204 $context['page_title'] = $txt['maintain_title']; 205 $context['sub_action'] = $subAction; 206 $context['sub_template'] = !empty($subActions[$subAction]['template']) ? $subActions[$subAction]['template'] : ''; 207 208 // Finally fall through to what we are doing. 209 $subActions[$subAction]['function'](); 210 211 // Any special activity? 212 if (isset($activity)) 213 $subActions[$subAction]['activities'][$activity](); 214} 215 216// Supporting function for the database maintenance area. 217function MaintainDatabase() 218{ 219 global $context, $db_type, $db_character_set, $modSettings, $smcFunc, $txt; 220 221 // Show some conversion options? 222 $context['convert_utf8'] = $db_type == 'mysql' && (!isset($db_character_set) || $db_character_set !== 'utf8' || empty($modSettings['global_character_set']) || $modSettings['global_character_set'] !== 'UTF-8') && version_compare('4.1.2', preg_replace('~\-.+?$~', '', $smcFunc['db_server_info']())) <= 0; 223 $context['convert_entities'] = $db_type == 'mysql' && isset($db_character_set, $modSettings['global_character_set']) && $db_character_set === 'utf8' && $modSettings['global_character_set'] === 'UTF-8'; 224 225 if (isset($_GET['done']) && $_GET['done'] == 'convertutf8') 226 $context['maintenance_finished'] = $txt['utf8_title']; 227 if (isset($_GET['done']) && $_GET['done'] == 'convertentities') 228 $context['maintenance_finished'] = $txt['entity_convert_title']; 229} 230 231// Supporting function for the routine maintenance area. 232function MaintainRoutine() 233{ 234 global $context, $txt; 235 236 if (isset($_GET['done']) && $_GET['done'] == 'recount') 237 $context['maintenance_finished'] = $txt['maintain_recount']; 238} 239 240// Supporting function for the members maintenance area. 241function MaintainMembers() 242{ 243 global $context, $smcFunc, $txt; 244 245 // Get membergroups - for deleting members and the like. 246 $result = $smcFunc['db_query']('', ' 247 SELECT id_group, group_name 248 FROM {db_prefix}membergroups', 249 array( 250 ) 251 ); 252 $context['membergroups'] = array( 253 array( 254 'id' => 0, 255 'name' => $txt['maintain_members_ungrouped'] 256 ), 257 ); 258 while ($row = $smcFunc['db_fetch_assoc']($result)) 259 { 260 $context['membergroups'][] = array( 261 'id' => $row['id_group'], 262 'name' => $row['group_name'] 263 ); 264 } 265 $smcFunc['db_free_result']($result); 266} 267 268// Supporting function for the topics maintenance area. 269function MaintainTopics() 270{ 271 global $context, $smcFunc, $txt; 272 273 // Let's load up the boards in case they are useful. 274 $result = $smcFunc['db_query']('order_by_board_order', ' 275 SELECT b.id_board, b.name, b.child_level, c.name AS cat_name, c.id_cat 276 FROM {db_prefix}boards AS b 277 LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) 278 WHERE {query_see_board} 279 AND redirect = {string:blank_redirect}', 280 array( 281 'blank_redirect' => '', 282 ) 283 ); 284 $context['categories'] = array(); 285 while ($row = $smcFunc['db_fetch_assoc']($result)) 286 { 287 if (!isset($context['categories'][$row['id_cat']])) 288 $context['categories'][$row['id_cat']] = array( 289 'name' => $row['cat_name'], 290 'boards' => array() 291 ); 292 293 $context['categories'][$row['id_cat']]['boards'][] = array( 294 'id' => $row['id_board'], 295 'name' => $row['name'], 296 'child_level' => $row['child_level'] 297 ); 298 } 299 $smcFunc['db_free_result']($result); 300 301 if (isset($_GET['done']) && $_GET['done'] == 'purgeold') 302 $context['maintenance_finished'] = $txt['maintain_old']; 303 elseif (isset($_GET['done']) && $_GET['done'] == 'massmove') 304 $context['maintenance_finished'] = $txt['move_topics_maintenance']; 305} 306 307// Find and fix all errors. 308function MaintainFindFixErrors() 309{ 310 global $sourcedir; 311 312 require_once($sourcedir . '/RepairBoards.php'); 313 RepairBoards(); 314} 315 316// Wipes the whole cache directory. 317function MaintainCleanCache() 318{ 319 global $context, $txt; 320 321 // Just wipe the whole cache directory! 322 clean_cache(); 323 324 $context['maintenance_finished'] = $txt['maintain_cache']; 325} 326 327// Empties all uninmportant logs 328function MaintainEmptyUnimportantLogs() 329{ 330 global $context, $smcFunc, $txt; 331 332 checkSession(); 333 334 // No one's online now.... MUHAHAHAHA :P. 335 $smcFunc['db_query']('', ' 336 DELETE FROM {db_prefix}log_online'); 337 338 // Dump the banning logs. 339 $smcFunc['db_query']('', ' 340 DELETE FROM {db_prefix}log_banned'); 341 342 // Start id_error back at 0 and dump the error log. 343 $smcFunc['db_query']('truncate_table', ' 344 TRUNCATE {db_prefix}log_errors'); 345 346 // Clear out the spam log. 347 $smcFunc['db_query']('', ' 348 DELETE FROM {db_prefix}log_floodcontrol'); 349 350 // Clear out the karma actions. 351 $smcFunc['db_query']('', ' 352 DELETE FROM {db_prefix}log_karma'); 353 354 // Last but not least, the search logs! 355 $smcFunc['db_query']('truncate_table', ' 356 TRUNCATE {db_prefix}log_search_topics'); 357 358 $smcFunc['db_query']('truncate_table', ' 359 TRUNCATE {db_prefix}log_search_messages'); 360 361 $smcFunc['db_query']('truncate_table', ' 362 TRUNCATE {db_prefix}log_search_results'); 363 364 updateSettings(array('search_pointer' => 0)); 365 366 $context['maintenance_finished'] = $txt['maintain_logs']; 367} 368 369// Oh noes! 370function Destroy() 371{ 372 global $context; 373 374 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 375 <html xmlns="http://www.w3.org/1999/xhtml"', $context['right_to_left'] ? ' dir="rtl"' : '', '><head><title>', $context['forum_name_html_safe'], ' deleted!</title></head> 376 <body style="background-color: orange; font-family: arial, sans-serif; text-align: center;"> 377 <div style="margin-top: 8%; font-size: 400%; color: black;">Oh my, you killed ', $context['forum_name_html_safe'], '!</div> 378 <div style="margin-top: 7%; font-size: 500%; color: red;"><strong>You lazy bum!</strong></div> 379 </body></html>'; 380 obExit(false); 381} 382 383// Convert both data and database tables to UTF-8 character set. 384function ConvertUtf8() 385{ 386 global $scripturl, $context, $txt, $language, $db_character_set; 387 global $modSettings, $user_info, $sourcedir, $smcFunc, $db_prefix; 388 389 // Show me your badge! 390 isAllowedTo('admin_forum'); 391 392 // The character sets used in SMF's language files with their db equivalent. 393 $charsets = array( 394 // Chinese-traditional. 395 'big5' => 'big5', 396 // Chinese-simplified. 397 'gbk' => 'gbk', 398 // West European. 399 'ISO-8859-1' => 'latin1', 400 // Romanian. 401 'ISO-8859-2' => 'latin2', 402 // Turkish. 403 'ISO-8859-9' => 'latin5', 404 // West European with Euro sign. 405 'ISO-8859-15' => 'latin9', 406 // Thai. 407 'tis-620' => 'tis620', 408 // Persian, Chinese, etc. 409 'UTF-8' => 'utf8', 410 // Russian. 411 'windows-1251' => 'cp1251', 412 // Greek. 413 'windows-1253' => 'utf8', 414 // Hebrew. 415 'windows-1255' => 'utf8', 416 // Arabic. 417 'windows-1256' => 'cp1256', 418 ); 419 420 // Get a list of character sets supported by your MySQL server. 421 $request = $smcFunc['db_query']('', ' 422 SHOW CHARACTER SET', 423 array( 424 ) 425 ); 426 $db_charsets = array(); 427 while ($row = $smcFunc['db_fetch_assoc']($request)) 428 $db_charsets[] = $row['Charset']; 429 430 $smcFunc['db_free_result']($request); 431 432 // Character sets supported by both MySQL and SMF's language files. 433 $charsets = array_intersect($charsets, $db_charsets); 434 435 // This is for the first screen telling backups is good. 436 if (!isset($_POST['proceed'])) 437 { 438 // Character set conversions are only supported as of MySQL 4.1.2. 439 if (version_compare('4.1.2', preg_replace('~\-.+?$~', '', $smcFunc['db_server_info']())) > 0) 440 fatal_lang_error('utf8_db_version_too_low'); 441 442 // Use the messages.body column as indicator for the database charset. 443 $request = $smcFunc['db_query']('', ' 444 SHOW FULL COLUMNS 445 FROM {db_prefix}messages 446 LIKE {string:body_like}', 447 array( 448 'body_like' => 'body', 449 ) 450 ); 451 $column_info = $smcFunc['db_fetch_assoc']($request); 452 $smcFunc['db_free_result']($request); 453 454 // A collation looks like latin1_swedish. We only need the character set. 455 list($context['database_charset']) = explode('_', $column_info['Collation']); 456 $context['database_charset'] = in_array($context['database_charset'], $charsets) ? array_search($context['database_charset'], $charsets) : $context['database_charset']; 457 458 // No need to convert to UTF-8 if it already is. 459 if ($db_character_set === 'utf8' && !empty($modSettings['global_character_set']) && $modSettings['global_character_set'] === 'UTF-8') 460 fatal_lang_error('utf8_already_utf8'); 461 462 // Cannot do conversion if using a fulltext index 463 if (!empty($modSettings['search_index']) && $modSettings['search_index'] == 'fulltext') 464 fatal_lang_error('utf8_cannot_convert_fulltext'); 465 466 // Grab the character set from the default language file. 467 loadLanguage('index', $language, true); 468 $context['charset_detected'] = $txt['lang_character_set']; 469 $context['charset_about_detected'] = sprintf($txt['utf8_detected_charset'], $language, $context['charset_detected']); 470 471 // Go back to your own language. 472 loadLanguage('index', $user_info['language'], true); 473 474 // Show a warning if the character set seems not to be supported. 475 if (!isset($charsets[strtr(strtolower($context['charset_detected']), array('utf' => 'UTF', 'iso' => 'ISO'))])) 476 { 477 $context['charset_warning'] = sprintf($txt['utf8_charset_not_supported'], $txt['lang_character_set']); 478 479 // Default to ISO-8859-1. 480 $context['charset_detected'] = 'ISO-8859-1'; 481 } 482 483 $context['charset_list'] = array_keys($charsets); 484 485 $context['page_title'] = $txt['utf8_title']; 486 $context['sub_template'] = 'convert_utf8'; 487 return; 488 } 489 490 // After this point we're starting the conversion. But first: session check. 491 checkSession(); 492 493 // Translation table for the character sets not native for MySQL. 494 $translation_tables = array( 495 'windows-1255' => array( 496 '0x81' => '\'\'', '0x8A' => '\'\'', '0x8C' => '\'\'', 497 '0x8D' => '\'\'', '0x8E' => '\'\'', '0x8F' => '\'\'', 498 '0x90' => '\'\'', '0x9A' => '\'\'', '0x9C' => '\'\'', 499 '0x9D' => '\'\'', '0x9E' => '\'\'', '0x9F' => '\'\'', 500 '0xCA' => '\'\'', '0xD9' => '\'\'', '0xDA' => '\'\'', 501 '0xDB' => '\'\'', '0xDC' => '\'\'', '0xDD' => '\'\'', 502 '0xDE' => '\'\'', '0xDF' => '\'\'', '0xFB' => '\'\'', 503 '0xFC' => '\'\'', '0xFF' => '\'\'', '0xC2' => '0xFF', 504 '0x80' => '0xFC', '0xE2' => '0xFB', '0xA0' => '0xC2A0', 505 '0xA1' => '0xC2A1', '0xA2' => '0xC2A2', '0xA3' => '0xC2A3', 506 '0xA5' => '0xC2A5', '0xA6' => '0xC2A6', '0xA7' => '0xC2A7', 507 '0xA8' => '0xC2A8', '0xA9' => '0xC2A9', '0xAB' => '0xC2AB', 508 '0xAC' => '0xC2AC', '0xAD' => '0xC2AD', '0xAE' => '0xC2AE', 509 '0xAF' => '0xC2AF', '0xB0' => '0xC2B0', '0xB1' => '0xC2B1', 510 '0xB2' => '0xC2B2', '0xB3' => '0xC2B3', '0xB4' => '0xC2B4', 511 '0xB5' => '0xC2B5', '0xB6' => '0xC2B6', '0xB7' => '0xC2B7', 512 '0xB8' => '0xC2B8', '0xB9' => '0xC2B9', '0xBB' => '0xC2BB', 513 '0xBC' => '0xC2BC', '0xBD' => '0xC2BD', '0xBE' => '0xC2BE', 514 '0xBF' => '0xC2BF', '0xD7' => '0xD7B3', '0xD1' => '0xD781', 515 '0xD4' => '0xD7B0', '0xD5' => '0xD7B1', '0xD6' => '0xD7B2', 516 '0xE0' => '0xD790', '0xEA' => '0xD79A', '0xEC' => '0xD79C', 517 '0xED' => '0xD79D', '0xEE' => '0xD79E', '0xEF' => '0xD79F', 518 '0xF0' => '0xD7A0', '0xF1' => '0xD7A1', '0xF2' => '0xD7A2', 519 '0xF3' => '0xD7A3', '0xF5' => '0xD7A5', '0xF6' => '0xD7A6', 520 '0xF7' => '0xD7A7', '0xF8' => '0xD7A8', '0xF9' => '0xD7A9', 521 '0x82' => '0xE2809A', '0x84' => '0xE2809E', '0x85' => '0xE280A6', 522 '0x86' => '0xE280A0', '0x87' => '0xE280A1', '0x89' => '0xE280B0', 523 '0x8B' => '0xE280B9', '0x93' => '0xE2809C', '0x94' => '0xE2809D', 524 '0x95' => '0xE280A2', '0x97' => '0xE28094', '0x99' => '0xE284A2', 525 '0xC0' => '0xD6B0', '0xC1' => '0xD6B1', '0xC3' => '0xD6B3', 526 '0xC4' => '0xD6B4', '0xC5' => '0xD6B5', '0xC6' => '0xD6B6', 527 '0xC7' => '0xD6B7', '0xC8' => '0xD6B8', '0xC9' => '0xD6B9', 528 '0xCB' => '0xD6BB', '0xCC' => '0xD6BC', '0xCD' => '0xD6BD', 529 '0xCE' => '0xD6BE', '0xCF' => '0xD6BF', '0xD0' => '0xD780', 530 '0xD2' => '0xD782', '0xE3' => '0xD793', '0xE4' => '0xD794', 531 '0xE5' => '0xD795', '0xE7' => '0xD797', '0xE9' => '0xD799', 532 '0xFD' => '0xE2808E', '0xFE' => '0xE2808F', '0x92' => '0xE28099', 533 '0x83' => '0xC692', '0xD3' => '0xD783', '0x88' => '0xCB86', 534 '0x98' => '0xCB9C', '0x91' => '0xE28098', '0x96' => '0xE28093', 535 '0xBA' => '0xC3B7', '0x9B' => '0xE280BA', '0xAA' => '0xC397', 536 '0xA4' => '0xE282AA', '0xE1' => '0xD791', '0xE6' => '0xD796', 537 '0xE8' => '0xD798', '0xEB' => '0xD79B', '0xF4' => '0xD7A4', 538 '0xFA' => '0xD7AA', '0xFF' => '0xD6B2', '0xFC' => '0xE282AC', 539 '0xFB' => '0xD792', 540 ), 541 'windows-1253' => array( 542 '0x81' => '\'\'', '0x88' => '\'\'', '0x8A' => '\'\'', 543 '0x8C' => '\'\'', '0x8D' => '\'\'', '0x8E' => '\'\'', 544 '0x8F' => '\'\'', '0x90' => '\'\'', '0x98' => '\'\'', 545 '0x9A' => '\'\'', '0x9C' => '\'\'', '0x9D' => '\'\'', 546 '0x9E' => '\'\'', '0x9F' => '\'\'', '0xAA' => '\'\'', 547 '0xD2' => '\'\'', '0xFF' => '\'\'', '0xCE' => '0xCE9E', 548 '0xB8' => '0xCE88', '0xBA' => '0xCE8A', '0xBC' => '0xCE8C', 549 '0xBE' => '0xCE8E', '0xBF' => '0xCE8F', '0xC0' => '0xCE90', 550 '0xC8' => '0xCE98', '0xCA' => '0xCE9A', '0xCC' => '0xCE9C', 551 '0xCD' => '0xCE9D', '0xCF' => '0xCE9F', '0xDA' => '0xCEAA', 552 '0xE8' => '0xCEB8', '0xEA' => '0xCEBA', '0xEC' => '0xCEBC', 553 '0xEE' => '0xCEBE', '0xEF' => '0xCEBF', '0xC2' => '0xFF', 554 '0xBD' => '0xC2BD', '0xED' => '0xCEBD', '0xB2' => '0xC2B2', 555 '0xA0' => '0xC2A0', '0xA3' => '0xC2A3', '0xA4' => '0xC2A4', 556 '0xA5' => '0xC2A5', '0xA6' => '0xC2A6', '0xA7' => '0xC2A7', 557 '0xA8' => '0xC2A8', '0xA9' => '0xC2A9', '0xAB' => '0xC2AB', 558 '0xAC' => '0xC2AC', '0xAD' => '0xC2AD', '0xAE' => '0xC2AE', 559 '0xB0' => '0xC2B0', '0xB1' => '0xC2B1', '0xB3' => '0xC2B3', 560 '0xB5' => '0xC2B5', '0xB6' => '0xC2B6', '0xB7' => '0xC2B7', 561 '0xBB' => '0xC2BB', '0xE2' => '0xCEB2', '0x80' => '0xD2', 562 '0x82' => '0xE2809A', '0x84' => '0xE2809E', '0x85' => '0xE280A6', 563 '0x86' => '0xE280A0', '0xA1' => '0xCE85', '0xA2' => '0xCE86', 564 '0x87' => '0xE280A1', '0x89' => '0xE280B0', '0xB9' => '0xCE89', 565 '0x8B' => '0xE280B9', '0x91' => '0xE28098', '0x99' => '0xE284A2', 566 '0x92' => '0xE28099', '0x93' => '0xE2809C', '0x94' => '0xE2809D', 567 '0x95' => '0xE280A2', '0x96' => '0xE28093', '0x97' => '0xE28094', 568 '0x9B' => '0xE280BA', '0xAF' => '0xE28095', '0xB4' => '0xCE84', 569 '0xC1' => '0xCE91', '0xC3' => '0xCE93', '0xC4' => '0xCE94', 570 '0xC5' => '0xCE95', '0xC6' => '0xCE96', '0x83' => '0xC692', 571 '0xC7' => '0xCE97', '0xC9' => '0xCE99', '0xCB' => '0xCE9B', 572 '0xD0' => '0xCEA0', '0xD1' => '0xCEA1', '0xD3' => '0xCEA3', 573 '0xD4' => '0xCEA4', '0xD5' => '0xCEA5', '0xD6' => '0xCEA6', 574 '0xD7' => '0xCEA7', '0xD8' => '0xCEA8', '0xD9' => '0xCEA9', 575 '0xDB' => '0xCEAB', '0xDC' => '0xCEAC', '0xDD' => '0xCEAD', 576 '0xDE' => '0xCEAE', '0xDF' => '0xCEAF', '0xE0' => '0xCEB0', 577 '0xE1' => '0xCEB1', '0xE3' => '0xCEB3', '0xE4' => '0xCEB4', 578 '0xE5' => '0xCEB5', '0xE6' => '0xCEB6', '0xE7' => '0xCEB7', 579 '0xE9' => '0xCEB9', '0xEB' => '0xCEBB', '0xF0' => '0xCF80', 580 '0xF1' => '0xCF81', '0xF2' => '0xCF82', '0xF3' => '0xCF83', 581 '0xF4' => '0xCF84', '0xF5' => '0xCF85', '0xF6' => '0xCF86', 582 '0xF7' => '0xCF87', '0xF8' => '0xCF88', '0xF9' => '0xCF89', 583 '0xFA' => '0xCF8A', '0xFB' => '0xCF8B', '0xFC' => '0xCF8C', 584 '0xFD' => '0xCF8D', '0xFE' => '0xCF8E', '0xFF' => '0xCE92', 585 '0xD2' => '0xE282AC', 586 ), 587 ); 588 589 // Make some preparations. 590 if (isset($translation_tables[$_POST['src_charset']])) 591 { 592 $replace = '%field%'; 593 foreach ($translation_tables[$_POST['src_charset']] as $from => $to) 594 $replace = 'REPLACE(' . $replace . ', ' . $from . ', ' . $to . ')'; 595 } 596 597 // Grab a list of tables. 598 if (preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) === 1) 599 $queryTables = $smcFunc['db_query']('', ' 600 SHOW TABLE STATUS 601 FROM `' . strtr($match[1], array('`' => '')) . '` 602 LIKE {string:table_name}', 603 array( 604 'table_name' => str_replace('_', '\_', $match[2]) . '%', 605 ) 606 ); 607 else 608 $queryTables = $smcFunc['db_query']('', ' 609 SHOW TABLE STATUS 610 LIKE {string:table_name}', 611 array( 612 'table_name' => str_replace('_', '\_', $db_prefix) . '%', 613 ) 614 ); 615 616 while ($table_info = $smcFunc['db_fetch_assoc']($queryTables)) 617 { 618 // Just to make sure it doesn't time out. 619 if (function_exists('apache_reset_timeout')) 620 @apache_reset_timeout(); 621 622 $table_charsets = array(); 623 624 // Loop through each column. 625 $queryColumns = $smcFunc['db_query']('', ' 626 SHOW FULL COLUMNS 627 FROM ' . $table_info['Name'], 628 array( 629 ) 630 ); 631 while ($column_info = $smcFunc['db_fetch_assoc']($queryColumns)) 632 { 633 // Only text'ish columns have a character set and need converting. 634 if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false) 635 { 636 $collation = empty($column_info['Collation']) || $column_info['Collation'] === 'NULL' ? $table_info['Collation'] : $column_info['Collation']; 637 if (!empty($collation) && $collation !== 'NULL') 638 { 639 list($charset) = explode('_', $collation); 640 641 if (!isset($table_charsets[$charset])) 642 $table_charsets[$charset] = array(); 643 644 $table_charsets[$charset][] = $column_info; 645 } 646 } 647 } 648 $smcFunc['db_free_result']($queryColumns); 649 650 // Only change the column if the data doesn't match the current charset. 651 if ((count($table_charsets) === 1 && key($table_charsets) !== $charsets[$_POST['src_charset']]) || count($table_charsets) > 1) 652 { 653 $updates_blob = ''; 654 $updates_text = ''; 655 foreach ($table_charsets as $charset => $columns) 656 { 657 if ($charset !== $charsets[$_POST['src_charset']]) 658 { 659 foreach ($columns as $column) 660 { 661 $updates_blob .= ' 662 CHANGE COLUMN ' . $column['Field'] . ' ' . $column['Field'] . ' ' . strtr($column['Type'], array('text' => 'blob', 'char' => 'binary')) . ($column['Null'] === 'YES' ? ' NULL' : ' NOT NULL') . (strpos($column['Type'], 'char') === false ? '' : ' default \'' . $column['Default'] . '\'') . ','; 663 $updates_text .= ' 664 CHANGE COLUMN ' . $column['Field'] . ' ' . $column['Field'] . ' ' . $column['Type'] . ' CHARACTER SET ' . $charsets[$_POST['src_charset']] . ($column['Null'] === 'YES' ? '' : ' NOT NULL') . (strpos($column['Type'], 'char') === false ? '' : ' default \'' . $column['Default'] . '\'') . ','; 665 } 666 } 667 } 668 669 // Change the columns to binary form. 670 $smcFunc['db_query']('', ' 671 ALTER TABLE {raw:table_name}{raw:updates_blob}', 672 array( 673 'table_name' => $table_info['Name'], 674 'updates_blob' => substr($updates_blob, 0, -1), 675 ) 676 ); 677 678 // Convert the character set if MySQL has no native support for it. 679 if (isset($translation_tables[$_POST['src_charset']])) 680 { 681 $update = ''; 682 foreach ($table_charsets as $charset => $columns) 683 foreach ($columns as $column) 684 $update .= ' 685 ' . $column['Field'] . ' = ' . strtr($replace, array('%field%' => $column['Field'])) . ','; 686 687 $smcFunc['db_query']('', ' 688 UPDATE {raw:table_name} 689 SET {raw:updates}', 690 array( 691 'table_name' => $table_info['Name'], 692 'updates' => substr($update, 0, -1), 693 ) 694 ); 695 } 696 697 // Change the columns back, but with the proper character set. 698 $smcFunc['db_query']('', ' 699 ALTER TABLE {raw:table_name}{raw:updates_text}', 700 array( 701 'table_name' => $table_info['Name'], 702 'updates_text' => substr($updates_text, 0, -1), 703 ) 704 ); 705 } 706 707 // Now do the actual conversion (if still needed). 708 if ($charsets[$_POST['src_charset']] !== 'utf8') 709 $smcFunc['db_query']('', ' 710 ALTER TABLE {raw:table_name} 711 CONVERT TO CHARACTER SET utf8', 712 array( 713 'table_name' => $table_info['Name'], 714 ) 715 ); 716 } 717 $smcFunc['db_free_result']($queryTables); 718 719 // Let the settings know we have a new character set. 720 updateSettings(array('global_character_set' => 'UTF-8', 'previousCharacterSet' => $translation_tables[$_POST['src_charset']])); 721 722 // Store it in Settings.php too because it's needed before db connection. 723 require_once($sourcedir . '/Subs-Admin.php'); 724 updateSettingsFile(array('db_character_set' => '\'utf8\'')); 725 726 // The conversion might have messed up some serialized strings. Fix them! 727 require_once($sourcedir . '/Subs-Charset.php'); 728 fix_serialized_columns(); 729 730 redirectexit('action=admin;area=maintain;done=convertutf8'); 731} 732 733// Convert HTML-entities to their UTF-8 character equivalents. 734function ConvertEntities() 735{ 736 global $db_character_set, $modSettings, $context, $sourcedir, $smcFunc; 737 738 isAllowedTo('admin_forum'); 739 740 // Check to see if UTF-8 is currently the default character set. 741 if ($modSettings['global_character_set'] !== 'UTF-8' || !isset($db_character_set) || $db_character_set !== 'utf8') 742 fatal_lang_error('entity_convert_only_utf8'); 743 744 // Some starting values. 745 $context['table'] = empty($_REQUEST['table']) ? 0 : (int) $_REQUEST['table']; 746 $context['start'] = empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start']; 747 748 $context['start_time'] = time(); 749 750 $context['first_step'] = !isset($_REQUEST[$context['session_var']]); 751 $context['last_step'] = false; 752 753 // The first step is just a text screen with some explanation. 754 if ($context['first_step']) 755 { 756 $context['sub_template'] = 'convert_entities'; 757 return; 758 } 759 // Otherwise use the generic "not done" template. 760 $context['sub_template'] = 'not_done'; 761 $context['continue_post_data'] = ''; 762 $context['continue_countdown'] = 3; 763 764 // Now we're actually going to convert... 765 checkSession('request'); 766 767 // A list of tables ready for conversion. 768 $tables = array( 769 'ban_groups', 770 'ban_items', 771 'boards', 772 'calendar', 773 'calendar_holidays', 774 'categories', 775 'log_errors', 776 'log_search_subjects', 777 'membergroups', 778 'members', 779 'message_icons', 780 'messages', 781 'package_servers', 782 'personal_messages', 783 'pm_recipients', 784 'polls', 785 'poll_choices', 786 'smileys', 787 'themes', 788 ); 789 $context['num_tables'] = count($tables); 790 791 // This function will do the conversion later on. 792 $entity_replace = create_function('$string', ' 793 $num = substr($string, 0, 1) === \'x\' ? hexdec(substr($string, 1)) : (int) $string; 794 return $num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) ? \'\' : ($num < 0x80 ? \'&#\' . $num . \';\' : ($num < 0x800 ? chr(192 | $num >> 6) . chr(128 | $num & 63) : ($num < 0x10000 ? chr(224 | $num >> 12) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63) : chr(240 | $num >> 18) . chr(128 | $num >> 12 & 63) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63))));'); 795 796 // Loop through all tables that need converting. 797 for (; $context['table'] < $context['num_tables']; $context['table']++) 798 { 799 $cur_table = $tables[$context['table']]; 800 $primary_key = ''; 801 // Make sure we keep stuff unique! 802 $primary_keys = array(); 803 804 if (function_exists('apache_reset_timeout')) 805 @apache_reset_timeout(); 806 807 // Get a list of text columns. 808 $columns = array(); 809 $request = $smcFunc['db_query']('', ' 810 SHOW FULL COLUMNS 811 FROM {db_prefix}' . $cur_table, 812 array( 813 ) 814 ); 815 while ($column_info = $smcFunc['db_fetch_assoc']($request)) 816 if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false) 817 $columns[] = strtolower($column_info['Field']); 818 819 // Get the column with the (first) primary key. 820 $request = $smcFunc['db_query']('', ' 821 SHOW KEYS 822 FROM {db_prefix}' . $cur_table, 823 array( 824 ) 825 ); 826 while ($row = $smcFunc['db_fetch_assoc']($request)) 827 { 828 if ($row['Key_name'] === 'PRIMARY') 829 { 830 if (empty($primary_key) || ($row['Seq_in_index'] == 1 && !in_array(strtolower($row['Column_name']), $columns))) 831 $primary_key = $row['Column_name']; 832 833 $primary_keys[] = $row['Column_name']; 834 } 835 } 836 $smcFunc['db_free_result']($request); 837 838 // No primary key, no glory. 839 if (empty($primary_key)) 840 continue; 841 842 // Get the maximum value for the primary key. 843 $request = $smcFunc['db_query']('', ' 844 SELECT MAX(' . $primary_key . ') 845 FROM {db_prefix}' . $cur_table, 846 array( 847 ) 848 ); 849 list($max_value) = $smcFunc['db_fetch_row']($request); 850 $smcFunc['db_free_result']($request); 851 852 if (empty($max_value)) 853 continue; 854 855 while ($context['start'] <= $max_value) 856 { 857 // Retrieve a list of rows that has at least one entity to convert. 858 $request = $smcFunc['db_query']('', ' 859 SELECT {raw:primary_keys}, {raw:columns} 860 FROM {db_prefix}{raw:cur_table} 861 WHERE {raw:primary_key} BETWEEN {int:start} AND {int:start} + 499 862 AND {raw:like_compare} 863 LIMIT 500', 864 array( 865 'primary_keys' => implode(', ', $primary_keys), 866 'columns' => implode(', ', $columns), 867 'cur_table' => $cur_table, 868 'primary_key' => $primary_key, 869 'start' => $context['start'], 870 'like_compare' => '(' . implode(' LIKE \'%&#%\' OR ', $columns) . ' LIKE \'%&#%\')', 871 ) 872 ); 873 while ($row = $smcFunc['db_fetch_assoc']($request)) 874 { 875 $insertion_variables = array(); 876 $changes = array(); 877 foreach ($row as $column_name => $column_value) 878 if ($column_name !== $primary_key && strpos($column_value, '&#') !== false) 879 { 880 $changes[] = $column_name . ' = {string:changes_' . $column_name . '}'; 881 $insertion_variables['changes_' . $column_name] = preg_replace('~(&#(\d{1,7}|x[0-9a-fA-F]{1,6});)~e', '$entity_replace(\'\\2\')', $column_value); 882 } 883 884 $where = array(); 885 foreach ($primary_keys as $key) 886 { 887 $where[] = $key . ' = {string:where_' . $key . '}'; 888 $insertion_variables['where_' . $key] = $row[$key]; 889 } 890 891 892 // Update the row. 893 if (!empty($changes)) 894 $smcFunc['db_query']('', ' 895 UPDATE {db_prefix}' . $cur_table . ' 896 SET 897 ' . implode(', 898 ', $changes) . ' 899 WHERE ' . implode(' AND ', $where), 900 $insertion_variables 901 ); 902 } 903 $smcFunc['db_free_result']($request); 904 $context['start'] += 500; 905 906 // After ten seconds interrupt. 907 if (time() - $context['start_time'] > 10) 908 { 909 // Calculate an approximation of the percentage done. 910 $context['continue_percent'] = round(100 * ($context['table'] + ($context['start'] / $max_value)) / $context['num_tables'], 1); 911 $context['continue_get_data'] = '?action=admin;area=maintain;sa=database;activity=convertentities;table=' . $context['table'] . ';start=' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; 912 return; 913 } 914 } 915 $context['start'] = 0; 916 } 917 918 // Make sure all serialized strings are all right. 919 require_once($sourcedir . '/Subs-Charset.php'); 920 fix_serialized_columns(); 921 922 // If we're here, we must be done. 923 $context['continue_percent'] = 100; 924 $context['continue_get_data'] = '?action=admin;area=maintain;sa=database;done=convertentities'; 925 $context['last_step'] = true; 926 $context['continue_countdown'] = -1; 927} 928 929// Optimize the database's tables. 930function OptimizeTables() 931{ 932 global $db_type, $db_name, $db_prefix, $txt, $context, $scripturl, $sourcedir, $smcFunc; 933 934 isAllowedTo('admin_forum'); 935 936 checkSession('post'); 937 938 ignore_user_abort(true); 939 db_extend(); 940 941 // Start with no tables optimized. 942 $opttab = 0; 943 944 $context['page_title'] = $txt['database_optimize']; 945 $context['sub_template'] = 'optimize'; 946 947 // Only optimize the tables related to this smf install, not all the tables in the db 948 $real_prefix = preg_match('~^(`?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix; 949 950 // Get a list of tables, as well as how many there are. 951 $temp_tables = $smcFunc['db_list_tables'](false, $real_prefix . '%'); 952 $tables = array(); 953 foreach ($temp_tables as $table) 954 $tables[] = array('table_name' => $table); 955 956 // If there aren't any tables then I believe that would mean the world has exploded... 957 $context['num_tables'] = count($tables); 958 if ($context['num_tables'] == 0) 959 fatal_error('You appear to be running SMF in a flat file mode... fantastic!', false); 960 961 // For each table.... 962 $context['optimized_tables'] = array(); 963 foreach ($tables as $table) 964 { 965 // Optimize the table! We use backticks here because it might be a custom table. 966 $data_freed = $smcFunc['db_optimize_table']($table['table_name']); 967 968 // Optimizing one sqlite table optimizes them all. 969 if ($db_type == 'sqlite') 970 break; 971 972 if ($data_freed > 0) 973 $context['optimized_tables'][] = array( 974 'name' => $table['table_name'], 975 'data_freed' => $data_freed, 976 ); 977 } 978 979 // Number of tables, etc.... 980 $txt['database_numb_tables'] = sprintf($txt['database_numb_tables'], $context['num_tables']); 981 $context['num_tables_optimized'] = count($context['optimized_tables']); 982 983 // Check that we don't auto optimise again too soon! 984 require_once($sourcedir . '/ScheduledTasks.php'); 985 CalculateNextTrigger('auto_optimize', true); 986} 987 988// Recount all the important board totals. 989function AdminBoardRecount() 990{ 991 global $txt, $context, $scripturl, $modSettings, $sourcedir; 992 global $time_start, $smcFunc; 993 994 isAllowedTo('admin_forum'); 995 996 checkSession('request'); 997 998 $context['page_title'] = $txt['not_done_title']; 999 $context['continue_post_data'] = ''; 1000 $context['continue_countdown'] = '3'; 1001 $context['sub_template'] = 'not_done'; 1002 1003 // Try for as much time as possible. 1004 @set_time_limit(600); 1005 1006 // Step the number of topics at a time so things don't time out... 1007 $request = $smcFunc['db_query']('', ' 1008 SELECT MAX(id_topic) 1009 FROM {db_prefix}topics', 1010 array( 1011 ) 1012 ); 1013 list ($max_topics) = $smcFunc['db_fetch_row']($request); 1014 $smcFunc['db_free_result']($request); 1015 1016 $increment = min(max(50, ceil($max_topics / 4)), 2000); 1017 if (empty($_REQUEST['start'])) 1018 $_REQUEST['start'] = 0; 1019 1020 $total_steps = 8; 1021 1022 // Get each topic with a wrong reply count and fix it - let's just do some at a time, though. 1023 if (empty($_REQUEST['step'])) 1024 { 1025 $_REQUEST['step'] = 0; 1026 1027 while ($_REQUEST['start'] < $max_topics) 1028 { 1029 // Recount approved messages 1030 $request = $smcFunc['db_query']('', ' 1031 SELECT /*!40001 SQL_NO_CACHE */ t.id_topic, MAX(t.num_replies) AS num_replies, 1032 CASE WHEN COUNT(ma.id_msg) >= 1 THEN COUNT(ma.id_msg) - 1 ELSE 0 END AS real_num_replies 1033 FROM {db_prefix}topics AS t 1034 LEFT JOIN {db_prefix}messages AS ma ON (ma.id_topic = t.id_topic AND ma.approved = {int:is_approved}) 1035 WHERE t.id_topic > {int:start} 1036 AND t.id_topic <= {int:max_id} 1037 GROUP BY t.id_topic 1038 HAVING CASE WHEN COUNT(ma.id_msg) >= 1 THEN COUNT(ma.id_msg) - 1 ELSE 0 END != MAX(t.num_replies)', 1039 array( 1040 'is_approved' => 1, 1041 'start' => $_REQUEST['start'], 1042 'max_id' => $_REQUEST['start'] + $increment, 1043 ) 1044 ); 1045 while ($row = $smcFunc['db_fetch_assoc']($request)) 1046 $smcFunc['db_query']('', ' 1047 UPDATE {db_prefix}topics 1048 SET num_replies = {int:num_replies} 1049 WHERE id_topic = {int:id_topic}', 1050 array( 1051 'num_replies' => $row['real_num_replies'], 1052 'id_topic' => $row['id_topic'], 1053 ) 1054 ); 1055 $smcFunc['db_free_result']($request); 1056 1057 // Recount unapproved messages 1058 $request = $smcFunc['db_query']('', ' 1059 SELECT /*!40001 SQL_NO_CACHE */ t.id_topic, MAX(t.unapproved_posts) AS unapproved_posts, 1060 COUNT(mu.id_msg) AS real_unapproved_posts 1061 FROM {db_prefix}topics AS t 1062 LEFT JOIN {db_prefix}messages AS mu ON (mu.id_topic = t.id_topic AND mu.approved = {int:not_approved}) 1063 WHERE t.id_topic > {int:start} 1064 AND t.id_topic <= {int:max_id} 1065 GROUP BY t.id_topic 1066 HAVING COUNT(mu.id_msg) != MAX(t.unapproved_posts)', 1067 array( 1068 'not_approved' => 0, 1069 'start' => $_REQUEST['start'], 1070 'max_id' => $_REQUEST['start'] + $increment, 1071 ) 1072 ); 1073 while ($row = $smcFunc['db_fetch_assoc']($request)) 1074 $smcFunc['db_query']('', ' 1075 UPDATE {db_prefix}topics 1076 SET unapproved_posts = {int:unapproved_posts} 1077 WHERE id_topic = {int:id_topic}', 1078 array( 1079 'unapproved_posts' => $row['real_unapproved_posts'], 1080 'id_topic' => $row['id_topic'], 1081 ) 1082 ); 1083 $smcFunc['db_free_result']($request); 1084 1085 $_REQUEST['start'] += $increment; 1086 1087 if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) 1088 { 1089 $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=0;start=' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; 1090 $context['continue_percent'] = round((100 * $_REQUEST['start'] / $max_topics) / $total_steps); 1091 1092 return; 1093 } 1094 } 1095 1096 $_REQUEST['start'] = 0; 1097 } 1098 1099 // Update the post count of each board. 1100 if ($_REQUEST['step'] <= 1) 1101 { 1102 if (empty($_REQUEST['start'])) 1103 $smcFunc['db_query']('', ' 1104 UPDATE {db_prefix}boards 1105 SET num_posts = {int:num_posts} 1106 WHERE redirect = {string:redirect}', 1107 array( 1108 'num_posts' => 0, 1109 'redirect' => '', 1110 ) 1111 ); 1112 1113 while ($_REQUEST['start'] < $max_topics) 1114 { 1115 $request = $smcFunc['db_query']('', ' 1116 SELECT /*!40001 SQL_NO_CACHE */ m.id_board, COUNT(*) AS real_num_posts 1117 FROM {db_prefix}messages AS m 1118 WHERE m.id_topic > {int:id_topic_min} 1119 AND m.id_topic <= {int:id_topic_max} 1120 AND m.approved = {int:is_approved} 1121 GROUP BY m.id_board', 1122 array( 1123 'id_topic_min' => $_REQUEST['start'], 1124 'id_topic_max' => $_REQUEST['start'] + $increment, 1125 'is_approved' => 1, 1126 ) 1127 ); 1128 while ($row = $smcFunc['db_fetch_assoc']($request)) 1129 $smcFunc['db_query']('', ' 1130 UPDATE {db_prefix}boards 1131 SET num_posts = num_posts + {int:real_num_posts} 1132 WHERE id_board = {int:id_board}', 1133 array( 1134 'id_board' => $row['id_board'], 1135 'real_num_posts' => $row['real_num_posts'], 1136 ) 1137 ); 1138 $smcFunc['db_free_result']($request); 1139 1140 $_REQUEST['start'] += $increment; 1141 1142 if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) 1143 { 1144 $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=1;start=' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; 1145 $context['continue_percent'] = round((200 + 100 * $_REQUEST['start'] / $max_topics) / $total_steps); 1146 1147 return; 1148 } 1149 } 1150 1151 $_REQUEST['start'] = 0; 1152 } 1153 1154 // Update the topic count of each board. 1155 if ($_REQUEST['step'] <= 2) 1156 { 1157 if (empty($_REQUEST['start'])) 1158 $smcFunc['db_query']('', ' 1159 UPDATE {db_prefix}boards 1160 SET num_topics = {int:num_topics}', 1161 array( 1162 'num_topics' => 0, 1163 ) 1164 ); 1165 1166 while ($_REQUEST['start'] < $max_topics) 1167 { 1168 $request = $smcFunc['db_query']('', ' 1169 SELECT /*!40001 SQL_NO_CACHE */ t.id_board, COUNT(*) AS real_num_topics 1170 FROM {db_prefix}topics AS t 1171 WHERE t.approved = {int:is_approved} 1172 AND t.id_topic > {int:id_topic_min} 1173 AND t.id_topic <= {int:id_topic_max} 1174 GROUP BY t.id_board', 1175 array( 1176 'is_approved' => 1, 1177 'id_topic_min' => $_REQUEST['start'], 1178 'id_topic_max' => $_REQUEST['start'] + $increment, 1179 ) 1180 ); 1181 while ($row = $smcFunc['db_fetch_assoc']($request)) 1182 $smcFunc['db_query']('', ' 1183 UPDATE {db_prefix}boards 1184 SET num_topics = num_topics + {int:real_num_topics} 1185 WHERE id_board = {int:id_board}', 1186 array( 1187 'id_board' => $row['id_board'], 1188 'real_num_topics' => $row['real_num_topics'], 1189 ) 1190 ); 1191 $smcFunc['db_free_result']($request); 1192 1193 $_REQUEST['start'] += $increment; 1194 1195 if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) 1196 { 1197 $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=2;start=' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; 1198 $context['continue_percent'] = round((300 + 100 * $_REQUEST['start'] / $max_topics) / $total_steps); 1199 1200 return; 1201 } 1202 } 1203 1204 $_REQUEST['start'] = 0; 1205 } 1206 1207 // Update the unapproved post count of each board. 1208 if ($_REQUEST['step'] <= 3) 1209 { 1210 if (empty($_REQUEST['start'])) 1211 $smcFunc['db_query']('', ' 1212 UPDATE {db_prefix}boards 1213 SET unapproved_posts = {int:unapproved_posts}', 1214 array( 1215 'unapproved_posts' => 0, 1216 ) 1217 ); 1218 1219 while ($_REQUEST['start'] < $max_topics) 1220 { 1221 $request = $smcFunc['db_query']('', ' 1222 SELECT /*!40001 SQL_NO_CACHE */ m.id_board, COUNT(*) AS real_unapproved_posts 1223 FROM {db_prefix}messages AS m 1224 WHERE m.id_topic > {int:id_topic_min} 1225 AND m.id_topic <= {int:id_topic_max} 1226 AND m.approved = {int:is_approved} 1227 GROUP BY m.id_board', 1228 array( 1229 'id_topic_min' => $_REQUEST['start'], 1230 'id_topic_max' => $_REQUEST['start'] + $increment, 1231 'is_approved' => 0, 1232 ) 1233 ); 1234 while ($row = $smcFunc['db_fetch_assoc']($request)) 1235 $smcFunc['db_query']('', ' 1236 UPDATE {db_prefix}boards 1237 SET unapproved_posts = unapproved_posts + {int:unapproved_posts} 1238 WHERE id_board = {int:id_board}', 1239 array( 1240 'id_board' => $row['id_board'], 1241 'unapproved_posts' => $row['real_unapproved_posts'], 1242 ) 1243 ); 1244 $smcFunc['db_free_result']($request); 1245 1246 $_REQUEST['start'] += $increment; 1247 1248 if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) 1249 { 1250 $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=3;start=' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; 1251 $context['continue_percent'] = round((400 + 100 * $_REQUEST['start'] / $max_topics) / $total_steps); 1252 1253 return; 1254 } 1255 } 1256 1257 $_REQUEST['start'] = 0; 1258 } 1259 1260 // Update the unapproved topic count of each board. 1261 if ($_REQUEST['step'] <= 4) 1262 { 1263 if (empty($_REQUEST['start'])) 1264 $smcFunc['db_query']('', ' 1265 UPDATE {db_prefix}boards 1266 SET unapproved_topics = {int:unapproved_topics}', 1267 array( 1268 'unapproved_topics' => 0, 1269 ) 1270 ); 1271 1272 while ($_REQUEST['start'] < $max_topics) 1273 { 1274 $request = $smcFunc['db_query']('', ' 1275 SELECT /*!40001 SQL_NO_CACHE */ t.id_board, COUNT(*) AS real_unapproved_topics 1276 FROM {db_prefix}topics AS t 1277 WHERE t.approved = {int:is_approved} 1278 AND t.id_topic > {int:id_topic_min} 1279 AND t.id_topic <= {int:id_topic_max} 1280 GROUP BY t.id_board', 1281 array( 1282 'is_approved' => 0, 1283 'id_topic_min' => $_REQUEST['start'], 1284 'id_topic_max' => $_REQUEST['start'] + $increment, 1285 ) 1286 ); 1287 while ($row = $smcFunc['db_fetch_assoc']($request)) 1288 $smcFunc['db_query']('', ' 1289 UPDATE {db_prefix}boards 1290 SET unapproved_topics = unapproved_topics + {int:real_unapproved_topics} 1291 WHERE id_board = {int:id_board}', 1292 array( 1293 'id_board' => $row['id_board'], 1294 'real_unapproved_topics' => $row['real_unapproved_topics'], 1295 ) 1296 ); 1297 $smcFunc['db_free_result']($request); 1298 1299 $_REQUEST['start'] += $increment; 1300 1301 if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) 1302 { 1303 $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=4;start=' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; 1304 $context['continue_percent'] = round((500 + 100 * $_REQUEST['start'] / $max_topics) / $total_steps); 1305 1306 return; 1307 } 1308 } 1309 1310 $_REQUEST['start'] = 0; 1311 } 1312 1313 // Get all members with wrong number of personal messages. 1314 if ($_REQUEST['step'] <= 5) 1315 { 1316 $request = $smcFunc['db_query']('', ' 1317 SELECT /*!40001 SQL_NO_CACHE */ mem.id_member, COUNT(pmr.id_pm) AS real_num, 1318 MAX(mem.instant_messages) AS instant_messages 1319 FROM {db_prefix}members AS mem 1320 LEFT JOIN {db_prefix}pm_recipients AS pmr ON (mem.id_member = pmr.id_member AND pmr.deleted = {int:is_not_deleted}) 1321 GROUP BY mem.id_member 1322 HAVING COUNT(pmr.id_pm) != MAX(mem.instant_messages)', 1323 array( 1324 'is_not_deleted' => 0, 1325 ) 1326 ); 1327 while ($row = $smcFunc['db_fetch_assoc']($request)) 1328 updateMemberData($row['id_member'], array('instant_messages' => $row['real_num'])); 1329 $smcFunc['db_free_result']($request); 1330 1331 $request = $smcFunc['db_query']('', ' 1332 SELECT /*!40001 SQL_NO_CACHE */ mem.id_member, COUNT(pmr.id_pm) AS real_num, 1333 MAX(mem.unread_messages) AS unread_messages 1334 FROM {db_prefix}members AS mem 1335 LEFT JOIN {db_prefix}pm_recipients AS pmr ON (mem.id_member = pmr.id_member AND pmr.deleted = {int:is_not_deleted} AND pmr.is_read = {int:is_not_read}) 1336 GROUP BY mem.id_member 1337 HAVING COUNT(pmr.id_pm) != MAX(mem.unread_messages)', 1338 array( 1339 'is_not_deleted' => 0, 1340 'is_not_read' => 0, 1341 ) 1342 ); 1343 while ($row = $smcFunc['db_fetch_assoc']($request)) 1344 updateMemberData($row['id_member'], array('unread_messages' => $row['real_num'])); 1345 $smcFunc['db_free_result']($request); 1346 1347 if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) 1348 { 1349 $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=6;start=0;' . $context['session_var'] . '=' . $context['session_id']; 1350 $context['continue_percent'] = round(700 / $total_steps); 1351 1352 return; 1353 } 1354 } 1355 1356 // Any messages pointing to the wrong board? 1357 if ($_REQUEST['step'] <= 6) 1358 { 1359 while ($_REQUEST['start'] < $modSettings['maxMsgID']) 1360 { 1361 $request = $smcFunc['db_query']('', ' 1362 SELECT /*!40001 SQL_NO_CACHE */ t.id_board, m.id_msg 1363 FROM {db_prefix}messages AS m 1364 INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic AND t.id_board != m.id_board) 1365 WHERE m.id_msg > {int:id_msg_min} 1366 AND m.id_msg <= {int:id_msg_max}', 1367 array( 1368 'id_msg_min' => $_REQUEST['start'], 1369 'id_msg_max' => $_REQUEST['start'] + $increment, 1370 ) 1371 ); 1372 $boards = array(); 1373 while ($row = $smcFunc['db_fetch_assoc']($request)) 1374 $boards[$row['id_board']][] = $row['id_msg']; 1375 $smcFunc['db_free_result']($request); 1376 1377 foreach ($boards as $board_id => $messages) 1378 $smcFunc['db_query']('', ' 1379 UPDATE {db_prefix}messages 1380 SET id_board = {int:id_board} 1381 WHERE id_msg IN ({array_int:id_msg_array})', 1382 array( 1383 'id_msg_array' => $messages, 1384 'id_board' => $board_id, 1385 ) 1386 ); 1387 1388 $_REQUEST['start'] += $increment; 1389 1390 if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3) 1391 { 1392 $context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=6;start=' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; 1393 $context['continue_percent'] = round((700 + 100 * $_REQUEST['start'] / $modSettings['maxMsgID']) / $total_steps); 1394 1395 return; 1396 } 1397 } 1398 1399 $_REQUEST['start'] = 0; 1400 } 1401 1402 // Update the latest message of each board. 1403 $request = $smcFunc['db_query']('', ' 1404 SELECT m.id_board, MAX(m.id_msg) AS local_last_msg 1405 FROM {db_prefix}messages AS m 1406 WHERE m.approved = {int:is_approved} 1407 GROUP BY m.id_board', 1408 array( 1409 'is_approved' => 1, 1410 ) 1411 ); 1412 $realBoardCounts = array(); 1413 while ($row = $smcFunc['db_fetch_assoc']($request)) 1414 $realBoardCounts[$row['id_board']] = $row['local_last_msg']; 1415 $smcFunc['db_free_result']($request); 1416 1417 $request = $smcFunc['db_query']('', ' 1418 SELECT /*!40001 SQL_NO_CACHE */ id_board, id_parent, id_last_msg, child_level, id_msg_updated 1419 FROM {db_prefix}boards', 1420 array( 1421 ) 1422 ); 1423 $resort_me = array(); 1424 while ($row = $smcFunc['db_fetch_assoc']($request)) 1425 { 1426 $row['local_last_msg'] = isset($realBoardCounts[$row['id_board']]) ? $realBoardCounts[$row['id_board']] : 0; 1427 $resort_me[$row['child_level']][] = $row; 1428 } 1429 $smcFunc['db_free_result']($request); 1430 1431 krsort($resort_me); 1432 1433 $lastModifiedMsg = array(); 1434 foreach ($resort_me as $rows) 1435 foreach ($rows as $row) 1436 { 1437 // The latest message is the latest of the current board and its children. 1438 if (isset($lastModifiedMsg[$row['id_board']])) 1439 { 1440 $curLastModifiedMsg = max($row['local_last_msg'], $lastModifiedMsg[$row['id_board']]); 1441 1442 } 1443 else 1444 $curLastModifiedMsg = $row['local_last_msg']; 1445 1446 // If what is and what should be the latest message differ, an update is necessary. 1447 if ($row['local_last_msg'] != $row['id_last_msg'] || $curLastModifiedMsg != $row['id_msg_updated']) 1448 $smcFunc['db_query']('', ' 1449 UPDATE {db_prefix}boards 1450 SET id_last_msg = {int:id_last_msg}, id_msg_updated = {int:id_msg_updated} 1451 WHERE id_board = {int:id_board}', 1452 array( 1453 'id_last_msg' => $row['local_last_msg'], 1454 'id_msg_updated' => $curLastModifiedMsg, 1455 'id_board' => $row['id_board'], 1456 ) 1457 ); 1458 1459 // Parent boards inherit the latest modified message of their children. 1460 if (isset($lastModifiedMsg[$row['id_parent']])) 1461 $lastModifiedMsg[$row['id_parent']] = max($row['local_last_msg'], $lastModifiedMsg[$row['id_parent']]); 1462 else 1463 $lastModifiedMsg[$row['id_parent']] = $row['local_last_msg']; 1464 } 1465 1466 // Update all the basic statistics. 1467 updateStats('member'); 1468 updateStats('message'); 1469 updateStats('topic'); 1470 1471 // Finally, update the latest event times. 1472 require_once($sourcedir . '/ScheduledTasks.php'); 1473 CalculateNextTrigger(); 1474 1475 redirectexit('action=admin;area=maintain;sa=routine;done=recount'); 1476} 1477 1478// Perform a detailed version check. A very good thing ;). 1479function VersionDetail() 1480{ 1481 global $forum_version, $txt, $sourcedir, $context; 1482 1483 isAllowedTo('admin_forum'); 1484 1485 // Call the function that'll get all the version info we need. 1486 require_once($sourcedir . '/Subs-Admin.php'); 1487 $versionOptions = array( 1488 'include_ssi' => true, 1489 'include_subscriptions' => true, 1490 'sort_results' => true, 1491 ); 1492 $version_info = getFileVersions($versionOptions); 1493 1494 // Add the new info to the template context. 1495 $context += array( 1496 'file_versions' => $version_info['file_versions'], 1497 'default_template_versions' => $version_info['default_template_versions'], 1498 'template_versions' => $version_info['template_versions'], 1499 'default_language_versions' => $version_info['default_language_versions'], 1500 'default_known_languages' => array_keys($version_info['default_language_versions']), 1501 ); 1502 1503 // Make it easier to manage for the template. 1504 $context['forum_version'] = $forum_version; 1505 1506 $context['sub_template'] = 'view_versions'; 1507 $context['page_title'] = $txt['admin_version_check']; 1508} 1509 1510// This function caches the relevant language files, and if the cache doesn't work includes them with eval. 1511function cacheLanguage($template_name, $lang, $fatal, $theme_name) 1512{ 1513 global $language, $settings, $txt, $modSettings; 1514 global $sourcedir, $cachedir, $smcFunc; 1515 1516 // Is the file writable? 1517 $can_write = !empty($modSettings['cache_enable']) && is_writable($cachedir) ? 1 : 0; 1518 1519 // Assume it's not invalid! 1520 $invalid_file_found = false; 1521 1522 // Lets assume we can cache the file and include it. 1523 $do_include = true; 1524 1525 // Make sure we have $settings - if not we're in trouble and need to find it! 1526 if (empty($settings['default_theme_dir'])) 1527 { 1528 require_once($sourcedir . '/ScheduledTasks.php'); 1529 loadEssentialThemeData(); 1530 } 1531 1532 // Open the file to write to. 1533 if ($can_write) 1534 { 1535 $fh = fopen($cachedir . '/lang_' . $template_name . '_' . $lang . '_' . $theme_name . '.php', 'w'); 1536 @flock($fh, LOCK_EX); 1537 fwrite($fh, '<?php' . "\n"); 1538 } 1539 1540 // For each file open it up and write it out! 1541 foreach (explode('+', $template_name) as $template) 1542 { 1543 // Obviously, the current theme is most important to check. 1544 $attempts = array( 1545 array($settings['theme_dir'], $template, $lang, $settings['theme_url']), 1546 array($settings['theme_dir'], $template, $language, $settings['theme_url']), 1547 ); 1548 1549 // Do we have a base theme to worry about? 1550 if (isset($settings['base_theme_dir'])) 1551 { 1552 $attempts[] = array($settings['base_theme_dir'], $template, $lang, $settings['base_theme_url']); 1553 $attempts[] = array($settings['base_theme_dir'], $template, $language, $settings['base_theme_url']); 1554 } 1555 1556 // Fall back on the default theme if necessary. 1557 $attempts[] = array($settings['default_theme_dir'], $template, $lang, $settings['default_theme_url']); 1558 $attempts[] = array($settings['default_theme_dir'], $template, $language, $settings['default_theme_url']); 1559 1560 // Fall back on the English language if none of the preferred languages can be found. 1561 if (!in_array('english', array($lang, $language))) 1562 { 1563 $attempts[] = array($settings['theme_dir'], $template, 'english', $settings['theme_url']); 1564 $attempts[] = array($settings['default_theme_dir'], $template, 'english', $settings['default_theme_url']); 1565 } 1566 1567 // Try to find the language file. 1568 $found = false; 1569 foreach ($attempts as $k => $file) 1570 { 1571 if (file_exists($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php')) 1572 { 1573 // Are we caching? 1574 if ($can_write) 1575 { 1576 foreach (file($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php') as $line) 1577 { 1578 if (substr($line, 0, 2) != '?>' && substr($line, 0, 2) != '<?') 1579 { 1580 // Some common variables get parsed in... 1581 $line = preg_replace('~\{NL\}~', '\\\\n', $line); 1582 fwrite($fh, $line); 1583 } 1584 } 1585 } 1586 // If the cache directory is not writable, we're having a bad day. 1587 else 1588 $do_include = false; 1589 1590 // Include it for fun. 1591 require($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php'); 1592 1593 // Note that we found it. 1594 $found = true; 1595 1596 break; 1597 } 1598 } 1599 1600 // That couldn't be found! Log the error, but *try* to continue normally. 1601 if (!$found) 1602 { 1603 $invalid_file_found = true; 1604 1605 if ($fatal) 1606 { 1607 log_error(sprintf($txt['theme_language_error'], $template_name . '.' . $lang, 'template')); 1608 break; 1609 } 1610 } 1611 else 1612 unset($language_url); 1613 } 1614 1615 if ($can_write) 1616 { 1617 fwrite($fh, '?>'); 1618 @flock($fh, LOCK_UN); 1619 fclose($fh); 1620 1621 // If we couldn't find the file don't cache it! 1622 if ($invalid_file_found) 1623 @unlink($cachedir . '/lang_' . $template_name . '_' . $lang . '_' . $theme_name . '.php'); 1624 } 1625 1626 return $do_include; 1627} 1628 1629// Removing old posts doesn't take much as we really pass through. 1630function MaintainReattributePosts() 1631{ 1632 global $sourcedir, $context, $txt; 1633 1634 checkSession(); 1635 1636 // Find the member. 1637 require_once($sourcedir . '/Subs-Auth.php'); 1638 $members = findMembers($_POST['to']); 1639 1640 if (empty($members)) 1641 fatal_lang_error('reattribute_cannot_find_member'); 1642 1643 $memID = array_shift($members); 1644 $memID = $memID['id']; 1645 1646 $email = $_POST['type'] == 'email' ? $_POST['from_email'] : ''; 1647 $membername = $_POST['type'] == 'name' ? $_POST['from_name'] : ''; 1648 1649 // Now call the reattribute function. 1650 require_once($sourcedir . '/Subs-Members.php'); 1651 reattributePosts($memID, $email, $membername, !empty($_POST['posts'])); 1652 1653 $context['maintenance_finished'] = $txt['maintain_reattribute_posts']; 1654} 1655 1656// Handling function for the backup stuff. 1657function MaintainDownloadBackup() 1658{ 1659 global $sourcedir; 1660 1661 require_once($sourcedir . '/DumpDatabase.php'); 1662 DumpDatabase2(); 1663} 1664 1665// Removing old members? 1666function MaintainPurgeInactiveMembers() 1667{ 1668 global $sourcedir, $context, $smcFunc, $txt; 1669 1670 $_POST['maxdays'] = (int) $_POST['maxdays']; 1671 if (!empty($_POST['groups']) && $_POST['maxdays']) 1672 { 1673 checkSession(); 1674 1675 $groups = array(); 1676 foreach ($_POST['groups'] as $id => $dummy) 1677 $groups[] = (int) $id; 1678 $time_limit = (time() - ($_POST['maxdays'] * 24 * 3600)); 1679 $where_vars = array( 1680 'last_login' => $time_limit, 1681 ); 1682 $where = 'mem.last_login < {int:last_login}'; 1683 if ($_POST['del_type'] == 'activated') 1684 { 1685 $where .= ' AND mem.is_activated = {int:is_activated}'; 1686 $where_vars['is_activated'] = 0; 1687 } 1688 1689 // Need to get *all* groups then work out which (if any) we avoid. 1690 $request = $smcFunc['db_query']('', ' 1691 SELECT id_group, group_name, min_posts 1692 FROM {db_prefix}membergroups', 1693 array( 1694 ) 1695 ); 1696 while ($row = $smcFunc['db_fetch_assoc']($request)) 1697 { 1698 // Avoid this one? 1699 if (!in_array($row['id_group'], $groups)) 1700 { 1701 // Post group? 1702 if ($row['min_posts'] != -1) 1703 { 1704 $where .= ' AND mem.id_post_group != {int:id_post_group_' . $row['id_group'] . '}'; 1705 $where_vars['id_post_group_' . $row['id_group']] = $row['id_group']; 1706 } 1707 else 1708 { 1709 $where .= ' AND mem.id_group != {int:id_group_' . $row['id_group'] . '} AND NOT FIND_IN_SET({int:id_group_' . $row['id_group'] . '}, mem.additional_groups)'; 1710 $where_vars['id_group_' . $row['id_group']] = $row['id_group']; 1711 } 1712 } 1713 } 1714 $smcFunc['db_free_result']($request); 1715 1716 // If we have ungrouped unselected we need to avoid those guys. 1717 if (!in_array(0, $groups)) 1718 { 1719 $where .= ' AND (mem.id_group != 0 OR mem.additional_groups != {string:blank_add_groups})'; 1720 $where_vars['blank_add_groups'] = ''; 1721 } 1722 1723 // Select all the members we're about to murder/remove... 1724 $request = $smcFunc['db_query']('', ' 1725 SELECT mem.id_member, IFNULL(m.id_member, 0) AS is_mod 1726 FROM {db_prefix}members AS mem 1727 LEFT JOIN {db_prefix}moderators AS m ON (m.id_member = mem.id_member) 1728 WHERE ' . $where, 1729 $where_vars 1730 ); 1731 $members = array(); 1732 while ($row = $smcFunc['db_fetch_assoc']($request)) 1733 { 1734 if (!$row['is_mod'] || !in_array(3, $groups)) 1735 $members[] = $row['id_member']; 1736 } 1737 $smcFunc['db_free_result']($request); 1738 1739 require_once($sourcedir . '/Subs-Members.php'); 1740 deleteMembers($members); 1741 } 1742 1743 $context['maintenance_finished'] = $txt['maintain_members']; 1744} 1745 1746// Removing old posts doesn't take much as we really pass through. 1747function MaintainRemoveOldPosts() 1748{ 1749 global $sourcedir, $context, $txt; 1750 1751 // Actually do what we're told! 1752 require_once($sourcedir . '/RemoveTopic.php'); 1753 RemoveOldTopics2(); 1754} 1755 1756function MaintainMassMoveTopics() 1757{ 1758 global $smcFunc, $sourcedir, $context, $txt; 1759 1760 // Only admins. 1761 isAllowedTo('admin_forum'); 1762 1763 checkSession('request'); 1764 1765 // Set up to the context. 1766 $context['page_title'] = $txt['not_done_title']; 1767 $context['continue_countdown'] = '3'; 1768 $context['continue_post_data'] = ''; 1769 $context['continue_get_data'] = ''; 1770 $context['sub_template'] = 'not_done'; 1771 $context['start'] = empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start']; 1772 $context['start_time'] = time(); 1773 1774 // First time we do this? 1775 $id_board_from = isset($_POST['id_board_from']) ? (int) $_POST['id_board_from'] : (int) $_REQUEST['id_board_from']; 1776 $id_board_to = isset($_POST['id_board_to']) ? (int) $_POST['id_board_to'] : (int) $_REQUEST['id_board_to']; 1777 1778 // No boards then this is your stop. 1779 if (empty($id_board_from) || empty($id_board_to)) 1780 return; 1781 1782 // How many topics are we converting? 1783 if (!isset($_REQUEST['totaltopics'])) 1784 { 1785 $request = $smcFunc['db_query']('', ' 1786 SELECT COUNT(*) 1787 FROM {db_prefix}topics 1788 WHERE id_board = {int:id_board_from}', 1789 array( 1790 'id_board_from' => $id_board_from, 1791 ) 1792 ); 1793 list ($total_topics) = $smcFunc['db_fetch_row']($request); 1794 $smcFunc['db_free_result']($request); 1795 } 1796 else 1797 $total_topics = (int) $_REQUEST['totaltopics']; 1798 1799 // Seems like we need this here. 1800 $context['continue_get_data'] = '?action=admin;area=maintain;sa=topics;activity=massmove;id_board_from=' . $id_board_from . ';id_board_to=' . $id_board_to . ';totaltopics=' . $total_topics . ';start=' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; 1801 1802 // We have topics to move so start the process. 1803 if (!empty($total_topics)) 1804 { 1805 while ($context['start'] <= $total_topics) 1806 { 1807 // Lets get the topics. 1808 $request = $smcFunc['db_query']('', ' 1809 SELECT id_topic 1810 FROM {db_prefix}topics 1811 WHERE id_board = {int:id_board_from} 1812 LIMIT 10', 1813 array( 1814 'id_board_from' => $id_board_from, 1815 ) 1816 ); 1817 1818 // Get the ids. 1819 $topics = array(); 1820 while ($row = $smcFunc['db_fetch_assoc']($request)) 1821 $topics[] = $row['id_topic']; 1822 1823 // Just return if we don't have any topics left to move. 1824 if (empty($topics)) 1825 { 1826 cache_put_data('board-' . $id_board_from, null, 120); 1827 cache_put_data('board-' . $id_board_to, null, 120); 1828 redirectexit('action=admin;area=maintain;sa=topics;done=massmove'); 1829 } 1830 1831 // Lets move them. 1832 require_once($sourcedir . '/MoveTopic.php'); 1833 moveTopics($topics, $id_board_to); 1834 1835 // We've done at least ten more topics. 1836 $context['start'] += 10; 1837 1838 // Lets wait a while. 1839 if (time() - $context['start_time'] > 3) 1840 { 1841 // What's the percent? 1842 $context['continue_percent'] = round(100 * ($context['start'] / $total_topics), 1); 1843 $context['continue_get_data'] = '?action=admin;area=maintain;sa=topics;activity=massmove;id_board_from=' . $id_board_from . ';id_board_to=' . $id_board_to . ';totaltopics=' . $total_topics . ';start=' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; 1844 1845 // Let the template system do it's thang. 1846 return; 1847 } 1848 } 1849 } 1850 1851 // Don't confuse admins by having an out of date cache. 1852 cache_put_data('board-' . $id_board_from, null, 120); 1853 cache_put_data('board-' . $id_board_to, null, 120); 1854 1855 redirectexit('action=admin;area=maintain;sa=topics;done=massmove'); 1856} 1857 1858?>