2
0

RepairBoards.php 50 KB

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