RepairBoards.php 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668
  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 2012 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. setMemoryLimit('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 = {int: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. \'newTopicID\' => $newTopicID,
  258. )
  259. );
  260. '),
  261. 'force_fix' => array('stats_topics'),
  262. 'messages' => array('repair_missing_topics', 'id_msg', 'id_topic'),
  263. ),
  264. // Find topics with no messages.
  265. 'missing_messages' => array(
  266. 'substeps' => array(
  267. 'step_size' => 1000,
  268. 'step_max' => '
  269. SELECT MAX(id_topic)
  270. FROM {db_prefix}topics'
  271. ),
  272. 'check_query' => '
  273. SELECT t.id_topic, COUNT(m.id_msg) AS num_msg
  274. FROM {db_prefix}topics AS t
  275. LEFT JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic)
  276. WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  277. GROUP BY t.id_topic
  278. HAVING COUNT(m.id_msg) = 0',
  279. // Remove all topics that have zero messages in the messages table.
  280. 'fix_collect' => array(
  281. 'index' => 'id_topic',
  282. 'process' => create_function('$topics', '
  283. global $smcFunc;
  284. $smcFunc[\'db_query\'](\'\', \'
  285. DELETE FROM {db_prefix}topics
  286. WHERE id_topic IN ({array_int:topics})\',
  287. array(
  288. \'topics\' => $topics,
  289. )
  290. );
  291. $smcFunc[\'db_query\'](\'\', "
  292. DELETE FROM {db_prefix}log_topics
  293. WHERE id_topic IN ({array_int:topics})",
  294. array(
  295. \'topics\' => $topics,
  296. )
  297. );
  298. '),
  299. ),
  300. 'messages' => array('repair_missing_messages', 'id_topic'),
  301. ),
  302. 'polls_missing_topics' => array(
  303. 'substeps' => array(
  304. 'step_size' => 500,
  305. 'step_max' => '
  306. SELECT MAX(id_poll)
  307. FROM {db_prefix}polls'
  308. ),
  309. 'check_query' => '
  310. SELECT p.id_poll, p.id_member, p.poster_name, t.id_board
  311. FROM {db_prefix}polls AS p
  312. LEFT JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll)
  313. WHERE p.id_poll BETWEEN {STEP_LOW} AND {STEP_HIGH}
  314. AND t.id_poll IS NULL',
  315. 'fix_processing' => create_function('$row', '
  316. global $smcFunc, $salvageBoardID, $txt;
  317. // Only if we don\'t have a reasonable idea of where to put it.
  318. if ($row[\'id_board\'] == 0)
  319. {
  320. createSalvageArea();
  321. $row[\'id_board\'] = (int) $salvageBoardID;
  322. }
  323. $row[\'poster_name\'] = !empty($row[\'poster_name\']) ? $row[\'poster_name\'] : $txt[\'guest\'];
  324. $smcFunc[\'db_insert\'](\'\',
  325. \'{db_prefix}messages\',
  326. array(
  327. \'id_board\' => \'int\',
  328. \'id_topic\' => \'int\',
  329. \'poster_time\' => \'int\',
  330. \'id_member\' => \'int\',
  331. \'subject\' => \'string-255\',
  332. \'poster_name\' => \'string-255\',
  333. \'poster_email\' => \'string-255\',
  334. \'poster_ip\' => \'string-16\',
  335. \'smileys_enabled\' => \'int\',
  336. \'body\' => \'string-65534\',
  337. \'icon\' => \'string-16\',
  338. \'approved\' => \'int\',
  339. ),
  340. array(
  341. $row[\'id_board\'],
  342. 0,
  343. time(),
  344. $row[\'id_member\'],
  345. $txt[\'salvaged_poll_topic_name\'],
  346. $row[\'poster_name\'],
  347. \'\',
  348. \'127.0.0.1\',
  349. 1,
  350. $txt[\'salvaged_poll_message_body\'],
  351. \'xx\',
  352. 1,
  353. ),
  354. array(\'id_topic\')
  355. );
  356. $newMessageID = $smcFunc[\'db_insert_id\']("{db_prefix}messages", \'id_msg\');
  357. $smcFunc[\'db_insert\'](\'\',
  358. \'{db_prefix}topics\',
  359. array(
  360. \'id_board\' => \'int\',
  361. \'id_poll\' => \'int\',
  362. \'id_member_started\' => \'int\',
  363. \'id_member_updated\' => \'int\',
  364. \'id_first_msg\' => \'int\',
  365. \'id_last_msg\' => \'int\',
  366. \'num_replies\' => \'int\',
  367. ),
  368. array(
  369. $row[\'id_board\'],
  370. $row[\'id_poll\'],
  371. $row[\'id_member\'],
  372. $row[\'id_member\'],
  373. $newMessageID,
  374. $newMessageID,
  375. 0,
  376. ),
  377. array(\'id_topic\')
  378. );
  379. $newTopicID = $smcFunc[\'db_insert_id\'](\'{db_prefix}topics\', \'id_topic\');
  380. $smcFunc[\'db_query\'](\'\', \'
  381. UPDATE {db_prefix}messages
  382. SET id_topic = {int:newTopicID}, id_board = {int:id_board}
  383. WHERE id_msg = $newMessageID\',
  384. array(
  385. \'id_board\' => $row[\'id_board\'],
  386. \'newTopicID\' => $newTopicID,
  387. )
  388. );
  389. updateStats(\'subject\', $newTopicID, $txt[\'salvaged_poll_topic_name\']);
  390. '),
  391. 'force_fix' => array('stats_topics'),
  392. 'messages' => array('repair_polls_missing_topics', 'id_poll', 'id_topic'),
  393. ),
  394. 'stats_topics' => array(
  395. 'substeps' => array(
  396. 'step_size' => 200,
  397. 'step_max' => '
  398. SELECT MAX(id_topic)
  399. FROM {db_prefix}topics'
  400. ),
  401. 'check_query' => '
  402. SELECT
  403. t.id_topic, t.id_first_msg, t.id_last_msg,
  404. CASE WHEN MIN(ma.id_msg) > 0 THEN
  405. CASE WHEN MIN(mu.id_msg) > 0 THEN
  406. CASE WHEN MIN(mu.id_msg) < MIN(ma.id_msg) THEN MIN(mu.id_msg) ELSE MIN(ma.id_msg) END ELSE
  407. MIN(ma.id_msg) END ELSE
  408. MIN(mu.id_msg) END AS myid_first_msg,
  409. CASE WHEN MAX(ma.id_msg) > 0 THEN MAX(ma.id_msg) ELSE MIN(mu.id_msg) END AS myid_last_msg,
  410. t.approved, mf.approved, mf.approved AS firstmsg_approved
  411. FROM {db_prefix}topics AS t
  412. LEFT JOIN {db_prefix}messages AS ma ON (ma.id_topic = t.id_topic AND ma.approved = 1)
  413. LEFT JOIN {db_prefix}messages AS mu ON (mu.id_topic = t.id_topic AND mu.approved = 0)
  414. LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
  415. WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  416. GROUP BY t.id_topic, t.id_first_msg, t.id_last_msg, t.approved, mf.approved
  417. ORDER BY t.id_topic',
  418. 'fix_processing' => create_function('$row', '
  419. global $smcFunc;
  420. $row[\'firstmsg_approved\'] = (int) $row[\'firstmsg_approved\'];
  421. $row[\'myid_first_msg\'] = (int) $row[\'myid_first_msg\'];
  422. $row[\'myid_last_msg\'] = (int) $row[\'myid_last_msg\'];
  423. // Not really a problem?
  424. if ($row[\'myid_first_msg\'] == $row[\'myid_first_msg\'] && $row[\'myid_first_msg\'] == $row[\'myid_first_msg\'] && $row[\'approved\'] == $row[\'firstmsg_approved\'])
  425. return false;
  426. $memberStartedID = (int) getMsgMemberID($row[\'myid_first_msg\']);
  427. $memberUpdatedID = (int) getMsgMemberID($row[\'myid_last_msg\']);
  428. $smcFunc[\'db_query\'](\'\', \'
  429. UPDATE {db_prefix}topics
  430. SET id_first_msg = {int:myid_first_msg},
  431. id_member_started = {int:memberStartedID}, id_last_msg = {int:myid_last_msg},
  432. id_member_updated = {int:memberUpdatedID}, approved = {int:firstmsg_approved}
  433. WHERE id_topic = {int:topic_id}\',
  434. array(
  435. \'myid_first_msg\' => $row[\'myid_first_msg\'],
  436. \'memberStartedID\' => $memberStartedID,
  437. \'myid_last_msg\' => $row[\'myid_last_msg\'],
  438. \'memberUpdatedID\' => $memberUpdatedID,
  439. \'firstmsg_approved\' => $row[\'firstmsg_approved\'],
  440. \'topic_id\' => $row[\'id_topic\'],
  441. )
  442. );
  443. '),
  444. 'message_function' => create_function('$row', '
  445. global $txt, $context;
  446. // A pretend error?
  447. if ($row[\'myid_first_msg\'] == $row[\'myid_first_msg\'] && $row[\'myid_first_msg\'] == $row[\'myid_first_msg\'] && $row[\'approved\'] == $row[\'firstmsg_approved\'])
  448. return false;
  449. if ($row[\'id_first_msg\'] != $row[\'myid_first_msg\'])
  450. $context[\'repair_errors\'][] = sprintf($txt[\'repair_stats_topics_1\'], $row[\'id_topic\'], $row[\'id_first_msg\']);
  451. if ($row[\'id_last_msg\'] != $row[\'myid_last_msg\'])
  452. $context[\'repair_errors\'][] = sprintf($txt[\'repair_stats_topics_2\'], $row[\'id_topic\'], $row[\'id_last_msg\']);
  453. if ($row[\'approved\'] != $row[\'firstmsg_approved\'])
  454. $context[\'repair_errors\'][] = sprintf($txt[\'repair_stats_topics_5\'], $row[\'id_topic\']);
  455. return true;
  456. '),
  457. ),
  458. // Find topics with incorrect num_replies.
  459. 'stats_topics2' => array(
  460. 'substeps' => array(
  461. 'step_size' => 300,
  462. 'step_max' => '
  463. SELECT MAX(id_topic)
  464. FROM {db_prefix}topics'
  465. ),
  466. 'check_query' => '
  467. SELECT
  468. t.id_topic, t.num_replies, mf.approved,
  469. 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
  470. FROM {db_prefix}topics AS t
  471. LEFT JOIN {db_prefix}messages AS ma ON (ma.id_topic = t.id_topic AND ma.approved = 1)
  472. LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
  473. WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  474. GROUP BY t.id_topic, t.num_replies, mf.approved
  475. ORDER BY t.id_topic',
  476. 'fix_processing' => create_function('$row', '
  477. global $smcFunc;
  478. $row[\'my_num_replies\'] = (int) $row[\'my_num_replies\'];
  479. // Not really a problem?
  480. if ($row[\'my_num_replies\'] == $row[\'num_replies\'])
  481. return false;
  482. $smcFunc[\'db_query\'](\'\', \'
  483. UPDATE {db_prefix}topics
  484. SET num_replies = {int:my_num_replies}
  485. WHERE id_topic = {int:topic_id}\',
  486. array(
  487. \'my_num_replies\' => $row[\'my_num_replies\'],
  488. \'topic_id\' => $row[\'id_topic\'],
  489. )
  490. );
  491. '),
  492. 'message_function' => create_function('$row', '
  493. global $txt, $context;
  494. // Just joking?
  495. if ($row[\'my_num_replies\'] == $row[\'num_replies\'])
  496. return false;
  497. if ($row[\'num_replies\'] != $row[\'my_num_replies\'])
  498. $context[\'repair_errors\'][] = sprintf($txt[\'repair_stats_topics_3\'], $row[\'id_topic\'], $row[\'num_replies\']);
  499. return true;
  500. '),
  501. ),
  502. // Find topics with incorrect unapproved_posts.
  503. 'stats_topics3' => array(
  504. 'substeps' => array(
  505. 'step_size' => 1000,
  506. 'step_max' => '
  507. SELECT MAX(id_topic)
  508. FROM {db_prefix}topics'
  509. ),
  510. 'check_query' => '
  511. SELECT
  512. t.id_topic, t.unapproved_posts, COUNT(mu.id_msg) AS my_unapproved_posts
  513. FROM {db_prefix}topics AS t
  514. LEFT JOIN {db_prefix}messages AS mu ON (mu.id_topic = t.id_topic AND mu.approved = 0)
  515. WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  516. GROUP BY t.id_topic, t.unapproved_posts
  517. HAVING unapproved_posts != COUNT(mu.id_msg)
  518. ORDER BY t.id_topic',
  519. 'fix_processing' => create_function('$row', '
  520. global $smcFunc;
  521. $row[\'my_unapproved_posts\'] = (int) $row[\'my_unapproved_posts\'];
  522. $smcFunc[\'db_query\'](\'\', \'
  523. UPDATE {db_prefix}topics
  524. SET unapproved_posts = {int:my_unapproved_posts}
  525. WHERE id_topic = {int:topic_id}\',
  526. array(
  527. \'my_unapproved_posts\' => $row[\'my_unapproved_posts\'],
  528. \'topic_id\' => $row[\'id_topic\'],
  529. )
  530. );
  531. '),
  532. 'messages' => array('repair_stats_topics_4', 'id_topic', 'unapproved_posts'),
  533. ),
  534. // Find topics with nonexistent boards.
  535. 'missing_boards' => array(
  536. 'substeps' => array(
  537. 'step_size' => 1000,
  538. 'step_max' => '
  539. SELECT MAX(id_topic)
  540. FROM {db_prefix}topics'
  541. ),
  542. 'check_query' => '
  543. SELECT t.id_topic, t.id_board
  544. FROM {db_prefix}topics AS t
  545. LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
  546. WHERE b.id_board IS NULL
  547. AND t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  548. ORDER BY t.id_board, t.id_topic',
  549. 'fix_query' => '
  550. SELECT t.id_board, COUNT(*) AS my_num_topics, COUNT(m.id_msg) AS my_num_posts
  551. FROM {db_prefix}topics AS t
  552. LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
  553. LEFT JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic)
  554. WHERE b.id_board IS NULL
  555. AND t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  556. GROUP BY t.id_board',
  557. 'fix_processing' => create_function('$row', '
  558. global $smcFunc, $salvageCatID;
  559. createSalvageArea();
  560. $row[\'my_num_topics\'] = (int) $row[\'my_num_topics\'];
  561. $row[\'my_num_posts\'] = (int) $row[\'my_num_posts\'];
  562. $smcFunc[\'db_insert\'](\'\',
  563. \'{db_prefix}boards\',
  564. array(\'id_cat\' => \'int\', \'name\' => \'string\', \'description\' => \'string\', \'num_topics\' => \'int\', \'num_posts\' => \'int\', \'member_groups\' => \'string\'),
  565. array($salvageCatID, \'Salvaged board\', \'\', $row[\'my_num_topics\'], $row[\'my_num_posts\'], \'1\'),
  566. array(\'id_board\')
  567. );
  568. $newBoardID = $smcFunc[\'db_insert_id\'](\'{db_prefix}boards\', \'id_board\');
  569. $smcFunc[\'db_query\'](\'\', \'
  570. UPDATE {db_prefix}topics
  571. SET id_board = {int:newBoardID}
  572. WHERE id_board = {int:board_id}\',
  573. array(
  574. \'newBoardID\' => $newBoardID,
  575. \'board_id\' => $row[\'id_board\'],
  576. )
  577. );
  578. $smcFunc[\'db_query\'](\'\', \'
  579. UPDATE {db_prefix}messages
  580. SET id_board = {int:newBoardID}
  581. WHERE id_board = {int:board_id}\',
  582. array(
  583. \'newBoardID\' => $newBoardID,
  584. \'board_id\' => $row[\'id_board\'],
  585. )
  586. );
  587. '),
  588. 'messages' => array('repair_missing_boards', 'id_topic', 'id_board'),
  589. ),
  590. // Find boards with nonexistent categories.
  591. 'missing_categories' => array(
  592. 'check_query' => '
  593. SELECT b.id_board, b.id_cat
  594. FROM {db_prefix}boards AS b
  595. LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
  596. WHERE c.id_cat IS NULL
  597. ORDER BY b.id_cat, b.id_board',
  598. 'fix_collect' => array(
  599. 'index' => 'id_cat',
  600. 'process' => create_function('$cats', '
  601. global $smcFunc, $salvageCatID;
  602. createSalvageArea();
  603. $smcFunc[\'db_query\'](\'\', \'
  604. UPDATE {db_prefix}boards
  605. SET id_cat = {int:salvageCatID}
  606. WHERE id_cat IN ({array_int:categories})\',
  607. array(
  608. \'salvageCatID\' => $salvageCatID,
  609. \'categories\' => $cats,
  610. )
  611. );
  612. '),
  613. ),
  614. 'messages' => array('repair_missing_categories', 'id_board', 'id_cat'),
  615. ),
  616. // Find messages with nonexistent members.
  617. 'missing_posters' => array(
  618. 'substeps' => array(
  619. 'step_size' => 2000,
  620. 'step_max' => '
  621. SELECT MAX(id_msg)
  622. FROM {db_prefix}messages'
  623. ),
  624. 'check_query' => '
  625. SELECT m.id_msg, m.id_member
  626. FROM {db_prefix}messages AS m
  627. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
  628. WHERE mem.id_member IS NULL
  629. AND m.id_member != 0
  630. AND m.id_msg BETWEEN {STEP_LOW} AND {STEP_HIGH}
  631. ORDER BY m.id_msg',
  632. // Last step-make sure all non-guest posters still exist.
  633. 'fix_collect' => array(
  634. 'index' => 'id_msg',
  635. 'process' => create_function('$msgs', '
  636. global $smcFunc;
  637. $smcFunc[\'db_query\'](\'\', \'
  638. UPDATE {db_prefix}messages
  639. SET id_member = {int:guest_id}
  640. WHERE id_msg IN ({array_int:msgs})\',
  641. array(
  642. \'msgs\' => $msgs,
  643. \'guest_id\' => 0,
  644. )
  645. );
  646. '),
  647. ),
  648. 'messages' => array('repair_missing_posters', 'id_msg', 'id_member'),
  649. ),
  650. // Find boards with nonexistent parents.
  651. 'missing_parents' => array(
  652. 'check_query' => '
  653. SELECT b.id_board, b.id_parent
  654. FROM {db_prefix}boards AS b
  655. LEFT JOIN {db_prefix}boards AS p ON (p.id_board = b.id_parent)
  656. WHERE b.id_parent != 0
  657. AND (p.id_board IS NULL OR p.id_board = b.id_board)
  658. ORDER BY b.id_parent, b.id_board',
  659. 'fix_collect' => array(
  660. 'index' => 'id_parent',
  661. 'process' => create_function('$parents', '
  662. global $smcFunc, $salvageBoardID, $salvageCatID;
  663. createSalvageArea();
  664. $smcFunc[\'db_query\'](\'\', \'
  665. UPDATE {db_prefix}boards
  666. SET id_parent = {int:salvageBoardID}, id_cat = {int:salvageCatID}, child_level = 1
  667. WHERE id_parent IN ({array_int:parents})\',
  668. array(
  669. \'salvageBoardID\' => $salvageBoardID,
  670. \'salvageCatID\' => $salvageCatID,
  671. \'parents\' => $parents,
  672. )
  673. );
  674. '),
  675. ),
  676. 'messages' => array('repair_missing_parents', 'id_board', 'id_parent'),
  677. ),
  678. 'missing_polls' => array(
  679. 'substeps' => array(
  680. 'step_size' => 500,
  681. 'step_max' => '
  682. SELECT MAX(id_poll)
  683. FROM {db_prefix}topics'
  684. ),
  685. 'check_query' => '
  686. SELECT t.id_poll, t.id_topic
  687. FROM {db_prefix}topics AS t
  688. LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
  689. WHERE t.id_poll != 0
  690. AND t.id_poll BETWEEN {STEP_LOW} AND {STEP_HIGH}
  691. AND p.id_poll IS NULL',
  692. 'fix_collect' => array(
  693. 'index' => 'id_poll',
  694. 'process' => create_function('$polls', '
  695. global $smcFunc;
  696. $smcFunc[\'db_query\'](\'\', \'
  697. UPDATE {db_prefix}topics
  698. SET id_poll = 0
  699. WHERE id_poll IN ({array_int:polls})\',
  700. array(
  701. \'polls\' => $polls,
  702. )
  703. );
  704. '),
  705. ),
  706. 'messages' => array('repair_missing_polls', 'id_topic', 'id_poll'),
  707. ),
  708. 'missing_calendar_topics' => array(
  709. 'substeps' => array(
  710. 'step_size' => 1000,
  711. 'step_max' => '
  712. SELECT MAX(id_topic)
  713. FROM {db_prefix}calendar'
  714. ),
  715. 'check_query' => '
  716. SELECT cal.id_topic, cal.id_event
  717. FROM {db_prefix}calendar AS cal
  718. LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = cal.id_topic)
  719. WHERE cal.id_topic != 0
  720. AND cal.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  721. AND t.id_topic IS NULL
  722. ORDER BY cal.id_topic',
  723. 'fix_collect' => array(
  724. 'index' => 'id_topic',
  725. 'process' => create_function('$events', '
  726. global $smcFunc;
  727. $smcFunc[\'db_query\'](\'\', \'
  728. UPDATE {db_prefix}calendar
  729. SET id_topic = 0, id_board = 0
  730. WHERE id_topic IN ({array_int:events})\',
  731. array(
  732. \'events\' => $events,
  733. )
  734. );
  735. '),
  736. ),
  737. 'messages' => array('repair_missing_calendar_topics', 'id_event', 'id_topic'),
  738. ),
  739. 'missing_log_topics' => array(
  740. 'substeps' => array(
  741. 'step_size' => 150,
  742. 'step_max' => '
  743. SELECT MAX(id_member)
  744. FROM {db_prefix}log_topics'
  745. ),
  746. 'check_query' => '
  747. SELECT lt.id_topic
  748. FROM {db_prefix}log_topics AS lt
  749. LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lt.id_topic)
  750. WHERE t.id_topic IS NULL
  751. AND lt.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}',
  752. 'fix_collect' => array(
  753. 'index' => 'id_topic',
  754. 'process' => create_function('$topics', '
  755. global $smcFunc;
  756. $smcFunc[\'db_query\'](\'\', \'
  757. DELETE FROM {db_prefix}log_topics
  758. WHERE id_topic IN ({array_int:topics})\',
  759. array(
  760. \'topics\' => $topics,
  761. )
  762. );
  763. '),
  764. ),
  765. 'messages' => array('repair_missing_log_topics', 'id_topic'),
  766. ),
  767. 'missing_log_topics_members' => array(
  768. 'substeps' => array(
  769. 'step_size' => 150,
  770. 'step_max' => '
  771. SELECT MAX(id_member)
  772. FROM {db_prefix}log_topics'
  773. ),
  774. 'check_query' => '
  775. SELECT lt.id_member
  776. FROM {db_prefix}log_topics AS lt
  777. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lt.id_member)
  778. WHERE mem.id_member IS NULL
  779. AND lt.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  780. GROUP BY lt.id_member',
  781. 'fix_collect' => array(
  782. 'index' => 'id_member',
  783. 'process' => create_function('$members', '
  784. global $smcFunc;
  785. $smcFunc[\'db_query\'](\'\', \'
  786. DELETE FROM {db_prefix}log_topics
  787. WHERE id_member IN ({array_int:members})\',
  788. array(
  789. \'members\' => $members,
  790. )
  791. );
  792. '),
  793. ),
  794. 'messages' => array('repair_missing_log_topics_members', 'id_member'),
  795. ),
  796. 'missing_log_boards' => array(
  797. 'substeps' => array(
  798. 'step_size' => 500,
  799. 'step_max' => '
  800. SELECT MAX(id_member)
  801. FROM {db_prefix}log_boards'
  802. ),
  803. 'check_query' => '
  804. SELECT lb.id_board
  805. FROM {db_prefix}log_boards AS lb
  806. LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lb.id_board)
  807. WHERE b.id_board IS NULL
  808. AND lb.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  809. GROUP BY lb.id_board',
  810. 'fix_collect' => array(
  811. 'index' => 'id_board',
  812. 'process' => create_function('$boards', '
  813. global $smcFunc;
  814. $smcFunc[\'db_query\'](\'\', \'
  815. DELETE FROM {db_prefix}log_boards
  816. WHERE id_board IN ({array_int:boards})\',
  817. array(
  818. \'boards\' => $boards,
  819. )
  820. );
  821. '),
  822. ),
  823. 'messages' => array('repair_missing_log_boards', 'id_board'),
  824. ),
  825. 'missing_log_boards_members' => array(
  826. 'substeps' => array(
  827. 'step_size' => 500,
  828. 'step_max' => '
  829. SELECT MAX(id_member)
  830. FROM {db_prefix}log_boards'
  831. ),
  832. 'check_query' => '
  833. SELECT lb.id_member
  834. FROM {db_prefix}log_boards AS lb
  835. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lb.id_member)
  836. WHERE mem.id_member IS NULL
  837. AND lb.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  838. GROUP BY lb.id_member',
  839. 'fix_collect' => array(
  840. 'index' => 'id_member',
  841. 'process' => create_function('$members', '
  842. global $smcFunc;
  843. $smcFunc[\'db_query\'](\'\', \'
  844. DELETE FROM {db_prefix}log_boards
  845. WHERE id_member IN ({array_int:members})\',
  846. array(
  847. \'members\' => $members,
  848. )
  849. );
  850. '),
  851. ),
  852. 'messages' => array('repair_missing_log_boards_members', 'id_member'),
  853. ),
  854. 'missing_log_mark_read' => array(
  855. 'substeps' => array(
  856. 'step_size' => 500,
  857. 'step_max' => '
  858. SELECT MAX(id_member)
  859. FROM {db_prefix}log_mark_read'
  860. ),
  861. 'check_query' => '
  862. SELECT lmr.id_board
  863. FROM {db_prefix}log_mark_read AS lmr
  864. LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lmr.id_board)
  865. WHERE b.id_board IS NULL
  866. AND lmr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  867. GROUP BY lmr.id_board',
  868. 'fix_collect' => array(
  869. 'index' => 'id_board',
  870. 'process' => create_function('$boards', '
  871. global $smcFunc;
  872. $smcFunc[\'db_query\'](\'\', \'
  873. DELETE FROM {db_prefix}log_mark_read
  874. WHERE id_board IN ({array_int:boards})\',
  875. array(
  876. \'boards\' => $boards,
  877. )
  878. );
  879. '),
  880. ),
  881. 'messages' => array('repair_missing_log_mark_read', 'id_board'),
  882. ),
  883. 'missing_log_mark_read_members' => array(
  884. 'substeps' => array(
  885. 'step_size' => 500,
  886. 'step_max' => '
  887. SELECT MAX(id_member)
  888. FROM {db_prefix}log_mark_read'
  889. ),
  890. 'check_query' => '
  891. SELECT lmr.id_member
  892. FROM {db_prefix}log_mark_read AS lmr
  893. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lmr.id_member)
  894. WHERE mem.id_member IS NULL
  895. AND lmr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  896. GROUP BY lmr.id_member',
  897. 'fix_collect' => array(
  898. 'index' => 'id_member',
  899. 'process' => create_function('$members', '
  900. global $smcFunc;
  901. $smcFunc[\'db_query\'](\'\', \'
  902. DELETE FROM {db_prefix}log_mark_read
  903. WHERE id_member IN ({array_int:members})\',
  904. array(
  905. \'members\' => $members,
  906. )
  907. );
  908. '),
  909. ),
  910. 'messages' => array('repair_missing_log_mark_read_members', 'id_member'),
  911. ),
  912. 'missing_pms' => array(
  913. 'substeps' => array(
  914. 'step_size' => 500,
  915. 'step_max' => '
  916. SELECT MAX(id_pm)
  917. FROM {db_prefix}pm_recipients'
  918. ),
  919. 'check_query' => '
  920. SELECT pmr.id_pm
  921. FROM {db_prefix}pm_recipients AS pmr
  922. LEFT JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)
  923. WHERE pm.id_pm IS NULL
  924. AND pmr.id_pm BETWEEN {STEP_LOW} AND {STEP_HIGH}
  925. GROUP BY pmr.id_pm',
  926. 'fix_collect' => array(
  927. 'index' => 'id_pm',
  928. 'process' => create_function('$pms', '
  929. global $smcFunc;
  930. $smcFunc[\'db_query\'](\'\', \'
  931. DELETE FROM {db_prefix}pm_recipients
  932. WHERE id_pm IN ({array_int:pms})\',
  933. array(
  934. \'pms\' => $pms,
  935. )
  936. );
  937. '),
  938. ),
  939. 'messages' => array('repair_missing_pms', 'id_pm'),
  940. ),
  941. 'missing_recipients' => array(
  942. 'substeps' => array(
  943. 'step_size' => 500,
  944. 'step_max' => '
  945. SELECT MAX(id_member)
  946. FROM {db_prefix}pm_recipients'
  947. ),
  948. 'check_query' => '
  949. SELECT pmr.id_member
  950. FROM {db_prefix}pm_recipients AS pmr
  951. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pmr.id_member)
  952. WHERE pmr.id_member != 0
  953. AND pmr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  954. AND mem.id_member IS NULL
  955. GROUP BY pmr.id_member',
  956. 'fix_collect' => array(
  957. 'index' => 'id_member',
  958. 'process' => create_function('$members', '
  959. global $smcFunc;
  960. $smcFunc[\'db_query\'](\'\', \'
  961. DELETE FROM {db_prefix}pm_recipients
  962. WHERE id_member IN ({array_int:members})\',
  963. array(
  964. \'members\' => $members,
  965. )
  966. );
  967. '),
  968. ),
  969. 'messages' => array('repair_missing_recipients', 'id_member'),
  970. ),
  971. 'missing_senders' => array(
  972. 'substeps' => array(
  973. 'step_size' => 500,
  974. 'step_max' => '
  975. SELECT MAX(id_pm)
  976. FROM {db_prefix}personal_messages'
  977. ),
  978. 'check_query' => '
  979. SELECT pm.id_pm, pm.id_member_from
  980. FROM {db_prefix}personal_messages AS pm
  981. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from)
  982. WHERE pm.id_member_from != 0
  983. AND pm.id_pm BETWEEN {STEP_LOW} AND {STEP_HIGH}
  984. AND mem.id_member IS NULL',
  985. 'fix_collect' => array(
  986. 'index' => 'id_pm',
  987. 'process' => create_function('$guestMessages', '
  988. global $smcFunc;
  989. $smcFunc[\'db_query\'](\'\', \'
  990. UPDATE {db_prefix}personal_messages
  991. SET id_member_from = 0
  992. WHERE id_pm IN ({array_int:guestMessages})\',
  993. array(
  994. \'guestMessages\' => $guestMessages,
  995. ));
  996. '),
  997. ),
  998. 'messages' => array('repair_missing_senders', 'id_pm', 'id_member_from'),
  999. ),
  1000. 'missing_notify_members' => array(
  1001. 'substeps' => array(
  1002. 'step_size' => 500,
  1003. 'step_max' => '
  1004. SELECT MAX(id_member)
  1005. FROM {db_prefix}log_notify'
  1006. ),
  1007. 'check_query' => '
  1008. SELECT ln.id_member
  1009. FROM {db_prefix}log_notify AS ln
  1010. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
  1011. WHERE ln.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1012. AND mem.id_member IS NULL
  1013. GROUP BY ln.id_member',
  1014. 'fix_collect' => array(
  1015. 'index' => 'id_member',
  1016. 'process' => create_function('$members', '
  1017. global $smcFunc;
  1018. $smcFunc[\'db_query\'](\'\', \'
  1019. DELETE FROM {db_prefix}log_notify
  1020. WHERE id_member IN ({array_int:members})\',
  1021. array(
  1022. \'members\' => $members,
  1023. )
  1024. );
  1025. '),
  1026. ),
  1027. 'messages' => array('repair_missing_notify_members', 'id_member'),
  1028. ),
  1029. 'missing_cached_subject' => array(
  1030. 'substeps' => array(
  1031. 'step_size' => 100,
  1032. 'step_max' => '
  1033. SELECT MAX(id_topic)
  1034. FROM {db_prefix}topics'
  1035. ),
  1036. 'check_query' => '
  1037. SELECT t.id_topic, fm.subject
  1038. FROM {db_prefix}topics AS t
  1039. INNER JOIN {db_prefix}messages AS fm ON (fm.id_msg = t.id_first_msg)
  1040. LEFT JOIN {db_prefix}log_search_subjects AS lss ON (lss.id_topic = t.id_topic)
  1041. WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1042. AND lss.id_topic IS NULL',
  1043. 'fix_full_processing' => create_function('$result', '
  1044. global $smcFunc;
  1045. $inserts = array();
  1046. while ($row = $smcFunc[\'db_fetch_assoc\']($result))
  1047. {
  1048. foreach (text2words($row[\'subject\']) as $word)
  1049. $inserts[] = array($word, $row[\'id_topic\']);
  1050. if (count($inserts) > 500)
  1051. {
  1052. $smcFunc[\'db_insert\'](\'ignore\',
  1053. \'{db_prefix}log_search_subjects\',
  1054. array(\'word\' => \'string\', \'id_topic\' => \'int\'),
  1055. $inserts,
  1056. array(\'word\', \'id_topic\')
  1057. );
  1058. $inserts = array();
  1059. }
  1060. }
  1061. if (!empty($inserts))
  1062. $smcFunc[\'db_insert\'](\'ignore\',
  1063. \'{db_prefix}log_search_subjects\',
  1064. array(\'word\' => \'string\', \'id_topic\' => \'int\'),
  1065. $inserts,
  1066. array(\'word\', \'id_topic\')
  1067. );
  1068. '),
  1069. 'message_function' => create_function('$row', '
  1070. global $txt, $context;
  1071. if (count(text2words($row[\'subject\'])) != 0)
  1072. {
  1073. $context[\'repair_errors\'][] = sprintf($txt[\'repair_missing_cached_subject\'], $row[\'id_topic\']);
  1074. return true;
  1075. }
  1076. return false;
  1077. '),
  1078. ),
  1079. 'missing_topic_for_cache' => array(
  1080. 'substeps' => array(
  1081. 'step_size' => 50,
  1082. 'step_max' => '
  1083. SELECT MAX(id_topic)
  1084. FROM {db_prefix}log_search_subjects'
  1085. ),
  1086. 'check_query' => '
  1087. SELECT lss.id_topic, lss.word
  1088. FROM {db_prefix}log_search_subjects AS lss
  1089. LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lss.id_topic)
  1090. WHERE lss.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1091. AND t.id_topic IS NULL',
  1092. 'fix_collect' => array(
  1093. 'index' => 'id_topic',
  1094. 'process' => create_function('$deleteTopics', '
  1095. global $smcFunc;
  1096. $smcFunc[\'db_query\'](\'\', \'
  1097. DELETE FROM {db_prefix}log_search_subjects
  1098. WHERE id_topic IN ({array_int:deleteTopics})\',
  1099. array(
  1100. \'deleteTopics\' => $deleteTopics,
  1101. )
  1102. );
  1103. '),
  1104. ),
  1105. 'messages' => array('repair_missing_topic_for_cache', 'word'),
  1106. ),
  1107. 'missing_member_vote' => array(
  1108. 'substeps' => array(
  1109. 'step_size' => 500,
  1110. 'step_max' => '
  1111. SELECT MAX(id_member)
  1112. FROM {db_prefix}log_polls'
  1113. ),
  1114. 'check_query' => '
  1115. SELECT lp.id_poll, lp.id_member
  1116. FROM {db_prefix}log_polls AS lp
  1117. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lp.id_member)
  1118. WHERE lp.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1119. AND lp.id_member > 0
  1120. AND mem.id_member IS NULL',
  1121. 'fix_collect' => array(
  1122. 'index' => 'id_member',
  1123. 'process' => create_function('$members', '
  1124. global $smcFunc;
  1125. $smcFunc[\'db_query\'](\'\', \'
  1126. DELETE FROM {db_prefix}log_polls
  1127. WHERE id_member IN ({array_int:members})\',
  1128. array(
  1129. \'members\' => $members,
  1130. )
  1131. );
  1132. '),
  1133. ),
  1134. 'messages' => array('repair_missing_log_poll_member', 'id_poll', 'id_member'),
  1135. ),
  1136. 'missing_log_poll_vote' => array(
  1137. 'substeps' => array(
  1138. 'step_size' => 500,
  1139. 'step_max' => '
  1140. SELECT MAX(id_poll)
  1141. FROM {db_prefix}log_polls'
  1142. ),
  1143. 'check_query' => '
  1144. SELECT lp.id_poll, lp.id_member
  1145. FROM {db_prefix}log_polls AS lp
  1146. LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = lp.id_poll)
  1147. WHERE lp.id_poll BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1148. AND p.id_poll IS NULL',
  1149. 'fix_collect' => array(
  1150. 'index' => 'id_poll',
  1151. 'process' => create_function('$polls', '
  1152. global $smcFunc;
  1153. $smcFunc[\'db_query\'](\'\', \'
  1154. DELETE FROM {db_prefix}log_polls
  1155. WHERE id_poll IN ({array_int:polls})\',
  1156. array(
  1157. \'polls\' => $polls,
  1158. )
  1159. );
  1160. '),
  1161. ),
  1162. 'messages' => array('repair_missing_log_poll_vote', 'id_member', 'id_poll'),
  1163. ),
  1164. 'report_missing_comments' => array(
  1165. 'substeps' => array(
  1166. 'step_size' => 500,
  1167. 'step_max' => '
  1168. SELECT MAX(id_report)
  1169. FROM {db_prefix}log_reported'
  1170. ),
  1171. 'check_query' => '
  1172. SELECT lr.id_report, lr.subject
  1173. FROM {db_prefix}log_reported AS lr
  1174. LEFT JOIN {db_prefix}log_reported_comments AS lrc ON (lrc.id_report = lr.id_report)
  1175. WHERE lr.id_report BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1176. AND lrc.id_report IS NULL',
  1177. 'fix_collect' => array(
  1178. 'index' => 'id_report',
  1179. 'process' => create_function('$reports', '
  1180. global $smcFunc;
  1181. $smcFunc[\'db_query\'](\'\', \'
  1182. DELETE FROM {db_prefix}log_reported
  1183. WHERE id_report IN ({array_int:reports})\',
  1184. array(
  1185. \'reports\' => $reports,
  1186. )
  1187. );
  1188. '),
  1189. ),
  1190. 'messages' => array('repair_report_missing_comments', 'id_report', 'subject'),
  1191. ),
  1192. 'comments_missing_report' => array(
  1193. 'substeps' => array(
  1194. 'step_size' => 200,
  1195. 'step_max' => '
  1196. SELECT MAX(id_report)
  1197. FROM {db_prefix}log_reported_comments'
  1198. ),
  1199. 'check_query' => '
  1200. SELECT lrc.id_report, lrc.membername
  1201. FROM {db_prefix}log_reported_comments AS lrc
  1202. LEFT JOIN {db_prefix}log_reported AS lr ON (lr.id_report = lrc.id_report)
  1203. WHERE lrc.id_report BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1204. AND lr.id_report IS NULL',
  1205. 'fix_collect' => array(
  1206. 'index' => 'id_report',
  1207. 'process' => create_function('$reports', '
  1208. global $smcFunc;
  1209. $smcFunc[\'db_query\'](\'\', \'
  1210. DELETE FROM {db_prefix}log_reported_comments
  1211. WHERE id_report IN ({array_int:reports})\',
  1212. array(
  1213. \'reports\' => $reports,
  1214. )
  1215. );
  1216. '),
  1217. ),
  1218. 'messages' => array('repair_comments_missing_report', 'id_report', 'membername'),
  1219. ),
  1220. 'group_request_missing_member' => array(
  1221. 'substeps' => array(
  1222. 'step_size' => 200,
  1223. 'step_max' => '
  1224. SELECT MAX(id_member)
  1225. FROM {db_prefix}log_group_requests'
  1226. ),
  1227. 'check_query' => '
  1228. SELECT lgr.id_member
  1229. FROM {db_prefix}log_group_requests AS lgr
  1230. LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lgr.id_member)
  1231. WHERE lgr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1232. AND mem.id_member IS NULL
  1233. GROUP BY lgr.id_member',
  1234. 'fix_collect' => array(
  1235. 'index' => 'id_member',
  1236. 'process' => create_function('$members', '
  1237. global $smcFunc;
  1238. $smcFunc[\'db_query\'](\'\', \'
  1239. DELETE FROM {db_prefix}log_group_requests
  1240. WHERE id_member IN ({array_int:members})\',
  1241. array(
  1242. \'members\' => $members,
  1243. )
  1244. );
  1245. '),
  1246. ),
  1247. 'messages' => array('repair_group_request_missing_member', 'id_member'),
  1248. ),
  1249. 'group_request_missing_group' => array(
  1250. 'substeps' => array(
  1251. 'step_size' => 200,
  1252. 'step_max' => '
  1253. SELECT MAX(id_group)
  1254. FROM {db_prefix}log_group_requests'
  1255. ),
  1256. 'check_query' => '
  1257. SELECT lgr.id_group
  1258. FROM {db_prefix}log_group_requests AS lgr
  1259. LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = lgr.id_group)
  1260. WHERE lgr.id_group BETWEEN {STEP_LOW} AND {STEP_HIGH}
  1261. AND mg.id_group IS NULL
  1262. GROUP BY lgr.id_group',
  1263. 'fix_collect' => array(
  1264. 'index' => 'id_group',
  1265. 'process' => create_function('$groups', '
  1266. global $smcFunc;
  1267. $smcFunc[\'db_query\'](\'\', \'
  1268. DELETE FROM {db_prefix}log_group_requests
  1269. WHERE id_group IN ({array_int:groups})\',
  1270. array(
  1271. \'groups\' => $groups,
  1272. )
  1273. );
  1274. '),
  1275. ),
  1276. 'messages' => array('repair_group_request_missing_group', 'id_group'),
  1277. ),
  1278. );
  1279. }
  1280. /**
  1281. * Checks for errors in steps, until 5 seconds have passed.
  1282. * It keeps track of the errors it did find, so that the actual repair
  1283. * won't have to recheck everything.
  1284. *
  1285. * @param $do_fix
  1286. * @return array, the errors found.
  1287. */
  1288. function findForumErrors($do_fix = false)
  1289. {
  1290. global $context, $txt, $smcFunc, $errorTests, $db_cache, $db_temp_cache;
  1291. // This may take some time...
  1292. @set_time_limit(600);
  1293. $to_fix = !empty($_SESSION['repairboards_to_fix']) ? $_SESSION['repairboards_to_fix'] : array();
  1294. $context['repair_errors'] = isset($_SESSION['repairboards_to_fix2']) ? $_SESSION['repairboards_to_fix2'] : array();
  1295. $_GET['step'] = empty($_GET['step']) ? 0 : (int) $_GET['step'];
  1296. $_GET['substep'] = empty($_GET['substep']) ? 0 : (int) $_GET['substep'];
  1297. // Don't allow the cache to get too full.
  1298. $db_temp_cache = $db_cache;
  1299. $db_cache = '';
  1300. $context['total_steps'] = count($errorTests);
  1301. // For all the defined error types do the necessary tests.
  1302. $current_step = -1;
  1303. $total_queries = 0;
  1304. foreach ($errorTests as $error_type => $test)
  1305. {
  1306. $current_step++;
  1307. // Already done this?
  1308. if ($_GET['step'] > $current_step)
  1309. continue;
  1310. // If we're fixing it but it ain't broke why try?
  1311. if ($do_fix && !in_array($error_type, $to_fix))
  1312. {
  1313. $_GET['step']++;
  1314. continue;
  1315. }
  1316. // Has it got substeps?
  1317. if (isset($test['substeps']))
  1318. {
  1319. $step_size = isset($test['substeps']['step_size']) ? $test['substeps']['step_size'] : 100;
  1320. $request = $smcFunc['db_query']('',
  1321. $test['substeps']['step_max'],
  1322. array(
  1323. )
  1324. );
  1325. list ($step_max) = $smcFunc['db_fetch_row']($request);
  1326. $total_queries++;
  1327. $smcFunc['db_free_result']($request);
  1328. }
  1329. // We in theory keep doing this... the substeps.
  1330. $done = false;
  1331. while (!$done)
  1332. {
  1333. // Make sure there's at least one ID to test.
  1334. if (isset($test['substeps']) && empty($step_max))
  1335. break;
  1336. // What is the testing query (Changes if we are testing or fixing)
  1337. if (!$do_fix)
  1338. $test_query = 'check_query';
  1339. else
  1340. $test_query = isset($test['fix_query']) ? 'fix_query' : 'check_query';
  1341. // Do the test...
  1342. $request = $smcFunc['db_query']('',
  1343. isset($test['substeps']) ? strtr($test[$test_query], array('{STEP_LOW}' => $_GET['substep'], '{STEP_HIGH}' => $_GET['substep'] + $step_size - 1)) : $test[$test_query],
  1344. array(
  1345. )
  1346. );
  1347. $needs_fix = false;
  1348. // Does it need a fix?
  1349. if (!empty($test['check_type']) && $test['check_type'] == 'count')
  1350. list ($needs_fix) = $smcFunc['db_fetch_row']($request);
  1351. else
  1352. $needs_fix = $smcFunc['db_num_rows']($request);
  1353. $total_queries++;
  1354. if ($needs_fix)
  1355. {
  1356. // What about a message to the user?
  1357. if (!$do_fix)
  1358. {
  1359. // Assume need to fix.
  1360. $found_errors = true;
  1361. if (isset($test['message']))
  1362. $context['repair_errors'][] = $txt[$test['message']];
  1363. // One per row!
  1364. elseif (isset($test['messages']))
  1365. {
  1366. while ($row = $smcFunc['db_fetch_assoc']($request))
  1367. {
  1368. $variables = $test['messages'];
  1369. foreach ($variables as $k => $v)
  1370. {
  1371. if ($k == 0 && isset($txt[$v]))
  1372. $variables[$k] = $txt[$v];
  1373. elseif ($k > 0 && isset($row[$v]))
  1374. $variables[$k] = $row[$v];
  1375. }
  1376. $context['repair_errors'][] = call_user_func_array('sprintf', $variables);
  1377. }
  1378. }
  1379. // A function to process?
  1380. elseif (isset($test['message_function']))
  1381. {
  1382. // Find out if there are actually errors.
  1383. $found_errors = false;
  1384. while ($row = $smcFunc['db_fetch_assoc']($request))
  1385. $found_errors |= $test['message_function']($row);
  1386. }
  1387. // Actually have something to fix?
  1388. if ($found_errors)
  1389. $to_fix[] = $error_type;
  1390. }
  1391. // We want to fix, we need to fix - so work out what exactly to do!
  1392. else
  1393. {
  1394. // Are we simply getting a collection of ids?
  1395. if (isset($test['fix_collect']))
  1396. {
  1397. $ids = array();
  1398. while ($row = $smcFunc['db_fetch_assoc']($request))
  1399. $ids[] = $row[$test['fix_collect']['index']];
  1400. if (!empty($ids))
  1401. {
  1402. // Fix it!
  1403. $test['fix_collect']['process']($ids);
  1404. }
  1405. }
  1406. // Simply executing a fix it query?
  1407. elseif (isset($test['fix_it_query']))
  1408. $smcFunc['db_query']('',
  1409. $test['fix_it_query'],
  1410. array(
  1411. )
  1412. );
  1413. // Do we have some processing to do?
  1414. elseif (isset($test['fix_processing']))
  1415. {
  1416. while ($row = $smcFunc['db_fetch_assoc']($request))
  1417. $test['fix_processing']($row);
  1418. }
  1419. // What about the full set of processing?
  1420. elseif (isset($test['fix_full_processing']))
  1421. $test['fix_full_processing']($request);
  1422. // Do we have other things we need to fix as a result?
  1423. if (!empty($test['force_fix']))
  1424. {
  1425. foreach ($test['force_fix'] as $item)
  1426. if (!in_array($item, $to_fix))
  1427. $to_fix[] = $item;
  1428. }
  1429. }
  1430. }
  1431. // Free the result.
  1432. $smcFunc['db_free_result']($request);
  1433. // Keep memory down.
  1434. $db_cache = '';
  1435. // Are we done yet?
  1436. if (isset($test['substeps']))
  1437. {
  1438. $_GET['substep'] += $step_size;
  1439. // Not done?
  1440. if ($_GET['substep'] <= $step_max)
  1441. {
  1442. pauseRepairProcess($to_fix, $error_type, $step_max);
  1443. }
  1444. else
  1445. $done = true;
  1446. }
  1447. else
  1448. $done = true;
  1449. // Don't allow more than 1000 queries at a time.
  1450. if ($total_queries >= 1000)
  1451. pauseRepairProcess($to_fix, $error_type, $step_max, true);
  1452. }
  1453. // Keep going.
  1454. $_GET['step']++;
  1455. $_GET['substep'] = 0;
  1456. $to_fix = array_unique($to_fix);
  1457. // If we're doing fixes and this needed a fix and we're all done then don't do it again.
  1458. if ($do_fix)
  1459. {
  1460. $key = array_search($error_type, $to_fix);
  1461. if ($key !== false && isset($to_fix[$key]))
  1462. unset($to_fix[$key]);
  1463. }
  1464. // Are we done?
  1465. pauseRepairProcess($to_fix, $error_type);
  1466. }
  1467. // Restore the cache.
  1468. $db_cache = $db_temp_cache;
  1469. return $to_fix;
  1470. }
  1471. /**
  1472. * Create a salvage area for repair purposes, if one doesn't already exist.
  1473. * Uses the forum's default language, and checks based on that name.
  1474. */
  1475. function createSalvageArea()
  1476. {
  1477. global $txt, $language, $salvageBoardID, $salvageCatID, $smcFunc;
  1478. static $createOnce = false;
  1479. // Have we already created it?
  1480. if ($createOnce)
  1481. return;
  1482. else
  1483. $createOnce = true;
  1484. // Back to the forum's default language.
  1485. loadLanguage('Admin', $language);
  1486. // Check to see if a 'Salvage Category' exists, if not => insert one.
  1487. $result = $smcFunc['db_query']('', '
  1488. SELECT id_cat
  1489. FROM {db_prefix}categories
  1490. WHERE name = {string:cat_name}
  1491. LIMIT 1',
  1492. array(
  1493. 'cat_name' => $txt['salvaged_category_name'],
  1494. )
  1495. );
  1496. if ($smcFunc['db_num_rows']($result) != 0)
  1497. list ($salvageCatID) = $smcFunc['db_fetch_row']($result);
  1498. $smcFunc['db_free_result']($result);
  1499. if (empty($salvageCatID))
  1500. {
  1501. $smcFunc['db_insert']('',
  1502. '{db_prefix}categories',
  1503. array('name' => 'string-255', 'cat_order' => 'int'),
  1504. array($txt['salvaged_category_name'], -1),
  1505. array('id_cat')
  1506. );
  1507. if ($smcFunc['db_affected_rows']() <= 0)
  1508. {
  1509. loadLanguage('Admin');
  1510. fatal_lang_error('salvaged_category_error', false);
  1511. }
  1512. $salvageCatID = $smcFunc['db_insert_id']('{db_prefix}categories', 'id_cat');
  1513. }
  1514. // Check to see if a 'Salvage Board' exists, if not => insert one.
  1515. $result = $smcFunc['db_query']('', '
  1516. SELECT id_board
  1517. FROM {db_prefix}boards
  1518. WHERE id_cat = {int:id_cat}
  1519. AND name = {string:board_name}
  1520. LIMIT 1',
  1521. array(
  1522. 'id_cat' => $salvageCatID,
  1523. 'board_name' => $txt['salvaged_board_name'],
  1524. )
  1525. );
  1526. if ($smcFunc['db_num_rows']($result) != 0)
  1527. list ($salvageBoardID) = $smcFunc['db_fetch_row']($result);
  1528. $smcFunc['db_free_result']($result);
  1529. if (empty($salvageBoardID))
  1530. {
  1531. $smcFunc['db_insert']('',
  1532. '{db_prefix}boards',
  1533. array('name' => 'string-255', 'description' => 'string-255', 'id_cat' => 'int', 'member_groups' => 'string', 'board_order' => 'int', 'redirect' => 'string'),
  1534. array($txt['salvaged_board_name'], $txt['salvaged_board_description'], $salvageCatID, '1', -1, ''),
  1535. array('id_board')
  1536. );
  1537. if ($smcFunc['db_affected_rows']() <= 0)
  1538. {
  1539. loadLanguage('Admin');
  1540. fatal_lang_error('salvaged_board_error', false);
  1541. }
  1542. $salvageBoardID = $smcFunc['db_insert_id']('{db_prefix}boards', 'id_board');
  1543. }
  1544. $smcFunc['db_query']('alter_table_boards', '
  1545. ALTER TABLE {db_prefix}boards
  1546. ORDER BY board_order',
  1547. array(
  1548. )
  1549. );
  1550. // Restore the user's language.
  1551. loadLanguage('Admin');
  1552. }
  1553. ?>