PageRenderTime 58ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/sources/admin/RepairBoards.php

https://github.com/Arantor/Elkarte
PHP | 1672 lines | 1460 code | 79 blank | 133 comment | 55 complexity | 602f68f7b12ffa69fe33f9a1ecc40fc2 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file