RepairBoards.php 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644
  1. <?php
  2. /**
  3. * Simple Machines Forum (SMF)
  4. *
  5. * @package SMF
  6. * @author Simple Machines http://www.simplemachines.org
  7. * @copyright 2011 Simple Machines
  8. * @license http://www.simplemachines.org/about/smf/license.php BSD
  9. *
  10. * @version 2.0
  11. */
  12. if (!defined('SMF'))
  13. die('Hacking attempt...');
  14. /* This is here for the "repair any errors" feature in the admin center. It
  15. uses just two simple functions:
  16. void RepairBoards()
  17. - finds or repairs errors in the database to fix possible problems.
  18. - requires the admin_forum permission.
  19. - uses the raw_data sub template.
  20. - calls createSalvageArea() to create a new board, if necesary.
  21. - accessed by ?action=admin;area=repairboards.
  22. void pauseRepairProcess(array to_fix, string current_step_desc, int max_substep = none, force = false)
  23. - show the not_done template to avoid CGI timeouts and similar.
  24. - called when 3 or more seconds have passed while searching for errors.
  25. - if max_substep is set, $_GET['substep'] / $max_substep is the percent
  26. done this step is.
  27. array findForumErrors()
  28. - checks for errors in steps, until 5 seconds have passed.
  29. - keeps track of the errors it did find, so that the actual repair
  30. won't have to recheck everything.
  31. - returns the array of errors found.
  32. void createSalvageArea()
  33. - creates a salvage board/category if one doesn't already exist.
  34. - uses the forum's default language, and checks based on that name.
  35. */
  36. function RepairBoards()
  37. {
  38. global $txt, $scripturl, $db_connection, $context, $sourcedir;
  39. global $salvageCatID, $salvageBoardID, $smcFunc, $errorTests;
  40. isAllowedTo('admin_forum');
  41. // Try secure more memory.
  42. @ini_set('memory_limit', '128M');
  43. // Print out the top of the webpage.
  44. $context['page_title'] = $txt['admin_repair'];
  45. $context['sub_template'] = 'repair_boards';
  46. $context[$context['admin_menu_name']]['current_subsection'] = 'general';
  47. // Load the language file.
  48. loadLanguage('ManageMaintenance');
  49. // Make sure the tabs stay nice.
  50. $context[$context['admin_menu_name']]['tab_data'] = array(
  51. 'title' => $txt['maintain_title'],
  52. 'help' => '',
  53. 'description' => $txt['maintain_info'],
  54. 'tabs' => array(),
  55. );
  56. // Start displaying errors without fixing them.
  57. if (isset($_GET['fixErrors']))
  58. checkSession('get');
  59. // Will want this.
  60. loadForumTests();
  61. // Giant if/else. The first displays the forum errors if a variable is not set and asks
  62. // if you would like to continue, the other fixes the errors.
  63. if (!isset($_GET['fixErrors']))
  64. {
  65. $context['error_search'] = true;
  66. $context['repair_errors'] = array();
  67. $context['to_fix'] = findForumErrors();
  68. if (!empty($context['to_fix']))
  69. {
  70. $_SESSION['repairboards_to_fix'] = $context['to_fix'];
  71. $_SESSION['repairboards_to_fix2'] = null;
  72. if (empty($context['repair_errors']))
  73. $context['repair_errors'][] = '???';
  74. }
  75. }
  76. else
  77. {
  78. $context['error_search'] = false;
  79. $context['to_fix'] = isset($_SESSION['repairboards_to_fix']) ? $_SESSION['repairboards_to_fix'] : array();
  80. require_once($sourcedir . '/Subs-Boards.php');
  81. // Get the MySQL version for future reference.
  82. $mysql_version = $smcFunc['db_server_info']($db_connection);
  83. // Actually do the fix.
  84. findForumErrors(true);
  85. // Note that we've changed everything possible ;)
  86. updateSettings(array(
  87. 'settings_updated' => time(),
  88. ));
  89. updateStats('message');
  90. updateStats('topic');
  91. updateSettings(array(
  92. 'calendar_updated' => time(),
  93. ));
  94. if (!empty($salvageBoardID))
  95. {
  96. $context['redirect_to_recount'] = true;
  97. }
  98. $_SESSION['repairboards_to_fix'] = null;
  99. $_SESSION['repairboards_to_fix2'] = null;
  100. }
  101. }
  102. function pauseRepairProcess($to_fix, $current_step_description, $max_substep = 0, $force = false)
  103. {
  104. global $context, $txt, $time_start, $db_temp_cache, $db_cache;
  105. // More time, I need more time!
  106. @set_time_limit(600);
  107. if (function_exists('apache_reset_timeout'))
  108. @apache_reset_timeout();
  109. // Errr, wait. How much time has this taken already?
  110. if (!$force && time() - array_sum(explode(' ', $time_start)) < 3)
  111. return;
  112. // Restore the query cache if interested.
  113. if (!empty($db_temp_cache))
  114. $db_cache = $db_temp_cache;
  115. $context['continue_get_data'] = '?action=admin;area=repairboards' . (isset($_GET['fixErrors']) ? ';fixErrors' : '') . ';step=' . $_GET['step'] . ';substep=' . $_GET['substep'] . ';' . $context['session_var'] . '=' . $context['session_id'];
  116. $context['page_title'] = $txt['not_done_title'];
  117. $context['continue_post_data'] = '';
  118. $context['continue_countdown'] = '2';
  119. $context['sub_template'] = 'not_done';
  120. // Change these two if more steps are added!
  121. if (empty($max_substep))
  122. $context['continue_percent'] = round(($_GET['step'] * 100) / $context['total_steps']);
  123. else
  124. $context['continue_percent'] = round((($_GET['step'] + ($_GET['substep'] / $max_substep)) * 100) / $context['total_steps']);
  125. // Never more than 100%!
  126. $context['continue_percent'] = min($context['continue_percent'], 100);
  127. // What about substeps?
  128. $context['substep_enabled'] = $max_substep != 0;
  129. $context['substep_title'] = sprintf($txt['repair_currently_' . (isset($_GET['fixErrors']) ? 'fixing' : 'checking')], (isset($txt['repair_operation_' . $current_step_description]) ? $txt['repair_operation_' . $current_step_description] : $current_step_description));
  130. $context['substep_continue_percent'] = $max_substep == 0 ? 0 : round(($_GET['substep'] * 100) / $max_substep, 1);
  131. $_SESSION['repairboards_to_fix'] = $to_fix;
  132. $_SESSION['repairboards_to_fix2'] = $context['repair_errors'];
  133. obExit();
  134. }
  135. // Load up all the tests we might want to do ;)
  136. function loadForumTests()
  137. {
  138. global $smcFunc, $errorTests;
  139. /* Here this array is defined like so:
  140. string check_query: Query to be executed when testing if errors exist.
  141. string check_type: Defines how it knows if a problem was found. If set to count looks for the first variable from check_query
  142. being > 0. Anything else it looks for some results. If not set assumes you want results.
  143. string fix_it_query: When doing fixes if an error was detected this query is executed to "fix" it.
  144. string fix_query: The query to execute to get data when doing a fix. If not set check_query is used again.
  145. array fix_collect: This array is used if the fix is basically gathering all broken ids and then doing something with it.
  146. - string index: The value returned from the main query and passed to the processing function.
  147. - process: A function passed an array of ids to execute the fix on.
  148. function fix_processing:
  149. Function called for each row returned from fix_query to execute whatever fixes are required.
  150. function fix_full_processing:
  151. As above but does the while loop and everything itself - except the freeing.
  152. array force_fix: If this is set then the error types included within this array will also be assumed broken.
  153. Note: At the moment only processes these if they occur after the primary error in the array.
  154. */
  155. // This great array contains all of our error checks, fixes, etc etc etc.
  156. $errorTests = array(
  157. // Make a last-ditch-effort check to get rid of topics with zeros..
  158. 'zero_topics' => array(
  159. 'check_query' => '
  160. SELECT COUNT(*)
  161. FROM {db_prefix}topics
  162. WHERE id_topic = 0',
  163. 'check_type' => 'count',
  164. 'fix_it_query' => '
  165. UPDATE {db_prefix}topics
  166. SET id_topic = NULL
  167. WHERE id_topic = 0',
  168. 'message' => 'repair_zero_ids',
  169. ),
  170. // ... and same with messages.
  171. 'zero_messages' => array(
  172. 'check_query' => '
  173. SELECT COUNT(*)
  174. FROM {db_prefix}messages
  175. WHERE id_msg = 0',
  176. 'check_type' => 'count',
  177. 'fix_it_query' => '
  178. UPDATE {db_prefix}messages
  179. SET id_msg = NULL
  180. WHERE id_msg = 0',
  181. 'message' => 'repair_zero_ids',
  182. ),
  183. // Find messages that don't have existing topics.
  184. 'missing_topics' => array(
  185. 'substeps' => array(
  186. 'step_size' => 1000,
  187. 'step_max' => '
  188. SELECT MAX(id_topic)
  189. FROM {db_prefix}messages'
  190. ),
  191. 'check_query' => '
  192. SELECT m.id_topic, m.id_msg
  193. FROM {db_prefix}messages AS m
  194. LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
  195. WHERE m.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  196. AND t.id_topic IS NULL
  197. ORDER BY m.id_topic, m.id_msg',
  198. 'fix_query' => '
  199. SELECT
  200. m.id_board, m.id_topic, MIN(m.id_msg) AS myid_first_msg, MAX(m.id_msg) AS myid_last_msg,
  201. COUNT(*) - 1 AS my_num_replies
  202. FROM {db_prefix}messages AS m
  203. LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
  204. WHERE t.id_topic IS NULL
  205. GROUP BY m.id_topic, m.id_board',
  206. 'fix_processing' => create_function('$row', '
  207. global $smcFunc, $salvageBoardID;
  208. // Only if we don\'t have a reasonable idea of where to put it.
  209. if ($row[\'id_board\'] == 0)
  210. {
  211. createSalvageArea();
  212. $row[\'id_board\'] = (int) $salvageBoardID;
  213. }
  214. // Make sure that no topics claim the first/last message as theirs.
  215. $smcFunc[\'db_query\'](\'\', \'
  216. UPDATE {db_prefix}topics
  217. SET id_first_msg = 0
  218. WHERE id_first_msg = {int:id_first_msg}\',
  219. array(
  220. \'id_first_msg\' => $row[\'myid_first_msg\'],
  221. )
  222. );
  223. $smcFunc[\'db_query\'](\'\', \'
  224. UPDATE {db_prefix}topics
  225. SET id_last_msg = 0
  226. WHERE id_last_msg = {int:id_last_msg}\',
  227. array(
  228. \'id_last_msg\' => $row[\'myid_last_msg\'],
  229. )
  230. );
  231. $memberStartedID = (int) getMsgMemberID($row[\'myid_first_msg\']);
  232. $memberUpdatedID = (int) getMsgMemberID($row[\'myid_last_msg\']);
  233. $smcFunc[\'db_insert\'](\'\',
  234. \'{db_prefix}topics\',
  235. array(
  236. \'id_board\' => \'int\',
  237. \'id_member_started\' => \'int\',
  238. \'id_member_updated\' => \'int\',
  239. \'id_first_msg\' => \'int\',
  240. \'id_last_msg\' => \'int\',
  241. \'num_replies\' => \'int\'
  242. ),
  243. array(
  244. $row[\'id_board\'],
  245. $memberStartedID,
  246. $memberUpdatedID,
  247. $row[\'myid_first_msg\'],
  248. $row[\'myid_last_msg\'],
  249. $row[\'my_num_replies\']
  250. ),
  251. array(\'id_topic\')
  252. );
  253. $newTopicID = $smcFunc[\'db_insert_id\']("{db_prefix}topics", \'id_topic\');
  254. $smcFunc[\'db_query\'](\'\', "
  255. UPDATE {db_prefix}messages
  256. SET id_topic = $newTopicID, id_board = $row[id_board]
  257. WHERE id_topic = $row[id_topic]",
  258. array(
  259. )
  260. );
  261. '),
  262. 'force_fix' => array('stats_topics'),
  263. 'messages' => array('repair_missing_topics', 'id_msg', 'id_topic'),
  264. ),
  265. // Find topics with no messages.
  266. 'missing_messages' => array(
  267. 'substeps' => array(
  268. 'step_size' => 1000,
  269. 'step_max' => '
  270. SELECT MAX(id_topic)
  271. FROM {db_prefix}topics'
  272. ),
  273. 'check_query' => '
  274. SELECT t.id_topic, COUNT(m.id_msg) AS num_msg
  275. FROM {db_prefix}topics AS t
  276. LEFT JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic)
  277. WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  278. GROUP BY t.id_topic
  279. HAVING COUNT(m.id_msg) = 0',
  280. // Remove all topics that have zero messages in the messages table.
  281. 'fix_collect' => array(
  282. 'index' => 'id_topic',
  283. 'process' => create_function('$topics', '
  284. global $smcFunc;
  285. $smcFunc[\'db_query\'](\'\', "
  286. DELETE FROM {db_prefix}topics
  287. WHERE id_topic IN ({array_int:topics})",
  288. array(
  289. \'topics\' => $topics
  290. )
  291. );
  292. $smcFunc[\'db_query\'](\'\', "
  293. DELETE FROM {db_prefix}log_topics
  294. WHERE id_topic IN ({array_int:topics})",
  295. array(
  296. \'topics\' => $topics
  297. )
  298. );
  299. '),
  300. ),
  301. 'messages' => array('repair_missing_messages', 'id_topic'),
  302. ),
  303. 'polls_missing_topics' => array(
  304. 'substeps' => array(
  305. 'step_size' => 500,
  306. 'step_max' => '
  307. SELECT MAX(id_poll)
  308. FROM {db_prefix}polls'
  309. ),
  310. 'check_query' => '
  311. SELECT p.id_poll, p.id_member, p.poster_name, t.id_board
  312. FROM {db_prefix}polls AS p
  313. LEFT JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll)
  314. WHERE p.id_poll BETWEEN {STEP_LOW} AND {STEP_HIGH}
  315. AND t.id_poll IS NULL',
  316. 'fix_processing' => create_function('$row', '
  317. global $smcFunc, $salvageBoardID, $txt;
  318. // Only if we don\'t have a reasonable idea of where to put it.
  319. if ($row[\'id_board\'] == 0)
  320. {
  321. createSalvageArea();
  322. $row[\'id_board\'] = (int) $salvageBoardID;
  323. }
  324. $row[\'poster_name\'] = !empty($row[\'poster_name\']) ? $row[\'poster_name\'] : $txt[\'guest\'];
  325. $smcFunc[\'db_insert\'](\'\',
  326. \'{db_prefix}messages\',
  327. array(
  328. \'id_board\' => \'int\',
  329. \'id_topic\' => \'int\',
  330. \'poster_time\' => \'int\',
  331. \'id_member\' => \'int\',
  332. \'subject\' => \'string-255\',
  333. \'poster_name\' => \'string-255\',
  334. \'poster_email\' => \'string-255\',
  335. \'poster_ip\' => \'string-16\',
  336. \'smileys_enabled\' => \'int\',
  337. \'body\' => \'string-65534\',
  338. \'icon\' => \'string-16\',
  339. \'approved\' => \'int\',
  340. ),
  341. array(
  342. $row[\'id_board\'],
  343. 0,
  344. time(),
  345. $row[\'id_member\'],
  346. $txt[\'salvaged_poll_topic_name\'],
  347. $row[\'poster_name\'],
  348. \'\',
  349. \'127.0.0.1\',
  350. 1,
  351. $txt[\'salvaged_poll_message_body\'],
  352. \'xx\',
  353. 1,
  354. ),
  355. array(\'id_topic\')
  356. );
  357. $newMessageID = $smcFunc[\'db_insert_id\']("{db_prefix}messages", \'id_msg\');
  358. $smcFunc[\'db_insert\'](\'\',
  359. \'{db_prefix}topics\',
  360. array(
  361. \'id_board\' => \'int\',
  362. \'id_poll\' => \'int\',
  363. \'id_member_started\' => \'int\',
  364. \'id_member_updated\' => \'int\',
  365. \'id_first_msg\' => \'int\',
  366. \'id_last_msg\' => \'int\',
  367. \'num_replies\' => \'int\',
  368. ),
  369. array(
  370. $row[\'id_board\'],
  371. $row[\'id_poll\'],
  372. $row[\'id_member\'],
  373. $row[\'id_member\'],
  374. $newMessageID,
  375. $newMessageID,
  376. 0,
  377. ),
  378. array(\'id_topic\')
  379. );
  380. $newTopicID = $smcFunc[\'db_insert_id\']("{db_prefix}topics", \'id_topic\');
  381. $smcFunc[\'db_query\'](\'\', "
  382. UPDATE {db_prefix}messages
  383. SET id_topic = $newTopicID, id_board = $row[id_board]
  384. WHERE id_msg = $newMessageID",
  385. array(
  386. )
  387. );
  388. updateStats(\'subject\', $newTopicID, $txt[\'salvaged_poll_topic_name\']);
  389. '),
  390. 'force_fix' => array('stats_topics'),
  391. 'messages' => array('repair_polls_missing_topics', 'id_poll', 'id_topic'),
  392. ),
  393. 'stats_topics' => array(
  394. 'substeps' => array(
  395. 'step_size' => 200,
  396. 'step_max' => '
  397. SELECT MAX(id_topic)
  398. FROM {db_prefix}topics'
  399. ),
  400. 'check_query' => '
  401. SELECT
  402. t.id_topic, t.id_first_msg, t.id_last_msg,
  403. CASE WHEN MIN(ma.id_msg) > 0 THEN
  404. CASE WHEN MIN(mu.id_msg) > 0 THEN
  405. CASE WHEN MIN(mu.id_msg) < MIN(ma.id_msg) THEN MIN(mu.id_msg) ELSE MIN(ma.id_msg) END ELSE
  406. MIN(ma.id_msg) END ELSE
  407. MIN(mu.id_msg) END AS myid_first_msg,
  408. CASE WHEN MAX(ma.id_msg) > 0 THEN MAX(ma.id_msg) ELSE MIN(mu.id_msg) END AS myid_last_msg,
  409. t.approved, mf.approved, mf.approved AS firstmsg_approved
  410. FROM {db_prefix}topics AS t
  411. LEFT JOIN {db_prefix}messages AS ma ON (ma.id_topic = t.id_topic AND ma.approved = 1)
  412. LEFT JOIN {db_prefix}messages AS mu ON (mu.id_topic = t.id_topic AND mu.approved = 0)
  413. LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
  414. WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  415. GROUP BY t.id_topic, t.id_first_msg, t.id_last_msg, t.approved, mf.approved
  416. ORDER BY t.id_topic',
  417. 'fix_processing' => create_function('$row', '
  418. global $smcFunc;
  419. $row[\'firstmsg_approved\'] = (int) $row[\'firstmsg_approved\'];
  420. $row[\'myid_first_msg\'] = (int) $row[\'myid_first_msg\'];
  421. $row[\'myid_last_msg\'] = (int) $row[\'myid_last_msg\'];
  422. // Not really a problem?
  423. if ($row[\'myid_first_msg\'] == $row[\'myid_first_msg\'] && $row[\'myid_first_msg\'] == $row[\'myid_first_msg\'] && $row[\'approved\'] == $row[\'firstmsg_approved\'])
  424. return false;
  425. $memberStartedID = (int) getMsgMemberID($row[\'myid_first_msg\']);
  426. $memberUpdatedID = (int) getMsgMemberID($row[\'myid_last_msg\']);
  427. $smcFunc[\'db_query\'](\'\', "
  428. UPDATE {db_prefix}topics
  429. SET id_first_msg = $row[myid_first_msg],
  430. id_member_started = $memberStartedID, id_last_msg = $row[myid_last_msg],
  431. id_member_updated = $memberUpdatedID, approved = $row[firstmsg_approved]
  432. WHERE id_topic = $row[id_topic]",
  433. array(
  434. )
  435. );
  436. '),
  437. 'message_function' => create_function('$row', '
  438. global $txt, $context;
  439. // A pretend error?
  440. if ($row[\'myid_first_msg\'] == $row[\'myid_first_msg\'] && $row[\'myid_first_msg\'] == $row[\'myid_first_msg\'] && $row[\'approved\'] == $row[\'firstmsg_approved\'])
  441. return false;
  442. if ($row[\'id_first_msg\'] != $row[\'myid_first_msg\'])
  443. $context[\'repair_errors\'][] = sprintf($txt[\'repair_stats_topics_1\'], $row[\'id_topic\'], $row[\'id_first_msg\']);
  444. if ($row[\'id_last_msg\'] != $row[\'myid_last_msg\'])
  445. $context[\'repair_errors\'][] = sprintf($txt[\'repair_stats_topics_2\'], $row[\'id_topic\'], $row[\'id_last_msg\']);
  446. if ($row[\'approved\'] != $row[\'firstmsg_approved\'])
  447. $context[\'repair_errors\'][] = sprintf($txt[\'repair_stats_topics_5\'], $row[\'id_topic\']);
  448. return true;
  449. '),
  450. ),
  451. // Find topics with incorrect num_replies.
  452. 'stats_topics2' => array(
  453. 'substeps' => array(
  454. 'step_size' => 300,
  455. 'step_max' => '
  456. SELECT MAX(id_topic)
  457. FROM {db_prefix}topics'
  458. ),
  459. 'check_query' => '
  460. SELECT
  461. t.id_topic, t.num_replies, mf.approved,
  462. CASE WHEN COUNT(ma.id_msg) > 0 THEN CASE WHEN mf.approved > 0 THEN COUNT(ma.id_msg) - 1 ELSE COUNT(ma.id_msg) END ELSE 0 END AS my_num_replies
  463. FROM {db_prefix}topics AS t
  464. LEFT JOIN {db_prefix}messages AS ma ON (ma.id_topic = t.id_topic AND ma.approved = 1)
  465. LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
  466. WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  467. GROUP BY t.id_topic, t.num_replies, mf.approved
  468. ORDER BY t.id_topic',
  469. 'fix_processing' => create_function('$row', '
  470. global $smcFunc;
  471. $row[\'my_num_replies\'] = (int) $row[\'my_num_replies\'];
  472. // Not really a problem?
  473. if ($row[\'my_num_replies\'] == $row[\'num_replies\'])
  474. return false;
  475. $smcFunc[\'db_query\'](\'\', "
  476. UPDATE {db_prefix}topics
  477. SET num_replies = $row[my_num_replies]
  478. WHERE id_topic = $row[id_topic]",
  479. array(
  480. )
  481. );
  482. '),
  483. 'message_function' => create_function('$row', '
  484. global $txt, $context;
  485. // Just joking?
  486. if ($row[\'my_num_replies\'] == $row[\'num_replies\'])
  487. return false;
  488. if ($row[\'num_replies\'] != $row[\'my_num_replies\'])
  489. $context[\'repair_errors\'][] = sprintf($txt[\'repair_stats_topics_3\'], $row[\'id_topic\'], $row[\'num_replies\']);
  490. return true;
  491. '),
  492. ),
  493. // Find topics with incorrect unapproved_posts.
  494. 'stats_topics3' => array(
  495. 'substeps' => array(
  496. 'step_size' => 1000,
  497. 'step_max' => '
  498. SELECT MAX(id_topic)
  499. FROM {db_prefix}topics'
  500. ),
  501. 'check_query' => '
  502. SELECT
  503. t.id_topic, t.unapproved_posts, COUNT(mu.id_msg) AS my_unapproved_posts
  504. FROM {db_prefix}topics AS t
  505. LEFT JOIN {db_prefix}messages AS mu ON (mu.id_topic = t.id_topic AND mu.approved = 0)
  506. WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  507. GROUP BY t.id_topic, t.unapproved_posts
  508. HAVING unapproved_posts != COUNT(mu.id_msg)
  509. ORDER BY t.id_topic',
  510. 'fix_processing' => create_function('$row', '
  511. global $smcFunc;
  512. $row[\'my_unapproved_posts\'] = (int) $row[\'my_unapproved_posts\'];
  513. $smcFunc[\'db_query\'](\'\', "
  514. UPDATE {db_prefix}topics
  515. SET unapproved_posts = $row[my_unapproved_posts]
  516. WHERE id_topic = $row[id_topic]",
  517. array(
  518. )
  519. );
  520. '),
  521. 'messages' => array('repair_stats_topics_4', 'id_topic', 'unapproved_posts'),
  522. ),
  523. // Find topics with nonexistent boards.
  524. 'missing_boards' => array(
  525. 'substeps' => array(
  526. 'step_size' => 1000,
  527. 'step_max' => '
  528. SELECT MAX(id_topic)
  529. FROM {db_prefix}topics'
  530. ),
  531. 'check_query' => '
  532. SELECT t.id_topic, t.id_board
  533. FROM {db_prefix}topics AS t
  534. LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
  535. WHERE b.id_board IS NULL
  536. AND t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  537. ORDER BY t.id_board, t.id_topic',
  538. 'fix_query' => '
  539. SELECT t.id_board, COUNT(*) AS my_num_topics, COUNT(m.id_msg) AS my_num_posts
  540. FROM {db_prefix}topics AS t
  541. LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
  542. LEFT JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic)
  543. WHERE b.id_board IS NULL
  544. AND t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  545. GROUP BY t.id_board',
  546. 'fix_processing' => create_function('$row', '
  547. global $smcFunc, $salvageCatID;
  548. createSalvageArea();
  549. $row[\'my_num_topics\'] = (int) $row[\'my_num_topics\'];
  550. $row[\'my_num_posts\'] = (int) $row[\'my_num_posts\'];
  551. $smcFunc[\'db_insert\'](\'\',
  552. \'{db_prefix}boards\',
  553. array(\'id_cat\' => \'int\', \'name\' => \'string\', \'description\' => \'string\', \'num_topics\' => \'int\', \'num_posts\' => \'int\', \'member_groups\' => \'string\'),
  554. array($salvageCatID, \'Salvaged board\', \'\', $row[\'my_num_topics\'], $row[\'my_num_posts\'], \'1\'),
  555. array(\'id_board\')
  556. );
  557. $newBoardID = $smcFunc[\'db_insert_id\'](\'{db_prefix}boards\', \'id_board\');
  558. $smcFunc[\'db_query\'](\'\', "
  559. UPDATE {db_prefix}topics
  560. SET id_board = $newBoardID
  561. WHERE id_board = $row[id_board]",
  562. array(
  563. )
  564. );
  565. $smcFunc[\'db_query\'](\'\', "
  566. UPDATE {db_prefix}messages
  567. SET id_board = $newBoardID
  568. WHERE id_board = $row[id_board]",
  569. array(
  570. )
  571. );
  572. '),
  573. 'messages' => array('repair_missing_boards', 'id_topic', 'id_board'),
  574. ),
  575. // Find boards with nonexistent categories.
  576. 'missing_categories' => array(
  577. 'check_query' => '
  578. SELECT b.id_board, b.id_cat
  579. FROM {db_prefix}boards AS b
  580. LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
  581. WHERE c.id_cat IS NULL
  582. ORDER BY b.id_cat, b.id_board',
  583. 'fix_collect' => array(
  584. 'index' => 'id_cat',
  585. 'process' => create_function('$cats', '
  586. global $smcFunc, $salvageCatID;
  587. createSalvageArea();
  588. $smcFunc[\'db_query\'](\'\', "
  589. UPDATE {db_prefix}boards
  590. SET id_cat = $salvageCatID
  591. WHERE id_cat IN ({array_int:categories})",
  592. array(
  593. \'categories\' => $cats
  594. )
  595. );
  596. '),
  597. ),
  598. 'messages' => array('repair_missing_categories', 'id_board', 'id_cat'),
  599. ),
  600. // Find messages with nonexistent members.
  601. 'missing_posters' => array(
  602. 'substeps' => array(
  603. 'step_size' => 2000,
  604. 'step_max' => '
  605. SELECT MAX(id_msg)
  606. FROM {db_prefix}messages'
  607. ),
  608. 'check_query' => '
  609. SELECT m.id_msg, m.id_member
  610. FROM {db_prefix}messages AS m
  611. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
  612. WHERE mem.id_member IS NULL
  613. AND m.id_member != 0
  614. AND m.id_msg BETWEEN {STEP_LOW} AND {STEP_HIGH}
  615. ORDER BY m.id_msg',
  616. // Last step-make sure all non-guest posters still exist.
  617. 'fix_collect' => array(
  618. 'index' => 'id_msg',
  619. 'process' => create_function('$msgs', '
  620. global $smcFunc;
  621. $smcFunc[\'db_query\'](\'\', "
  622. UPDATE {db_prefix}messages
  623. SET id_member = 0
  624. WHERE id_msg IN ({array_int:msgs})",
  625. array(
  626. \'msgs\' => $msgs
  627. )
  628. );
  629. '),
  630. ),
  631. 'messages' => array('repair_missing_posters', 'id_msg', 'id_member'),
  632. ),
  633. // Find boards with nonexistent parents.
  634. 'missing_parents' => array(
  635. 'check_query' => '
  636. SELECT b.id_board, b.id_parent
  637. FROM {db_prefix}boards AS b
  638. LEFT JOIN {db_prefix}boards AS p ON (p.id_board = b.id_parent)
  639. WHERE b.id_parent != 0
  640. AND (p.id_board IS NULL OR p.id_board = b.id_board)
  641. ORDER BY b.id_parent, b.id_board',
  642. 'fix_collect' => array(
  643. 'index' => 'id_parent',
  644. 'process' => create_function('$parents', '
  645. global $smcFunc, $salvageBoardID, $salvageCatID;
  646. createSalvageArea();
  647. $smcFunc[\'db_query\'](\'\', "
  648. UPDATE {db_prefix}boards
  649. SET id_parent = $salvageBoardID, id_cat = $salvageCatID, child_level = 1
  650. WHERE id_parent IN ({array_int:parents})",
  651. array(
  652. \'parents\' => $parents
  653. )
  654. );
  655. '),
  656. ),
  657. 'messages' => array('repair_missing_parents', 'id_board', 'id_parent'),
  658. ),
  659. 'missing_polls' => array(
  660. 'substeps' => array(
  661. 'step_size' => 500,
  662. 'step_max' => '
  663. SELECT MAX(id_poll)
  664. FROM {db_prefix}topics'
  665. ),
  666. 'check_query' => '
  667. SELECT t.id_poll, t.id_topic
  668. FROM {db_prefix}topics AS t
  669. LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
  670. WHERE t.id_poll != 0
  671. AND t.id_poll BETWEEN {STEP_LOW} AND {STEP_HIGH}
  672. AND p.id_poll IS NULL',
  673. 'fix_collect' => array(
  674. 'index' => 'id_poll',
  675. 'process' => create_function('$polls', '
  676. global $smcFunc;
  677. $smcFunc[\'db_query\'](\'\', "
  678. UPDATE {db_prefix}topics
  679. SET id_poll = 0
  680. WHERE id_poll IN ({array_int:polls})",
  681. array(
  682. \'polls\' => $polls
  683. )
  684. );
  685. '),
  686. ),
  687. 'messages' => array('repair_missing_polls', 'id_topic', 'id_poll'),
  688. ),
  689. 'missing_calendar_topics' => array(
  690. 'substeps' => array(
  691. 'step_size' => 1000,
  692. 'step_max' => '
  693. SELECT MAX(id_topic)
  694. FROM {db_prefix}calendar'
  695. ),
  696. 'check_query' => '
  697. SELECT cal.id_topic, cal.id_event
  698. FROM {db_prefix}calendar AS cal
  699. LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = cal.id_topic)
  700. WHERE cal.id_topic != 0
  701. AND cal.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  702. AND t.id_topic IS NULL
  703. ORDER BY cal.id_topic',
  704. 'fix_collect' => array(
  705. 'index' => 'id_topic',
  706. 'process' => create_function('$events', '
  707. global $smcFunc;
  708. $smcFunc[\'db_query\'](\'\', \'
  709. UPDATE {db_prefix}calendar
  710. SET id_topic = 0, id_board = 0
  711. WHERE id_topic IN ({array_int:events})\',
  712. array(
  713. \'events\' => $events
  714. )
  715. );
  716. '),
  717. ),
  718. 'messages' => array('repair_missing_calendar_topics', 'id_event', 'id_topic'),
  719. ),
  720. 'missing_log_topics' => array(
  721. 'substeps' => array(
  722. 'step_size' => 150,
  723. 'step_max' => '
  724. SELECT MAX(id_member)
  725. FROM {db_prefix}log_topics'
  726. ),
  727. 'check_query' => '
  728. SELECT lt.id_topic
  729. FROM {db_prefix}log_topics AS lt
  730. LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lt.id_topic)
  731. WHERE t.id_topic IS NULL
  732. AND lt.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}',
  733. 'fix_collect' => array(
  734. 'index' => 'id_topic',
  735. 'process' => create_function('$topics', '
  736. global $smcFunc;
  737. $smcFunc[\'db_query\'](\'\', "
  738. DELETE FROM {db_prefix}log_topics
  739. WHERE id_topic IN ({array_int:topics})",
  740. array(
  741. \'topics\' => $topics
  742. )
  743. );
  744. '),
  745. ),
  746. 'messages' => array('repair_missing_log_topics', 'id_topic'),
  747. ),
  748. 'missing_log_topics_members' => array(
  749. 'substeps' => array(
  750. 'step_size' => 150,
  751. 'step_max' => '
  752. SELECT MAX(id_member)
  753. FROM {db_prefix}log_topics'
  754. ),
  755. 'check_query' => '
  756. SELECT lt.id_member
  757. FROM {db_prefix}log_topics AS lt
  758. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lt.id_member)
  759. WHERE mem.id_member IS NULL
  760. AND lt.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  761. GROUP BY lt.id_member',
  762. 'fix_collect' => array(
  763. 'index' => 'id_member',
  764. 'process' => create_function('$members', '
  765. global $smcFunc;
  766. $smcFunc[\'db_query\'](\'\', "
  767. DELETE FROM {db_prefix}log_topics
  768. WHERE id_member IN ({array_int:members})",
  769. array(
  770. \'members\' => $members
  771. )
  772. );
  773. '),
  774. ),
  775. 'messages' => array('repair_missing_log_topics_members', 'id_member'),
  776. ),
  777. 'missing_log_boards' => array(
  778. 'substeps' => array(
  779. 'step_size' => 500,
  780. 'step_max' => '
  781. SELECT MAX(id_member)
  782. FROM {db_prefix}log_boards'
  783. ),
  784. 'check_query' => '
  785. SELECT lb.id_board
  786. FROM {db_prefix}log_boards AS lb
  787. LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lb.id_board)
  788. WHERE b.id_board IS NULL
  789. AND lb.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  790. GROUP BY lb.id_board',
  791. 'fix_collect' => array(
  792. 'index' => 'id_board',
  793. 'process' => create_function('$boards', '
  794. global $smcFunc;
  795. $smcFunc[\'db_query\'](\'\', "
  796. DELETE FROM {db_prefix}log_boards
  797. WHERE id_board IN ({array_int:boards})",
  798. array(
  799. \'boards\' => $boards
  800. )
  801. );
  802. '),
  803. ),
  804. 'messages' => array('repair_missing_log_boards', 'id_board'),
  805. ),
  806. 'missing_log_boards_members' => array(
  807. 'substeps' => array(
  808. 'step_size' => 500,
  809. 'step_max' => '
  810. SELECT MAX(id_member)
  811. FROM {db_prefix}log_boards'
  812. ),
  813. 'check_query' => '
  814. SELECT lb.id_member
  815. FROM {db_prefix}log_boards AS lb
  816. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lb.id_member)
  817. WHERE mem.id_member IS NULL
  818. AND lb.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  819. GROUP BY lb.id_member',
  820. 'fix_collect' => array(
  821. 'index' => 'id_member',
  822. 'process' => create_function('$members', '
  823. global $smcFunc;
  824. $smcFunc[\'db_query\'](\'\', "
  825. DELETE FROM {db_prefix}log_boards
  826. WHERE id_member IN ({array_int:members})",
  827. array(
  828. \'members\' => $members
  829. )
  830. );
  831. '),
  832. ),
  833. 'messages' => array('repair_missing_log_boards_members', 'id_member'),
  834. ),
  835. 'missing_log_mark_read' => array(
  836. 'substeps' => array(
  837. 'step_size' => 500,
  838. 'step_max' => '
  839. SELECT MAX(id_member)
  840. FROM {db_prefix}log_mark_read'
  841. ),
  842. 'check_query' => '
  843. SELECT lmr.id_board
  844. FROM {db_prefix}log_mark_read AS lmr
  845. LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lmr.id_board)
  846. WHERE b.id_board IS NULL
  847. AND lmr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  848. GROUP BY lmr.id_board',
  849. 'fix_collect' => array(
  850. 'index' => 'id_board',
  851. 'process' => create_function('$boards', '
  852. global $smcFunc;
  853. $smcFunc[\'db_query\'](\'\', "
  854. DELETE FROM {db_prefix}log_mark_read
  855. WHERE id_board IN ({array_int:boards})",
  856. array(
  857. \'boards\' => $boards
  858. )
  859. );
  860. '),
  861. ),
  862. 'messages' => array('repair_missing_log_mark_read', 'id_board'),
  863. ),
  864. 'missing_log_mark_read_members' => array(
  865. 'substeps' => array(
  866. 'step_size' => 500,
  867. 'step_max' => '
  868. SELECT MAX(id_member)
  869. FROM {db_prefix}log_mark_read'
  870. ),
  871. 'check_query' => '
  872. SELECT lmr.id_member
  873. FROM {db_prefix}log_mark_read AS lmr
  874. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lmr.id_member)
  875. WHERE mem.id_member IS NULL
  876. AND lmr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  877. GROUP BY lmr.id_member',
  878. 'fix_collect' => array(
  879. 'index' => 'id_member',
  880. 'process' => create_function('$members', '
  881. global $smcFunc;
  882. $smcFunc[\'db_query\'](\'\', "
  883. DELETE FROM {db_prefix}log_mark_read
  884. WHERE id_member IN ({array_int:members})",
  885. array(
  886. \'members\' => $members
  887. )
  888. );
  889. '),
  890. ),
  891. 'messages' => array('repair_missing_log_mark_read_members', 'id_member'),
  892. ),
  893. 'missing_pms' => array(
  894. 'substeps' => array(
  895. 'step_size' => 500,
  896. 'step_max' => '
  897. SELECT MAX(id_pm)
  898. FROM {db_prefix}pm_recipients'
  899. ),
  900. 'check_query' => '
  901. SELECT pmr.id_pm
  902. FROM {db_prefix}pm_recipients AS pmr
  903. LEFT JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)
  904. WHERE pm.id_pm IS NULL
  905. AND pmr.id_pm BETWEEN {STEP_LOW} AND {STEP_HIGH}
  906. GROUP BY pmr.id_pm',
  907. 'fix_collect' => array(
  908. 'index' => 'id_pm',
  909. 'process' => create_function('$pms', '
  910. global $smcFunc;
  911. $smcFunc[\'db_query\'](\'\', "
  912. DELETE FROM {db_prefix}pm_recipients
  913. WHERE id_pm IN ({array_int:pms})",
  914. array(
  915. \'pms\' => $pms
  916. )
  917. );
  918. '),
  919. ),
  920. 'messages' => array('repair_missing_pms', 'id_pm'),
  921. ),
  922. 'missing_recipients' => array(
  923. 'substeps' => array(
  924. 'step_size' => 500,
  925. 'step_max' => '
  926. SELECT MAX(id_member)
  927. FROM {db_prefix}pm_recipients'
  928. ),
  929. 'check_query' => '
  930. SELECT pmr.id_member
  931. FROM {db_prefix}pm_recipients AS pmr
  932. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pmr.id_member)
  933. WHERE pmr.id_member != 0
  934. AND pmr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  935. AND mem.id_member IS NULL
  936. GROUP BY pmr.id_member',
  937. 'fix_collect' => array(
  938. 'index' => 'id_member',
  939. 'process' => create_function('$members', '
  940. global $smcFunc;
  941. $smcFunc[\'db_query\'](\'\', "
  942. DELETE FROM {db_prefix}pm_recipients
  943. WHERE id_member IN ({array_int:members})",
  944. array(
  945. \'members\' => $members
  946. )
  947. );
  948. '),
  949. ),
  950. 'messages' => array('repair_missing_recipients', 'id_member'),
  951. ),
  952. 'missing_senders' => array(
  953. 'substeps' => array(
  954. 'step_size' => 500,
  955. 'step_max' => '
  956. SELECT MAX(id_pm)
  957. FROM {db_prefix}personal_messages'
  958. ),
  959. 'check_query' => '
  960. SELECT pm.id_pm, pm.id_member_from
  961. FROM {db_prefix}personal_messages AS pm
  962. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from)
  963. WHERE pm.id_member_from != 0
  964. AND pm.id_pm BETWEEN {STEP_LOW} AND {STEP_HIGH}
  965. AND mem.id_member IS NULL',
  966. 'fix_collect' => array(
  967. 'index' => 'id_pm',
  968. 'process' => create_function('$guestMessages', '
  969. global $smcFunc;
  970. $smcFunc[\'db_query\'](\'\', "
  971. UPDATE {db_prefix}personal_messages
  972. SET id_member_from = 0
  973. WHERE id_pm IN ({array_int:guestMessages})",
  974. array(
  975. \'guestMessages\' => $guestMessages
  976. ));
  977. '),
  978. ),
  979. 'messages' => array('repair_missing_senders', 'id_pm', 'id_member_from'),
  980. ),
  981. 'missing_notify_members' => array(
  982. 'substeps' => array(
  983. 'step_size' => 500,
  984. 'step_max' => '
  985. SELECT MAX(id_member)
  986. FROM {db_prefix}log_notify'
  987. ),
  988. 'check_query' => '
  989. SELECT ln.id_member
  990. FROM {db_prefix}log_notify AS ln
  991. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
  992. WHERE ln.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  993. AND mem.id_member IS NULL
  994. GROUP BY ln.id_member',
  995. 'fix_collect' => array(
  996. 'index' => 'id_member',
  997. 'process' => create_function('$members', '
  998. global $smcFunc;
  999. $smcFunc[\'db_query\'](\'\', "
  1000. DELETE FROM {db_prefix}log_notify
  1001. WHERE id_member IN ({array_int:members})",
  1002. array(
  1003. \'members\' => $members
  1004. )
  1005. );
  1006. '),
  1007. ),
  1008. 'messages' => array('repair_missing_notify_members', 'id_member'),
  1009. ),
  1010. 'missing_cached_subject' => array(
  1011. 'substeps' => array(
  1012. 'step_size' => 100,
  1013. 'step_max' => '
  1014. SELECT MAX(id_topic)
  1015. FROM {db_prefix}topics'
  1016. ),
  1017. 'check_query' => '
  1018. SELECT t.id_topic, fm.subject
  1019. FROM {db_prefix}topics AS t
  1020. INNER JOIN {db_prefix}messages AS fm ON (fm.id_msg = t.id_first_msg)
  1021. LEFT JOIN {db_prefix}log_search_subjects AS lss ON (lss.id_topic = t.id_topic)
  1022. WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1023. AND lss.id_topic IS NULL',
  1024. 'fix_full_processing' => create_function('$result', '
  1025. global $smcFunc;
  1026. $inserts = array();
  1027. while ($row = $smcFunc[\'db_fetch_assoc\']($result))
  1028. {
  1029. foreach (text2words($row[\'subject\']) as $word)
  1030. $inserts[] = array($word, $row[\'id_topic\']);
  1031. if (count($inserts) > 500)
  1032. {
  1033. $smcFunc[\'db_insert\'](\'ignore\',
  1034. "{db_prefix}log_search_subjects",
  1035. array(\'word\' => \'string\', \'id_topic\' => \'int\'),
  1036. $inserts,
  1037. array(\'word\', \'id_topic\')
  1038. );
  1039. $inserts = array();
  1040. }
  1041. }
  1042. if (!empty($inserts))
  1043. $smcFunc[\'db_insert\'](\'ignore\',
  1044. "{db_prefix}log_search_subjects",
  1045. array(\'word\' => \'string\', \'id_topic\' => \'int\'),
  1046. $inserts,
  1047. array(\'word\', \'id_topic\')
  1048. );
  1049. '),
  1050. 'message_function' => create_function('$row', '
  1051. global $txt, $context;
  1052. if (count(text2words($row[\'subject\'])) != 0)
  1053. {
  1054. $context[\'repair_errors\'][] = sprintf($txt[\'repair_missing_cached_subject\'], $row[\'id_topic\']);
  1055. return true;
  1056. }
  1057. return false;
  1058. '),
  1059. ),
  1060. 'missing_topic_for_cache' => array(
  1061. 'substeps' => array(
  1062. 'step_size' => 50,
  1063. 'step_max' => '
  1064. SELECT MAX(id_topic)
  1065. FROM {db_prefix}log_search_subjects'
  1066. ),
  1067. 'check_query' => '
  1068. SELECT lss.id_topic, lss.word
  1069. FROM {db_prefix}log_search_subjects AS lss
  1070. LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lss.id_topic)
  1071. WHERE lss.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1072. AND t.id_topic IS NULL',
  1073. 'fix_collect' => array(
  1074. 'index' => 'id_topic',
  1075. 'process' => create_function('$deleteTopics', '
  1076. global $smcFunc;
  1077. $smcFunc[\'db_query\'](\'\', "
  1078. DELETE FROM {db_prefix}log_search_subjects
  1079. WHERE id_topic IN ({array_int:deleteTopics})",
  1080. array(
  1081. \'deleteTopics\' => $deleteTopics
  1082. )
  1083. );
  1084. '),
  1085. ),
  1086. 'messages' => array('repair_missing_topic_for_cache', 'word'),
  1087. ),
  1088. 'missing_member_vote' => array(
  1089. 'substeps' => array(
  1090. 'step_size' => 500,
  1091. 'step_max' => '
  1092. SELECT MAX(id_member)
  1093. FROM {db_prefix}log_polls'
  1094. ),
  1095. 'check_query' => '
  1096. SELECT lp.id_poll, lp.id_member
  1097. FROM {db_prefix}log_polls AS lp
  1098. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lp.id_member)
  1099. WHERE lp.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1100. AND lp.id_member > 0
  1101. AND mem.id_member IS NULL',
  1102. 'fix_collect' => array(
  1103. 'index' => 'id_member',
  1104. 'process' => create_function('$members', '
  1105. global $smcFunc;
  1106. $smcFunc[\'db_query\'](\'\', "
  1107. DELETE FROM {db_prefix}log_polls
  1108. WHERE id_member IN ({array_int:members})",
  1109. array(
  1110. \'members\' => $members
  1111. )
  1112. );
  1113. '),
  1114. ),
  1115. 'messages' => array('repair_missing_log_poll_member', 'id_poll', 'id_member'),
  1116. ),
  1117. 'missing_log_poll_vote' => array(
  1118. 'substeps' => array(
  1119. 'step_size' => 500,
  1120. 'step_max' => '
  1121. SELECT MAX(id_poll)
  1122. FROM {db_prefix}log_polls'
  1123. ),
  1124. 'check_query' => '
  1125. SELECT lp.id_poll, lp.id_member
  1126. FROM {db_prefix}log_polls AS lp
  1127. LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = lp.id_poll)
  1128. WHERE lp.id_poll BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1129. AND p.id_poll IS NULL',
  1130. 'fix_collect' => array(
  1131. 'index' => 'id_poll',
  1132. 'process' => create_function('$polls', '
  1133. global $smcFunc;
  1134. $smcFunc[\'db_query\'](\'\', "
  1135. DELETE FROM {db_prefix}log_polls
  1136. WHERE id_poll IN ({array_int:polls})",
  1137. array(
  1138. \'polls\' => $polls
  1139. )
  1140. );
  1141. '),
  1142. ),
  1143. 'messages' => array('repair_missing_log_poll_vote', 'id_member', 'id_poll'),
  1144. ),
  1145. 'report_missing_comments' => array(
  1146. 'substeps' => array(
  1147. 'step_size' => 500,
  1148. 'step_max' => '
  1149. SELECT MAX(id_report)
  1150. FROM {db_prefix}log_reported'
  1151. ),
  1152. 'check_query' => '
  1153. SELECT lr.id_report, lr.subject
  1154. FROM {db_prefix}log_reported AS lr
  1155. LEFT JOIN {db_prefix}log_reported_comments AS lrc ON (lrc.id_report = lr.id_report)
  1156. WHERE lr.id_report BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1157. AND lrc.id_report IS NULL',
  1158. 'fix_collect' => array(
  1159. 'index' => 'id_report',
  1160. 'process' => create_function('$reports', '
  1161. global $smcFunc;
  1162. $smcFunc[\'db_query\'](\'\', "
  1163. DELETE FROM {db_prefix}log_reported
  1164. WHERE id_report IN ({array_int:reports})",
  1165. array(
  1166. \'reports\' => $reports
  1167. )
  1168. );
  1169. '),
  1170. ),
  1171. 'messages' => array('repair_report_missing_comments', 'id_report', 'subject'),
  1172. ),
  1173. 'comments_missing_report' => array(
  1174. 'substeps' => array(
  1175. 'step_size' => 200,
  1176. 'step_max' => '
  1177. SELECT MAX(id_report)
  1178. FROM {db_prefix}log_reported_comments'
  1179. ),
  1180. 'check_query' => '
  1181. SELECT lrc.id_report, lrc.membername
  1182. FROM {db_prefix}log_reported_comments AS lrc
  1183. LEFT JOIN {db_prefix}log_reported AS lr ON (lr.id_report = lrc.id_report)
  1184. WHERE lrc.id_report BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1185. AND lr.id_report IS NULL',
  1186. 'fix_collect' => array(
  1187. 'index' => 'id_report',
  1188. 'process' => create_function('$reports', '
  1189. global $smcFunc;
  1190. $smcFunc[\'db_query\'](\'\', "
  1191. DELETE FROM {db_prefix}log_reported_comments
  1192. WHERE id_report IN ({array_int:reports})",
  1193. array(
  1194. \'reports\' => $reports
  1195. )
  1196. );
  1197. '),
  1198. ),
  1199. 'messages' => array('repair_comments_missing_report', 'id_report', 'membername'),
  1200. ),
  1201. 'group_request_missing_member' => array(
  1202. 'substeps' => array(
  1203. 'step_size' => 200,
  1204. 'step_max' => '
  1205. SELECT MAX(id_member)
  1206. FROM {db_prefix}log_group_requests'
  1207. ),
  1208. 'check_query' => '
  1209. SELECT lgr.id_member
  1210. FROM {db_prefix}log_group_requests AS lgr
  1211. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lgr.id_member)
  1212. WHERE lgr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1213. AND mem.id_member IS NULL
  1214. GROUP BY lgr.id_member',
  1215. 'fix_collect' => array(
  1216. 'index' => 'id_member',
  1217. 'process' => create_function('$members', '
  1218. global $smcFunc;
  1219. $smcFunc[\'db_query\'](\'\', "
  1220. DELETE FROM {db_prefix}log_group_requests
  1221. WHERE id_member IN ({array_int:members})",
  1222. array(
  1223. \'members\' => $members
  1224. )
  1225. );
  1226. '),
  1227. ),
  1228. 'messages' => array('repair_group_request_missing_member', 'id_member'),
  1229. ),
  1230. 'group_request_missing_group' => array(
  1231. 'substeps' => array(
  1232. 'step_size' => 200,
  1233. 'step_max' => '
  1234. SELECT MAX(id_group)
  1235. FROM {db_prefix}log_group_requests'
  1236. ),
  1237. 'check_query' => '
  1238. SELECT lgr.id_group
  1239. FROM {db_prefix}log_group_requests AS lgr
  1240. LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = lgr.id_group)
  1241. WHERE lgr.id_group BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1242. AND mg.id_group IS NULL
  1243. GROUP BY lgr.id_group',
  1244. 'fix_collect' => array(
  1245. 'index' => 'id_group',
  1246. 'process' => create_function('$groups', '
  1247. global $smcFunc;
  1248. $smcFunc[\'db_query\'](\'\', \'
  1249. DELETE FROM {db_prefix}log_group_requests
  1250. WHERE id_group IN ({array_int:groups})\',
  1251. array(
  1252. \'groups\' => $groups
  1253. )
  1254. );
  1255. '),
  1256. ),
  1257. 'messages' => array('repair_group_request_missing_group', 'id_group'),
  1258. ),
  1259. );
  1260. }
  1261. function findForumErrors($do_fix = false)
  1262. {
  1263. global $context, $txt, $smcFunc, $errorTests, $db_cache, $db_temp_cache;
  1264. // This may take some time...
  1265. @set_time_limit(600);
  1266. $to_fix = !empty($_SESSION['repairboards_to_fix']) ? $_SESSION['repairboards_to_fix'] : array();
  1267. $context['repair_errors'] = isset($_SESSION['repairboards_to_fix2']) ? $_SESSION['repairboards_to_fix2'] : array();
  1268. $_GET['step'] = empty($_GET['step']) ? 0 : (int) $_GET['step'];
  1269. $_GET['substep'] = empty($_GET['substep']) ? 0 : (int) $_GET['substep'];
  1270. // Don't allow the cache to get too full.
  1271. $db_temp_cache = $db_cache;
  1272. $db_cache = '';
  1273. $context['total_steps'] = count($errorTests);
  1274. // For all the defined error types do the necessary tests.
  1275. $current_step = -1;
  1276. $total_queries = 0;
  1277. foreach ($errorTests as $error_type => $test)
  1278. {
  1279. $current_step++;
  1280. // Already done this?
  1281. if ($_GET['step'] > $current_step)
  1282. continue;
  1283. // If we're fixing it but it ain't broke why try?
  1284. if ($do_fix && !in_array($error_type, $to_fix))
  1285. {
  1286. $_GET['step']++;
  1287. continue;
  1288. }
  1289. // Has it got substeps?
  1290. if (isset($test['substeps']))
  1291. {
  1292. $step_size = isset($test['substeps']['step_size']) ? $test['substeps']['step_size'] : 100;
  1293. $request = $smcFunc['db_query']('',
  1294. $test['substeps']['step_max'],
  1295. array(
  1296. )
  1297. );
  1298. list ($step_max) = $smcFunc['db_fetch_row']($request);
  1299. $total_queries++;
  1300. $smcFunc['db_free_result']($request);
  1301. }
  1302. // We in theory keep doing this... the substeps.
  1303. $done = false;
  1304. while (!$done)
  1305. {
  1306. // Make sure there's at least one ID to test.
  1307. if (isset($test['substeps']) && empty($step_max))
  1308. break;
  1309. // What is the testing query (Changes if we are testing or fixing)
  1310. if (!$do_fix)
  1311. $test_query = 'check_query';
  1312. else
  1313. $test_query = isset($test['fix_query']) ? 'fix_query' : 'check_query';
  1314. // Do the test...
  1315. $request = $smcFunc['db_query']('',
  1316. isset($test['substeps']) ? strtr($test[$test_query], array('{STEP_LOW}' => $_GET['substep'], '{STEP_HIGH}' => $_GET['substep'] + $step_size - 1)) : $test[$test_query],
  1317. array(
  1318. )
  1319. );
  1320. $needs_fix = false;
  1321. // Does it need a fix?
  1322. if (!empty($test['check_type']) && $test['check_type'] == 'count')
  1323. list ($needs_fix) = $smcFunc['db_fetch_row']($request);
  1324. else
  1325. $needs_fix = $smcFunc['db_num_rows']($request);
  1326. $total_queries++;
  1327. if ($needs_fix)
  1328. {
  1329. // What about a message to the user?
  1330. if (!$do_fix)
  1331. {
  1332. // Assume need to fix.
  1333. $found_errors = true;
  1334. if (isset($test['message']))
  1335. $context['repair_errors'][] = $txt[$test['message']];
  1336. // One per row!
  1337. elseif (isset($test['messages']))
  1338. {
  1339. while ($row = $smcFunc['db_fetch_assoc']($request))
  1340. {
  1341. $variables = $test['messages'];
  1342. foreach ($variables as $k => $v)
  1343. {
  1344. if ($k == 0 && isset($txt[$v]))
  1345. $variables[$k] = $txt[$v];
  1346. elseif ($k > 0 && isset($row[$v]))
  1347. $variables[$k] = $row[$v];
  1348. }
  1349. $context['repair_errors'][] = call_user_func_array('sprintf', $variables);
  1350. }
  1351. }
  1352. // A function to process?
  1353. elseif (isset($test['message_function']))
  1354. {
  1355. // Find out if there are actually errors.
  1356. $found_errors = false;
  1357. while ($row = $smcFunc['db_fetch_assoc']($request))
  1358. $found_errors |= $test['message_function']($row);
  1359. }
  1360. // Actually have something to fix?
  1361. if ($found_errors)
  1362. $to_fix[] = $error_type;
  1363. }
  1364. // We want to fix, we need to fix - so work out what exactly to do!
  1365. else
  1366. {
  1367. // Are we simply getting a collection of ids?
  1368. if (isset($test['fix_collect']))
  1369. {
  1370. $ids = array();
  1371. while ($row = $smcFunc['db_fetch_assoc']($request))
  1372. $ids[] = $row[$test['fix_collect']['index']];
  1373. if (!empty($ids))
  1374. {
  1375. // Fix it!
  1376. $test['fix_collect']['process']($ids);
  1377. }
  1378. }
  1379. // Simply executing a fix it query?
  1380. elseif (isset($test['fix_it_query']))
  1381. $smcFunc['db_query']('',
  1382. $test['fix_it_query'],
  1383. array(
  1384. )
  1385. );
  1386. // Do we have some processing to do?
  1387. elseif (isset($test['fix_processing']))
  1388. {
  1389. while ($row = $smcFunc['db_fetch_assoc']($request))
  1390. $test['fix_processing']($row);
  1391. }
  1392. // What about the full set of processing?
  1393. elseif (isset($test['fix_full_processing']))
  1394. $test['fix_full_processing']($request);
  1395. // Do we have other things we need to fix as a result?
  1396. if (!empty($test['force_fix']))
  1397. {
  1398. foreach ($test['force_fix'] as $item)
  1399. if (!in_array($item, $to_fix))
  1400. $to_fix[] = $item;
  1401. }
  1402. }
  1403. }
  1404. // Free the result.
  1405. $smcFunc['db_free_result']($request);
  1406. // Keep memory down.
  1407. $db_cache = '';
  1408. // Are we done yet?
  1409. if (isset($test['substeps']))
  1410. {
  1411. $_GET['substep'] += $step_size;
  1412. // Not done?
  1413. if ($_GET['substep'] <= $step_max)
  1414. {
  1415. pauseRepairProcess($to_fix, $error_type, $step_max);
  1416. }
  1417. else
  1418. $done = true;
  1419. }
  1420. else
  1421. $done = true;
  1422. // Don't allow more than 1000 queries at a time.
  1423. if ($total_queries >= 1000)
  1424. pauseRepairProcess($to_fix, $error_type, $step_max, true);
  1425. }
  1426. // Keep going.
  1427. $_GET['step']++;
  1428. $_GET['substep'] = 0;
  1429. $to_fix = array_unique($to_fix);
  1430. // If we're doing fixes and this needed a fix and we're all done then don't do it again.
  1431. if ($do_fix)
  1432. {
  1433. $key = array_search($error_type, $to_fix);
  1434. if ($key !== false && isset($to_fix[$key]))
  1435. unset($to_fix[$key]);
  1436. }
  1437. // Are we done?
  1438. pauseRepairProcess($to_fix, $error_type);
  1439. }
  1440. // Restore the cache.
  1441. $db_cache = $db_temp_cache;
  1442. return $to_fix;
  1443. }
  1444. // Create a salvage area for repair purposes.
  1445. function createSalvageArea()
  1446. {
  1447. global $txt, $language, $salvageBoardID, $salvageCatID, $smcFunc;
  1448. static $createOnce = false;
  1449. // Have we already created it?
  1450. if ($createOnce)
  1451. return;
  1452. else
  1453. $createOnce = true;
  1454. // Back to the forum's default language.
  1455. loadLanguage('Admin', $language);
  1456. // Check to see if a 'Salvage Category' exists, if not => insert one.
  1457. $result = $smcFunc['db_query']('', '
  1458. SELECT id_cat
  1459. FROM {db_prefix}categories
  1460. WHERE name = {string:cat_name}
  1461. LIMIT 1',
  1462. array(
  1463. 'cat_name' => $txt['salvaged_category_name'],
  1464. )
  1465. );
  1466. if ($smcFunc['db_num_rows']($result) != 0)
  1467. list ($salvageCatID) = $smcFunc['db_fetch_row']($result);
  1468. $smcFunc['db_free_result']($result);
  1469. if (empty($salveageCatID))
  1470. {
  1471. $smcFunc['db_insert']('',
  1472. '{db_prefix}categories',
  1473. array('name' => 'string-255', 'cat_order' => 'int'),
  1474. array($txt['salvaged_category_name'], -1),
  1475. array('id_cat')
  1476. );
  1477. if ($smcFunc['db_affected_rows']() <= 0)
  1478. {
  1479. loadLanguage('Admin');
  1480. fatal_lang_error('salvaged_category_error', false);
  1481. }
  1482. $salvageCatID = $smcFunc['db_insert_id']('{db_prefix}categories', 'id_cat');
  1483. }
  1484. // Check to see if a 'Salvage Board' exists, if not => insert one.
  1485. $result = $smcFunc['db_query']('', '
  1486. SELECT id_board
  1487. FROM {db_prefix}boards
  1488. WHERE id_cat = {int:id_cat}
  1489. AND name = {string:board_name}
  1490. LIMIT 1',
  1491. array(
  1492. 'id_cat' => $salvageCatID,
  1493. 'board_name' => $txt['salvaged_board_name'],
  1494. )
  1495. );
  1496. if ($smcFunc['db_num_rows']($result) != 0)
  1497. list ($salvageBoardID) = $smcFunc['db_fetch_row']($result);
  1498. $smcFunc['db_free_result']($result);
  1499. if (empty($salvageBoardID))
  1500. {
  1501. $smcFunc['db_insert']('',
  1502. '{db_prefix}boards',
  1503. array('name' => 'string-255', 'description' => 'string-255', 'id_cat' => 'int', 'member_groups' => 'string', 'board_order' => 'int', 'redirect' => 'string'),
  1504. array($txt['salvaged_board_name'], $txt['salvaged_board_description'], $salvageCatID, '1', -1, ''),
  1505. array('id_board')
  1506. );
  1507. if ($smcFunc['db_affected_rows']() <= 0)
  1508. {
  1509. loadLanguage('Admin');
  1510. fatal_lang_error('salvaged_board_error', false);
  1511. }
  1512. $salvageBoardID = $smcFunc['db_insert_id']('{db_prefix}boards', 'id_board');
  1513. }
  1514. $smcFunc['db_query']('alter_table_boards', '
  1515. ALTER TABLE {db_prefix}boards
  1516. ORDER BY board_order',
  1517. array(
  1518. )
  1519. );
  1520. // Restore the user's language.
  1521. loadLanguage('Admin');
  1522. }
  1523. ?>