RepairBoards.php 50 KB

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