RepairBoards.php 49 KB

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