Completed
Pull Request — release-2.1 (#4959)
by Jeremy
07:43
created

RepairBoards.php ➔ RepairBoards()   C

Complexity

Conditions 8
Paths 14

Size

Total Lines 92

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
nc 14
nop 0
dl 0
loc 92
rs 6.9301
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This is here for the "repair any errors" feature in the admin center.
5
 *
6
 * Simple Machines Forum (SMF)
7
 *
8
 * @package SMF
9
 * @author Simple Machines http://www.simplemachines.org
10
 * @copyright 2018 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 4
14
 */
15
16
if (!defined('SMF'))
17
	die('No direct access...');
18
19
/**
20
 * Finds or repairs errors in the database to fix possible problems.
21
 * Requires the admin_forum permission.
22
 * Calls createSalvageArea() to create a new board, if necessary.
23
 * Accessed by ?action=admin;area=repairboards.
24
 *
25
 * @uses repair_boards sub-template.
26
 */
27
function RepairBoards()
28
{
29
	global $txt, $context, $sourcedir, $salvageBoardID;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
30
31
	isAllowedTo('admin_forum');
32
33
	// Try secure more memory.
34
	setMemoryLimit('128M');
35
36
	// Print out the top of the webpage.
37
	$context['page_title'] = $txt['admin_repair'];
38
	$context['sub_template'] = 'repair_boards';
39
	$context[$context['admin_menu_name']]['current_subsection'] = 'general';
40
41
	// Load the language file.
42
	loadLanguage('ManageMaintenance');
43
44
	// Make sure the tabs stay nice.
45
	$context[$context['admin_menu_name']]['tab_data'] = array(
46
		'title' => $txt['maintain_title'],
47
		'help' => '',
48
		'description' => $txt['maintain_info'],
49
		'tabs' => array(),
50
	);
51
52
	// Start displaying errors without fixing them.
53
	if (isset($_GET['fixErrors']))
54
		checkSession('get');
55
56
	// Will want this.
57
	loadForumTests();
58
59
	// Giant if/else. The first displays the forum errors if a variable is not set and asks
60
	// if you would like to continue, the other fixes the errors.
61
	if (!isset($_GET['fixErrors']))
62
	{
63
		$context['error_search'] = true;
64
		$context['repair_errors'] = array();
65
		$context['to_fix'] = findForumErrors();
66
67
		if (!empty($context['to_fix']))
68
		{
69
			$_SESSION['repairboards_to_fix'] = $context['to_fix'];
70
			$_SESSION['repairboards_to_fix2'] = null;
71
72
			if (empty($context['repair_errors']))
73
				$context['repair_errors'][] = '???';
74
		}
75
76
		// Need a token here.
77
		createToken('admin-repairboards', 'request');
78
	}
79
	else
80
	{
81
		// Validate the token, create a new one and tell the not done template.
82
		validateToken('admin-repairboards', 'request');
83
		createToken('admin-repairboards', 'request');
84
		$context['not_done_token'] = 'admin-repairboards';
85
86
		$context['error_search'] = false;
87
		$context['to_fix'] = isset($_SESSION['repairboards_to_fix']) ? $_SESSION['repairboards_to_fix'] : array();
88
89
		require_once($sourcedir . '/Subs-Boards.php');
90
91
		// Actually do the fix.
92
		findForumErrors(true);
93
94
		// Note that we've changed everything possible ;)
95
		updateSettings(array(
96
			'settings_updated' => time(),
97
		));
98
		updateStats('message');
99
		updateStats('topic');
100
		updateSettings(array(
101
			'calendar_updated' => time(),
102
		));
103
104
		// If we created a salvage area, we may need to recount stats properly.
105
		if (!empty($salvageBoardID) || !empty($_SESSION['salvageBoardID']))
106
		{
107
			unset($_SESSION['salvageBoardID']);
108
			$context['redirect_to_recount'] = true;
109
			createToken('admin-maint');
110
		}
111
112
		$_SESSION['repairboards_to_fix'] = null;
113
		$_SESSION['repairboards_to_fix2'] = null;
114
115
		// We are done at this point, dump the token,
116
		validateToken('admin-repairboards', 'request', false);
117
	}
118
}
119
120
/**
121
 * Show the not_done template to avoid CGI timeouts and similar.
122
 * Called when 3 or more seconds have passed while searching for errors.
123
 * If max_substep is set, $_GET['substep'] / $max_substep is the percent
124
 * done this step is.
125
 *
126
 * @param array $to_fix An array of information about what to fix
127
 * @param string $current_step_description The description of the current step
128
 * @param int $max_substep The maximum substep to reach before pausing
129
 * @param bool $force Whether to force pausing even if we don't really need to
130
 */
131
function pauseRepairProcess($to_fix, $current_step_description, $max_substep = 0, $force = false)
132
{
133
	global $context, $txt, $time_start, $db_temp_cache, $db_cache;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
134
135
	// More time, I need more time!
136
	@set_time_limit(600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
137
	if (function_exists('apache_reset_timeout'))
138
		@apache_reset_timeout();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
139
140
	// Errr, wait.  How much time has this taken already?
141
	if (!$force && (time() - $time_start) < 3)
142
		return;
143
144
	// Restore the query cache if interested.
145
	if (!empty($db_temp_cache))
146
		$db_cache = $db_temp_cache;
147
148
	$context['continue_get_data'] = '?action=admin;area=repairboards' . (isset($_GET['fixErrors']) ? ';fixErrors' : '') . ';step=' . $_GET['step'] . ';substep=' . $_GET['substep'] . ';' . $context['session_var'] . '=' . $context['session_id'];
149
	$context['page_title'] = $txt['not_done_title'];
150
	$context['continue_post_data'] = '';
151
	$context['continue_countdown'] = '2';
152
	$context['sub_template'] = 'not_done';
153
154
	// Change these two if more steps are added!
155
	if (empty($max_substep))
156
		$context['continue_percent'] = round(($_GET['step'] * 100) / $context['total_steps']);
157 View Code Duplication
	else
158
		$context['continue_percent'] = round((($_GET['step'] + ($_GET['substep'] / $max_substep)) * 100) / $context['total_steps']);
159
160
	// Never more than 100%!
161
	$context['continue_percent'] = min($context['continue_percent'], 100);
162
163
	// What about substeps?
164
	$context['substep_enabled'] = $max_substep != 0;
165
	$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));
166
	$context['substep_continue_percent'] = $max_substep == 0 ? 0 : round(($_GET['substep'] * 100) / $max_substep, 1);
167
168
	$_SESSION['repairboards_to_fix'] = $to_fix;
169
	$_SESSION['repairboards_to_fix2'] = $context['repair_errors'];
170
171
	obExit();
172
}
173
174
/**
175
 * Load up all the tests we might want to do ;)
176
 */
177
function loadForumTests()
178
{
179
	global $errorTests, $smcFunc, $txt, $context;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
180
181
	/* Here this array is defined like so:
182
		string check_query:	Query to be executed when testing if errors exist.
183
		string check_type:	Defines how it knows if a problem was found. If set to count looks for the first variable from check_query
184
					being > 0. Anything else it looks for some results. If not set assumes you want results.
185
		string fix_it_query:	When doing fixes if an error was detected this query is executed to "fix" it.
186
		string fix_query:	The query to execute to get data when doing a fix. If not set check_query is used again.
187
		array fix_collect:	This array is used if the fix is basically gathering all broken ids and then doing something with it.
188
			- string index:		The value returned from the main query and passed to the processing function.
189
			- process:		A function passed an array of ids to execute the fix on.
190
		function fix_processing:
191
					Function called for each row returned from fix_query to execute whatever fixes are required.
192
		function fix_full_processing:
193
					As above but does the while loop and everything itself - except the freeing.
194
		array force_fix:	If this is set then the error types included within this array will also be assumed broken.
195
					Note: At the moment only processes these if they occur after the primary error in the array.
196
	*/
197
198
	// This great array contains all of our error checks, fixes, etc etc etc.
199
	$errorTests = array(
200
		// Make a last-ditch-effort check to get rid of topics with zeros..
201
		'zero_topics' => array(
202
			'check_query' => '
203
				SELECT COUNT(*)
204
				FROM {db_prefix}topics
205
				WHERE id_topic = 0',
206
			'check_type' => 'count',
207
			'fix_it_query' => '
208
				UPDATE {db_prefix}topics
209
				SET id_topic = NULL
210
				WHERE id_topic = 0',
211
			'message' => 'repair_zero_ids',
212
		),
213
		// ... and same with messages.
214
		'zero_messages' => array(
215
			'check_query' => '
216
				SELECT COUNT(*)
217
				FROM {db_prefix}messages
218
				WHERE id_msg = 0',
219
			'check_type' => 'count',
220
			'fix_it_query' => '
221
				UPDATE {db_prefix}messages
222
				SET id_msg = NULL
223
				WHERE id_msg = 0',
224
			'message' => 'repair_zero_ids',
225
		),
226
		// Find messages that don't have existing topics.
227
		'missing_topics' => array(
228
			'substeps' => array(
229
				'step_size' => 1000,
230
				'step_max' => '
231
					SELECT MAX(id_topic)
232
					FROM {db_prefix}messages'
233
			),
234
			'check_query' => '
235
				SELECT m.id_topic, m.id_msg
236
				FROM {db_prefix}messages AS m
237
					LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
238
				WHERE m.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
239
					AND t.id_topic IS NULL
240
				ORDER BY m.id_topic, m.id_msg',
241
			'fix_query' => '
242
				SELECT
243
					m.id_board, m.id_topic, MIN(m.id_msg) AS myid_first_msg, MAX(m.id_msg) AS myid_last_msg,
244
					COUNT(*) - 1 AS my_num_replies
245
				FROM {db_prefix}messages AS m
246
					LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
247
				WHERE t.id_topic IS NULL
248
				GROUP BY m.id_topic, m.id_board',
249
			'fix_processing' => function ($row) use ($smcFunc)
250
			{
251
				global $salvageBoardID;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
252
253
				// Only if we don't have a reasonable idea of where to put it.
254 View Code Duplication
				if ($row['id_board'] == 0)
255
				{
256
					createSalvageArea();
257
					$row['id_board'] = $_SESSION['salvageBoardID'] = (int) $salvageBoardID;
258
				}
259
260
				// Make sure that no topics claim the first/last message as theirs.
261
				$smcFunc['db_query']('', '
262
					UPDATE {db_prefix}topics
263
					SET id_first_msg = 0
264
					WHERE id_first_msg = {int:id_first_msg}',
265
					array(
266
						'id_first_msg' => $row['myid_first_msg'],
267
					)
268
				);
269
				$smcFunc['db_query']('', '
270
					UPDATE {db_prefix}topics
271
					SET id_last_msg = 0
272
					WHERE id_last_msg = {int:id_last_msg}',
273
					array(
274
						'id_last_msg' => $row['myid_last_msg'],
275
					)
276
				);
277
278
				$memberStartedID = (int) getMsgMemberID($row['myid_first_msg']);
279
				$memberUpdatedID = (int) getMsgMemberID($row['myid_last_msg']);
280
281
				$newTopicID = $smcFunc['db_insert']('',
282
					'{db_prefix}topics',
283
					array(
284
						'id_board' => 'int',
285
						'id_member_started' => 'int',
286
						'id_member_updated' => 'int',
287
						'id_first_msg' => 'int',
288
						'id_last_msg' => 'int',
289
						'num_replies' => 'int'
290
					),
291
					array(
292
						$row['id_board'],
293
						$memberStartedID,
294
						$memberUpdatedID,
295
						$row['myid_first_msg'],
296
						$row['myid_last_msg'],
297
						$row['my_num_replies']
298
					),
299
					array('id_topic'),
300
					1
301
				);
302
303
				$smcFunc['db_query']('', '
304
					UPDATE {db_prefix}messages
305
					SET id_topic = {int:newTopicID}, id_board = {int:board_id}
306
					WHERE id_topic = {int:topic_id}',
307
					array(
308
						'board_id' => $row['id_board'],
309
						'topic_id' => $row['id_topic'],
310
						'newTopicID' => $newTopicID,
311
					)
312
				);
313
			},
314
			'force_fix' => array('stats_topics'),
315
			'messages' => array('repair_missing_topics', 'id_msg', 'id_topic'),
316
		),
317
		// Find topics with no messages.
318
		'missing_messages' => array(
319
			'substeps' => array(
320
				'step_size' => 1000,
321
				'step_max' => '
322
					SELECT MAX(id_topic)
323
					FROM {db_prefix}topics'
324
			),
325
			'check_query' => '
326
				SELECT t.id_topic, COUNT(m.id_msg) AS num_msg
327
				FROM {db_prefix}topics AS t
328
					LEFT JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic)
329
				WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
330
				GROUP BY t.id_topic
331
				HAVING COUNT(m.id_msg) = 0',
332
			// Remove all topics that have zero messages in the messages table.
333
			'fix_collect' => array(
334
				'index' => 'id_topic',
335
				'process' => function ($topics) use ($smcFunc)
336
				{
337
					$smcFunc['db_query']('', '
338
						DELETE FROM {db_prefix}topics
339
						WHERE id_topic IN ({array_int:topics})',
340
						array(
341
							'topics' => $topics,
342
						)
343
					);
344
					$smcFunc['db_query']('', '
345
						DELETE FROM {db_prefix}log_topics
346
						WHERE id_topic IN ({array_int:topics})',
347
						array(
348
							'topics' => $topics,
349
						)
350
					);
351
				},
352
			),
353
			'messages' => array('repair_missing_messages', 'id_topic'),
354
		),
355
		'poll_options_missing_poll' => array(
356
			'substeps' => array(
357
				'step_size' => 500,
358
				'step_max' => '
359
					SELECT MAX(id_poll)
360
					FROM {db_prefix}poll_choices'
361
			),
362
			'check_query' => '
363
				SELECT o.id_poll, count(*) as amount, t.id_topic, t.id_board, t.id_member_started AS id_poster, m.member_name AS poster_name
364
				FROM {db_prefix}poll_choices AS o
365
				  LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = o.id_poll)
366
				  LEFT JOIN {db_prefix}topics AS t ON (t.id_poll = o.id_poll)
367
				  LEFT JOIN {db_prefix}members AS m ON (m.id_member = t.id_member_started)
368
				WHERE o.id_poll BETWEEN {STEP_LOW} AND {STEP_HIGH}
369
				  AND p.id_poll IS NULL
370
				GROUP BY o.id_poll, t.id_topic, t.id_board, t.id_member_started, m.member_name
371
				  ',
372
			'fix_processing' => function ($row) use ($smcFunc, $txt)
373
			{
374
				global $salvageBoardID;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
375
376
				$row['poster_name'] = !empty($row['poster_name']) ? $row['poster_name'] : $txt['guest'];
377
				$row['id_poster'] = !empty($row['id_poster']) ? $row['id_poster'] : 0;
378
379 View Code Duplication
				if (empty($row['id_board']))
380
				{
381
					// Only if we don't have a reasonable idea of where to put it.
382
					createSalvageArea();
383
					$row['id_board'] = $_SESSION['salvageBoardID'] = (int) $salvageBoardID;
384
				}
385
386
				if (empty($row['id_topic']))
387
				{
388
					$newMessageID = $smcFunc['db_insert']('',
389
						'{db_prefix}messages',
390
						array(
391
							'id_board' => 'int',
392
							'id_topic' => 'int',
393
							'poster_time' => 'int',
394
							'id_member' => 'int',
395
							'subject' => 'string-255',
396
							'poster_name' => 'string-255',
397
							'poster_email' => 'string-255',
398
							'poster_ip' => 'inet',
399
							'smileys_enabled' => 'int',
400
							'body' => 'string-65534',
401
							'icon' => 'string-16',
402
							'approved' => 'int',
403
						),
404
						array(
405
							$row['id_board'],
406
							0,
407
							time(),
408
							$row['id_poster'],
409
							$txt['salvaged_poll_topic_name'],
410
							$row['poster_name'],
411
							$txt['salvaged_poll_topic_name'],
412
							'127.0.0.1',
413
							1,
414
							$txt['salvaged_poll_message_body'],
415
							'xx',
416
							1,
417
						),
418
						array('id_msg'),
419
						1
420
					);
421
422
					$row['id_topic'] = $smcFunc['db_insert']('',
423
						'{db_prefix}topics',
424
						array(
425
							'id_board' => 'int',
426
							'id_poll' => 'int',
427
							'id_member_started' => 'int',
428
							'id_member_updated' => 'int',
429
							'id_first_msg' => 'int',
430
							'id_last_msg' => 'int',
431
							'num_replies' => 'int',
432
						),
433
						array(
434
							$row['id_board'],
435
							$row['id_poll'],
436
							$row['id_poster'],
437
							$row['id_poster'],
438
							$newMessageID,
439
							$newMessageID,
440
							0,
441
						),
442
						array('id_topic'),
443
						1
444
					);
445
446
					$smcFunc['db_query']('', '
447
						UPDATE {db_prefix}messages
448
					SET id_topic = {int:newTopicID}, id_board = {int:id_board}
449
						WHERE id_msg = {int:newMessageID}',
450
						array(
451
							'id_board' => $row['id_board'],
452
							'newTopicID' => $row['id_topic'],
453
							'newMessageID' => $newMessageID,
454
						)
455
					);
456
457
					updateStats('subject', $row['id_topic'], $txt['salvaged_poll_topic_name']);
458
				}
459
460
				$smcFunc['db_insert']('',
461
					'{db_prefix}polls',
462
					array(
463
						'id_poll' => 'int',
464
						'question' => 'string-255',
465
						'voting_locked' => 'int',
466
						'max_votes' => 'int',
467
						'expire_time' => 'int',
468
						'hide_results' => 'int',
469
						'change_vote' => 'int',
470
						'guest_vote' => 'int',
471
						'num_guest_voters' => 'int',
472
						'reset_poll' => 'int',
473
						'id_member' => 'int',
474
						'poster_name' => 'string-255',
475
					),
476
					array(
477
						$row['id_poll'],
478
						$txt['salvaged_poll_question'],
479
						1,
480
						0,
481
						0,
482
						0,
483
						0,
484
						0,
485
						0,
486
						0,
487
						$row['id_poster'],
488
						$row['poster_name'],
489
					),
490
					array()
491
				);
492
			},
493
			'force_fix' => array('stats_topics'),
494
			'messages' => array('repair_poll_options_missing_poll', 'id_poll', 'amount'),
495
		),
496
		'polls_missing_topics' => array(
497
			'substeps' => array(
498
				'step_size' => 500,
499
				'step_max' => '
500
					SELECT MAX(id_poll)
501
					FROM {db_prefix}polls'
502
			),
503
			'check_query' => '
504
				SELECT p.id_poll, p.id_member, p.poster_name, t.id_board
505
				FROM {db_prefix}polls AS p
506
					LEFT JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll)
507
				WHERE p.id_poll BETWEEN {STEP_LOW} AND {STEP_HIGH}
508
					AND t.id_poll IS NULL',
509
			'fix_processing' => function ($row) use ($smcFunc, $txt)
510
			{
511
				global $salvageBoardID;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
512
513
				// Only if we don't have a reasonable idea of where to put it.
514 View Code Duplication
				if ($row['id_board'] == 0)
515
				{
516
					createSalvageArea();
517
					$row['id_board'] = $_SESSION['salvageBoardID'] = (int) $salvageBoardID;
518
				}
519
520
				$row['poster_name'] = !empty($row['poster_name']) ? $row['poster_name'] : $txt['guest'];
521
522
				$newMessageID = $smcFunc['db_insert']('',
523
					'{db_prefix}messages',
524
					array(
525
						'id_board' => 'int',
526
						'id_topic' => 'int',
527
						'poster_time' => 'int',
528
						'id_member' => 'int',
529
						'subject' => 'string-255',
530
						'poster_name' => 'string-255',
531
						'poster_email' => 'string-255',
532
						'poster_ip' => 'inet',
533
						'smileys_enabled' => 'int',
534
						'body' => 'string-65534',
535
						'icon' => 'string-16',
536
						'approved' => 'int',
537
					),
538
					array(
539
						$row['id_board'],
540
						0,
541
						time(),
542
						$row['id_member'],
543
						$txt['salvaged_poll_topic_name'],
544
						$row['poster_name'],
545
						'',
546
						'127.0.0.1',
547
						1,
548
						$txt['salvaged_poll_message_body'],
549
						'xx',
550
						1,
551
					),
552
					array('id_msg'),
553
					1
554
				);
555
556
				$newTopicID = $smcFunc['db_insert']('',
557
					'{db_prefix}topics',
558
					array(
559
						'id_board' => 'int',
560
						'id_poll' => 'int',
561
						'id_member_started' => 'int',
562
						'id_member_updated' => 'int',
563
						'id_first_msg' => 'int',
564
						'id_last_msg' => 'int',
565
						'num_replies' => 'int',
566
					),
567
					array(
568
						$row['id_board'],
569
						$row['id_poll'],
570
						$row['id_member'],
571
						$row['id_member'],
572
						$newMessageID,
573
						$newMessageID,
574
						0,
575
					),
576
					array('id_topic'),
577
					1
578
				);
579
580
				$smcFunc['db_query']('', '
581
					UPDATE {db_prefix}messages
582
				SET id_topic = {int:newTopicID}, id_board = {int:id_board}
583
					WHERE id_msg = {int:newMessageID}',
584
					array(
585
						'id_board' => $row['id_board'],
586
						'newTopicID' => $newTopicID,
587
						'newMessageID' => $newMessageID,
588
					)
589
				);
590
591
				updateStats('subject', $newTopicID, $txt['salvaged_poll_topic_name']);
592
		},
593
			'force_fix' => array('stats_topics'),
594
			'messages' => array('repair_polls_missing_topics', 'id_poll', 'id_topic'),
595
		),
596
		'stats_topics' => array(
597
			'substeps' => array(
598
				'step_size' => 200,
599
				'step_max' => '
600
					SELECT MAX(id_topic)
601
					FROM {db_prefix}topics'
602
			),
603
			'check_query' => '
604
				SELECT
605
					t.id_topic, t.id_first_msg, t.id_last_msg,
606
					CASE WHEN MIN(ma.id_msg) > 0 THEN
607
						CASE WHEN MIN(mu.id_msg) > 0 THEN
608
							CASE WHEN MIN(mu.id_msg) < MIN(ma.id_msg) THEN MIN(mu.id_msg) ELSE MIN(ma.id_msg) END ELSE
609
						MIN(ma.id_msg) END ELSE
610
					MIN(mu.id_msg) END AS myid_first_msg,
611
					CASE WHEN MAX(ma.id_msg) > 0 THEN MAX(ma.id_msg) ELSE MIN(mu.id_msg) END AS myid_last_msg,
612
					t.approved, mf.approved, mf.approved AS firstmsg_approved
613
				FROM {db_prefix}topics AS t
614
					LEFT JOIN {db_prefix}messages AS ma ON (ma.id_topic = t.id_topic AND ma.approved = 1)
615
					LEFT JOIN {db_prefix}messages AS mu ON (mu.id_topic = t.id_topic AND mu.approved = 0)
616
					LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
617
				WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
618
				GROUP BY t.id_topic, t.id_first_msg, t.id_last_msg, t.approved, mf.approved
619
				ORDER BY t.id_topic',
620
			'fix_processing' => function ($row) use ($smcFunc)
621
			{
622
				$row['firstmsg_approved'] = (int) $row['firstmsg_approved'];
623
				$row['myid_first_msg'] = (int) $row['myid_first_msg'];
624
				$row['myid_last_msg'] = (int) $row['myid_last_msg'];
625
626
				// Not really a problem?
627 View Code Duplication
				if ($row['id_first_msg'] == $row['myid_first_msg'] && $row['id_last_msg'] == $row['myid_last_msg'] && $row['approved'] == $row['firstmsg_approved'])
628
					return false;
629
630
				$memberStartedID = (int) getMsgMemberID($row['myid_first_msg']);
631
				$memberUpdatedID = (int) getMsgMemberID($row['myid_last_msg']);
632
633
				$smcFunc['db_query']('', '
634
					UPDATE {db_prefix}topics
635
					SET id_first_msg = {int:myid_first_msg},
636
						id_member_started = {int:memberStartedID}, id_last_msg = {int:myid_last_msg},
637
						id_member_updated = {int:memberUpdatedID}, approved = {int:firstmsg_approved}
638
					WHERE id_topic = {int:topic_id}',
639
					array(
640
						'myid_first_msg' => $row['myid_first_msg'],
641
						'memberStartedID' => $memberStartedID,
642
						'myid_last_msg' => $row['myid_last_msg'],
643
						'memberUpdatedID' => $memberUpdatedID,
644
						'firstmsg_approved' => $row['firstmsg_approved'],
645
						'topic_id' => $row['id_topic'],
646
					)
647
				);
648
			},
649
			'message_function' => function ($row) use ($txt, &$context)
650
			{
651
				// A pretend error?
652 View Code Duplication
				if ($row['id_first_msg'] == $row['myid_first_msg'] && $row['id_last_msg'] == $row['myid_last_msg'] && $row['approved'] == $row['firstmsg_approved'])
653
					return false;
654
655
				if ($row['id_first_msg'] != $row['myid_first_msg'])
656
					$context['repair_errors'][] = sprintf($txt['repair_stats_topics_1'], $row['id_topic'], $row['id_first_msg']);
657
				if ($row['id_last_msg'] != $row['myid_last_msg'])
658
					$context['repair_errors'][] = sprintf($txt['repair_stats_topics_2'], $row['id_topic'], $row['id_last_msg']);
659
				if ($row['approved'] != $row['firstmsg_approved'])
660
					$context['repair_errors'][] = sprintf($txt['repair_stats_topics_5'], $row['id_topic']);
661
662
				return true;
663
			},
664
		),
665
		// Find topics with incorrect num_replies.
666
		'stats_topics2' => array(
667
			'substeps' => array(
668
				'step_size' => 300,
669
				'step_max' => '
670
					SELECT MAX(id_topic)
671
					FROM {db_prefix}topics'
672
			),
673
			'check_query' => '
674
				SELECT
675
					t.id_topic, t.num_replies, mf.approved,
676
					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
677
				FROM {db_prefix}topics AS t
678
					LEFT JOIN {db_prefix}messages AS ma ON (ma.id_topic = t.id_topic AND ma.approved = 1)
679
					LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
680
				WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
681
				GROUP BY t.id_topic, t.num_replies, mf.approved
682
				ORDER BY t.id_topic',
683
			'fix_processing' => function ($row)
684
			{
685
				global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
686
				$row['my_num_replies'] = (int) $row['my_num_replies'];
687
688
				// Not really a problem?
689
				if ($row['my_num_replies'] == $row['num_replies'])
690
					return false;
691
692
				$smcFunc['db_query']('', '
693
					UPDATE {db_prefix}topics
694
					SET num_replies = {int:my_num_replies}
695
					WHERE id_topic = {int:topic_id}',
696
					array(
697
						'my_num_replies' => $row['my_num_replies'],
698
						'topic_id' => $row['id_topic'],
699
					)
700
				);
701
			},
702
			'message_function' => function ($row)
703
			{
704
				global $txt, $context;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
705
706
				// Just joking?
707
				if ($row['my_num_replies'] == $row['num_replies'])
708
					return false;
709
710
				if ($row['num_replies'] != $row['my_num_replies'])
711
					$context['repair_errors'][] = sprintf($txt['repair_stats_topics_3'], $row['id_topic'], $row['num_replies']);
712
713
				return true;
714
			},
715
		),
716
		// Find topics with incorrect unapproved_posts.
717
		'stats_topics3' => array(
718
			'substeps' => array(
719
				'step_size' => 1000,
720
				'step_max' => '
721
					SELECT MAX(id_topic)
722
					FROM {db_prefix}topics'
723
			),
724
			'check_query' => '
725
				SELECT
726
					t.id_topic, t.unapproved_posts, COUNT(mu.id_msg) AS my_unapproved_posts
727
				FROM {db_prefix}topics AS t
728
					LEFT JOIN {db_prefix}messages AS mu ON (mu.id_topic = t.id_topic AND mu.approved = 0)
729
				WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
730
				GROUP BY t.id_topic, t.unapproved_posts
731
				HAVING unapproved_posts != COUNT(mu.id_msg)
732
				ORDER BY t.id_topic',
733
			'fix_processing' => function ($row)
734
			{
735
				global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
736
				$row['my_unapproved_posts'] = (int) $row['my_unapproved_posts'];
737
738
				$smcFunc['db_query']('', '
739
					UPDATE {db_prefix}topics
740
					SET unapproved_posts = {int:my_unapproved_posts}
741
					WHERE id_topic = {int:topic_id}',
742
					array(
743
						'my_unapproved_posts' => $row['my_unapproved_posts'],
744
						'topic_id' => $row['id_topic'],
745
					)
746
				);
747
			},
748
			'messages' => array('repair_stats_topics_4', 'id_topic', 'unapproved_posts'),
749
		),
750
		// Find topics with nonexistent boards.
751
		'missing_boards' => array(
752
			'substeps' => array(
753
				'step_size' => 1000,
754
				'step_max' => '
755
					SELECT MAX(id_topic)
756
					FROM {db_prefix}topics'
757
			),
758
			'check_query' => '
759
				SELECT t.id_topic, t.id_board
760
				FROM {db_prefix}topics AS t
761
					LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
762
				WHERE b.id_board IS NULL
763
					AND t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
764
				ORDER BY t.id_board, t.id_topic',
765
			'fix_query' => '
766
				SELECT t.id_board, COUNT(*) AS my_num_topics, COUNT(m.id_msg) AS my_num_posts
767
				FROM {db_prefix}topics AS t
768
					LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
769
					LEFT JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic)
770
				WHERE b.id_board IS NULL
771
					AND t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
772
				GROUP BY t.id_board',
773
			'fix_processing' => function ($row)
774
			{
775
				global $smcFunc, $salvageCatID, $txt;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
776
				createSalvageArea();
777
778
				$row['my_num_topics'] = (int) $row['my_num_topics'];
779
				$row['my_num_posts'] = (int) $row['my_num_posts'];
780
781
				$newBoardID = $smcFunc['db_insert']('',
782
					'{db_prefix}boards',
783
					array('id_cat' => 'int', 'name' => 'string', 'description' => 'string', 'num_topics' => 'int', 'num_posts' => 'int', 'member_groups' => 'string'),
784
					array($salvageCatID, $txt['salvaged_board_name'], $txt['salvaged_board_description'], $row['my_num_topics'], $row['my_num_posts'], '1'),
785
					array('id_board'),
786
					1
787
				);
788
789
				$smcFunc['db_query']('', '
790
					UPDATE {db_prefix}topics
791
					SET id_board = {int:newBoardID}
792
					WHERE id_board = {int:board_id}',
793
					array(
794
						'newBoardID' => $newBoardID,
795
						'board_id' => $row['id_board'],
796
					)
797
				);
798
				$smcFunc['db_query']('', '
799
					UPDATE {db_prefix}messages
800
					SET id_board = {int:newBoardID}
801
					WHERE id_board = {int:board_id}',
802
					array(
803
						'newBoardID' => $newBoardID,
804
						'board_id' => $row['id_board'],
805
					)
806
				);
807
			},
808
			'messages' => array('repair_missing_boards', 'id_topic', 'id_board'),
809
		),
810
		// Find boards with nonexistent categories.
811
		'missing_categories' => array(
812
			'check_query' => '
813
				SELECT b.id_board, b.id_cat
814
				FROM {db_prefix}boards AS b
815
					LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
816
				WHERE c.id_cat IS NULL
817
				ORDER BY b.id_cat, b.id_board',
818
			'fix_collect' => array(
819
				'index' => 'id_cat',
820
				'process' => function ($cats)
821
				{
822
					global $smcFunc, $salvageCatID;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
823
					createSalvageArea();
824
					$smcFunc['db_query']('', '
825
						UPDATE {db_prefix}boards
826
						SET id_cat = {int:salvageCatID}
827
						WHERE id_cat IN ({array_int:categories})',
828
						array(
829
							'salvageCatID' => $salvageCatID,
830
							'categories' => $cats,
831
						)
832
					);
833
				},
834
			),
835
			'messages' => array('repair_missing_categories', 'id_board', 'id_cat'),
836
		),
837
		// Find messages with nonexistent members.
838
		'missing_posters' => array(
839
			'substeps' => array(
840
				'step_size' => 2000,
841
				'step_max' => '
842
					SELECT MAX(id_msg)
843
					FROM {db_prefix}messages'
844
			),
845
			'check_query' => '
846
				SELECT m.id_msg, m.id_member
847
				FROM {db_prefix}messages AS m
848
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
849
				WHERE mem.id_member IS NULL
850
					AND m.id_member != 0
851
					AND m.id_msg BETWEEN {STEP_LOW} AND {STEP_HIGH}
852
				ORDER BY m.id_msg',
853
			// Last step-make sure all non-guest posters still exist.
854
			'fix_collect' => array(
855
				'index' => 'id_msg',
856
				'process' => function ($msgs)
857
				{
858
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
859
					$smcFunc['db_query']('', '
860
						UPDATE {db_prefix}messages
861
						SET id_member = {int:guest_id}
862
						WHERE id_msg IN ({array_int:msgs})',
863
						array(
864
							'msgs' => $msgs,
865
							'guest_id' => 0,
866
						)
867
					);
868
				},
869
			),
870
			'messages' => array('repair_missing_posters', 'id_msg', 'id_member'),
871
		),
872
		// Find boards with nonexistent parents.
873
		'missing_parents' => array(
874
			'check_query' => '
875
				SELECT b.id_board, b.id_parent
876
				FROM {db_prefix}boards AS b
877
					LEFT JOIN {db_prefix}boards AS p ON (p.id_board = b.id_parent)
878
				WHERE b.id_parent != 0
879
					AND (p.id_board IS NULL OR p.id_board = b.id_board)
880
				ORDER BY b.id_parent, b.id_board',
881
			'fix_collect' => array(
882
				'index' => 'id_parent',
883
				'process' => function ($parents)
884
				{
885
					global $smcFunc, $salvageBoardID, $salvageCatID;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
886
887
					createSalvageArea();
888
					$_SESSION['salvageBoardID'] = (int) $salvageBoardID;
889
890
					$smcFunc['db_query']('', '
891
						UPDATE {db_prefix}boards
892
						SET id_parent = {int:salvageBoardID}, id_cat = {int:salvageCatID}, child_level = 1
893
						WHERE id_parent IN ({array_int:parents})',
894
						array(
895
							'salvageBoardID' => $salvageBoardID,
896
							'salvageCatID' => $salvageCatID,
897
							'parents' => $parents,
898
						)
899
					);
900
				},
901
			),
902
			'messages' => array('repair_missing_parents', 'id_board', 'id_parent'),
903
		),
904
		'missing_polls' => array(
905
			'substeps' => array(
906
				'step_size' => 500,
907
				'step_max' => '
908
					SELECT MAX(id_poll)
909
					FROM {db_prefix}topics'
910
			),
911
			'check_query' => '
912
				SELECT t.id_poll, t.id_topic
913
				FROM {db_prefix}topics AS t
914
					LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = t.id_poll)
915
				WHERE t.id_poll != 0
916
					AND t.id_poll BETWEEN {STEP_LOW} AND {STEP_HIGH}
917
					AND p.id_poll IS NULL',
918
			'fix_collect' => array(
919
				'index' => 'id_poll',
920
				'process' => function ($polls)
921
				{
922
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
923
					$smcFunc['db_query']('', '
924
						UPDATE {db_prefix}topics
925
						SET id_poll = 0
926
						WHERE id_poll IN ({array_int:polls})',
927
						array(
928
							'polls' => $polls,
929
						)
930
					);
931
				},
932
			),
933
			'messages' => array('repair_missing_polls', 'id_topic', 'id_poll'),
934
		),
935
		'missing_calendar_topics' => array(
936
			'substeps' => array(
937
				'step_size' => 1000,
938
				'step_max' => '
939
					SELECT MAX(id_topic)
940
					FROM {db_prefix}calendar'
941
			),
942
			'check_query' => '
943
				SELECT cal.id_topic, cal.id_event
944
				FROM {db_prefix}calendar AS cal
945
					LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = cal.id_topic)
946
				WHERE cal.id_topic != 0
947
					AND cal.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
948
					AND t.id_topic IS NULL
949
				ORDER BY cal.id_topic',
950
			'fix_collect' => array(
951
				'index' => 'id_topic',
952
				'process' => function ($events)
953
				{
954
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
955
					$smcFunc['db_query']('', '
956
						UPDATE {db_prefix}calendar
957
						SET id_topic = 0, id_board = 0
958
						WHERE id_topic IN ({array_int:events})',
959
						array(
960
							'events' => $events,
961
						)
962
					);
963
				},
964
			),
965
			'messages' => array('repair_missing_calendar_topics', 'id_event', 'id_topic'),
966
		),
967
		'missing_log_topics' => array(
968
			'substeps' => array(
969
				'step_size' => 150,
970
				'step_max' => '
971
					SELECT MAX(id_member)
972
					FROM {db_prefix}log_topics'
973
			),
974
			'check_query' => '
975
				SELECT lt.id_topic
976
				FROM {db_prefix}log_topics AS lt
977
					LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lt.id_topic)
978
				WHERE t.id_topic IS NULL
979
					AND lt.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}',
980
			'fix_collect' => array(
981
				'index' => 'id_topic',
982
				'process' => function ($topics)
983
				{
984
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
985
					$smcFunc['db_query']('', '
986
						DELETE FROM {db_prefix}log_topics
987
						WHERE id_topic IN ({array_int:topics})',
988
						array(
989
							'topics' => $topics,
990
						)
991
					);
992
				},
993
			),
994
			'messages' => array('repair_missing_log_topics', 'id_topic'),
995
		),
996
		'missing_log_topics_members' => array(
997
			'substeps' => array(
998
				'step_size' => 150,
999
				'step_max' => '
1000
					SELECT MAX(id_member)
1001
					FROM {db_prefix}log_topics'
1002
			),
1003
			'check_query' => '
1004
				SELECT lt.id_member
1005
				FROM {db_prefix}log_topics AS lt
1006
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lt.id_member)
1007
				WHERE mem.id_member IS NULL
1008
					AND lt.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
1009
				GROUP BY lt.id_member',
1010
			'fix_collect' => array(
1011
				'index' => 'id_member',
1012
				'process' => function ($members)
1013
				{
1014
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1015
					$smcFunc['db_query']('', '
1016
						DELETE FROM {db_prefix}log_topics
1017
						WHERE id_member IN ({array_int:members})',
1018
						array(
1019
							'members' => $members,
1020
						)
1021
					);
1022
				},
1023
			),
1024
			'messages' => array('repair_missing_log_topics_members', 'id_member'),
1025
		),
1026
		'missing_log_boards' => array(
1027
			'substeps' => array(
1028
				'step_size' => 500,
1029
				'step_max' => '
1030
					SELECT MAX(id_member)
1031
					FROM {db_prefix}log_boards'
1032
			),
1033
			'check_query' => '
1034
				SELECT lb.id_board
1035
				FROM {db_prefix}log_boards AS lb
1036
					LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lb.id_board)
1037
				WHERE b.id_board IS NULL
1038
					AND lb.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
1039
				GROUP BY lb.id_board',
1040
			'fix_collect' => array(
1041
				'index' => 'id_board',
1042
				'process' => function ($boards)
1043
				{
1044
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1045
					$smcFunc['db_query']('', '
1046
						DELETE FROM {db_prefix}log_boards
1047
						WHERE id_board IN ({array_int:boards})',
1048
						array(
1049
							'boards' => $boards,
1050
						)
1051
					);
1052
				},
1053
			),
1054
			'messages' => array('repair_missing_log_boards', 'id_board'),
1055
		),
1056
		'missing_log_boards_members' => array(
1057
			'substeps' => array(
1058
				'step_size' => 500,
1059
				'step_max' => '
1060
					SELECT MAX(id_member)
1061
					FROM {db_prefix}log_boards'
1062
			),
1063
			'check_query' => '
1064
				SELECT lb.id_member
1065
				FROM {db_prefix}log_boards AS lb
1066
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lb.id_member)
1067
				WHERE mem.id_member IS NULL
1068
					AND lb.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
1069
				GROUP BY lb.id_member',
1070
			'fix_collect' => array(
1071
				'index' => 'id_member',
1072
				'process' => function ($members) use ($smcFunc)
1073
				{
1074
					$smcFunc['db_query']('', '
1075
						DELETE FROM {db_prefix}log_boards
1076
						WHERE id_member IN ({array_int:members})',
1077
						array(
1078
							'members' => $members,
1079
						)
1080
					);
1081
				},
1082
			),
1083
			'messages' => array('repair_missing_log_boards_members', 'id_member'),
1084
		),
1085
		'missing_log_mark_read' => array(
1086
			'substeps' => array(
1087
				'step_size' => 500,
1088
				'step_max' => '
1089
					SELECT MAX(id_member)
1090
					FROM {db_prefix}log_mark_read'
1091
			),
1092
			'check_query' => '
1093
				SELECT lmr.id_board
1094
				FROM {db_prefix}log_mark_read AS lmr
1095
					LEFT JOIN {db_prefix}boards AS b ON (b.id_board = lmr.id_board)
1096
				WHERE b.id_board IS NULL
1097
					AND lmr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
1098
				GROUP BY lmr.id_board',
1099
			'fix_collect' => array(
1100
				'index' => 'id_board',
1101
				'process' => function ($boards) use ($smcFunc)
1102
				{
1103
					$smcFunc['db_query']('', '
1104
						DELETE FROM {db_prefix}log_mark_read
1105
						WHERE id_board IN ({array_int:boards})',
1106
						array(
1107
							'boards' => $boards,
1108
						)
1109
					);
1110
				},
1111
			),
1112
			'messages' => array('repair_missing_log_mark_read', 'id_board'),
1113
		),
1114
		'missing_log_mark_read_members' => array(
1115
			'substeps' => array(
1116
				'step_size' => 500,
1117
				'step_max' => '
1118
					SELECT MAX(id_member)
1119
					FROM {db_prefix}log_mark_read'
1120
			),
1121
			'check_query' => '
1122
				SELECT lmr.id_member
1123
				FROM {db_prefix}log_mark_read AS lmr
1124
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lmr.id_member)
1125
				WHERE mem.id_member IS NULL
1126
					AND lmr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
1127
				GROUP BY lmr.id_member',
1128
			'fix_collect' => array(
1129
				'index' => 'id_member',
1130
				'process' => function ($members) use ($smcFunc)
1131
				{
1132
					$smcFunc['db_query']('', '
1133
						DELETE FROM {db_prefix}log_mark_read
1134
						WHERE id_member IN ({array_int:members})',
1135
						array(
1136
							'members' => $members,
1137
						)
1138
					);
1139
				},
1140
			),
1141
			'messages' => array('repair_missing_log_mark_read_members', 'id_member'),
1142
		),
1143
		'missing_pms' => array(
1144
			'substeps' => array(
1145
				'step_size' => 500,
1146
				'step_max' => '
1147
					SELECT MAX(id_pm)
1148
					FROM {db_prefix}pm_recipients'
1149
			),
1150
			'check_query' => '
1151
				SELECT pmr.id_pm
1152
				FROM {db_prefix}pm_recipients AS pmr
1153
					LEFT JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)
1154
				WHERE pm.id_pm IS NULL
1155
					AND pmr.id_pm BETWEEN {STEP_LOW} AND {STEP_HIGH}
1156
				GROUP BY pmr.id_pm',
1157
			'fix_collect' => array(
1158
				'index' => 'id_pm',
1159
				'process' => function ($pms) use ($smcFunc)
1160
				{
1161
					$smcFunc['db_query']('', '
1162
						DELETE FROM {db_prefix}pm_recipients
1163
						WHERE id_pm IN ({array_int:pms})',
1164
						array(
1165
							'pms' => $pms,
1166
						)
1167
					);
1168
				},
1169
			),
1170
			'messages' => array('repair_missing_pms', 'id_pm'),
1171
		),
1172
		'missing_recipients' => array(
1173
			'substeps' => array(
1174
				'step_size' => 500,
1175
				'step_max' => '
1176
					SELECT MAX(id_member)
1177
					FROM {db_prefix}pm_recipients'
1178
			),
1179
			'check_query' => '
1180
				SELECT pmr.id_member
1181
				FROM {db_prefix}pm_recipients AS pmr
1182
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pmr.id_member)
1183
				WHERE pmr.id_member != 0
1184
					AND pmr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
1185
					AND mem.id_member IS NULL
1186
				GROUP BY pmr.id_member',
1187
			'fix_collect' => array(
1188
				'index' => 'id_member',
1189
				'process' => function ($members)
1190
				{
1191
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1192
					$smcFunc['db_query']('', '
1193
						DELETE FROM {db_prefix}pm_recipients
1194
						WHERE id_member IN ({array_int:members})',
1195
						array(
1196
							'members' => $members,
1197
						)
1198
					);
1199
				},
1200
			),
1201
			'messages' => array('repair_missing_recipients', 'id_member'),
1202
		),
1203
		'missing_senders' => array(
1204
			'substeps' => array(
1205
				'step_size' => 500,
1206
				'step_max' => '
1207
					SELECT MAX(id_pm)
1208
					FROM {db_prefix}personal_messages'
1209
			),
1210
			'check_query' => '
1211
				SELECT pm.id_pm, pm.id_member_from
1212
				FROM {db_prefix}personal_messages AS pm
1213
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from)
1214
				WHERE pm.id_member_from != 0
1215
					AND pm.id_pm BETWEEN {STEP_LOW} AND {STEP_HIGH}
1216
					AND mem.id_member IS NULL',
1217
			'fix_collect' => array(
1218
				'index' => 'id_pm',
1219
				'process' => function ($guestMessages)
1220
				{
1221
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1222
					$smcFunc['db_query']('', '
1223
						UPDATE {db_prefix}personal_messages
1224
						SET id_member_from = 0
1225
						WHERE id_pm IN ({array_int:guestMessages})',
1226
						array(
1227
							'guestMessages' => $guestMessages,
1228
						));
1229
				},
1230
			),
1231
			'messages' => array('repair_missing_senders', 'id_pm', 'id_member_from'),
1232
		),
1233
		'missing_notify_members' => array(
1234
			'substeps' => array(
1235
				'step_size' => 500,
1236
				'step_max' => '
1237
					SELECT MAX(id_member)
1238
					FROM {db_prefix}log_notify'
1239
			),
1240
			'check_query' => '
1241
				SELECT ln.id_member
1242
				FROM {db_prefix}log_notify AS ln
1243
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
1244
				WHERE ln.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
1245
					AND mem.id_member IS NULL
1246
				GROUP BY ln.id_member',
1247
			'fix_collect' => array(
1248
				'index' => 'id_member',
1249
				'process' => function ($members) use ($smcFunc)
1250
				{
1251
					$smcFunc['db_query']('', '
1252
						DELETE FROM {db_prefix}log_notify
1253
						WHERE id_member IN ({array_int:members})',
1254
						array(
1255
							'members' => $members,
1256
						)
1257
					);
1258
				},
1259
			),
1260
			'messages' => array('repair_missing_notify_members', 'id_member'),
1261
		),
1262
		'missing_cached_subject' => array(
1263
			'substeps' => array(
1264
				'step_size' => 100,
1265
				'step_max' => '
1266
					SELECT MAX(id_topic)
1267
					FROM {db_prefix}topics'
1268
			),
1269
			'check_query' => '
1270
				SELECT t.id_topic, fm.subject
1271
				FROM {db_prefix}topics AS t
1272
					INNER JOIN {db_prefix}messages AS fm ON (fm.id_msg = t.id_first_msg)
1273
					LEFT JOIN {db_prefix}log_search_subjects AS lss ON (lss.id_topic = t.id_topic)
1274
				WHERE t.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
1275
					AND lss.id_topic IS NULL',
1276
			'fix_full_processing' => function ($result)
1277
			{
1278
				global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1279
1280
				$inserts = array();
1281
				while ($row = $smcFunc['db_fetch_assoc']($result))
1282
				{
1283
					foreach (text2words($row['subject']) as $word)
1284
						$inserts[] = array($word, $row['id_topic']);
1285 View Code Duplication
					if (count($inserts) > 500)
1286
					{
1287
						$smcFunc['db_insert']('ignore',
1288
							'{db_prefix}log_search_subjects',
1289
							array('word' => 'string', 'id_topic' => 'int'),
1290
							$inserts,
1291
							array('word', 'id_topic')
1292
						);
1293
						$inserts = array();
1294
					}
1295
				}
1296
1297 View Code Duplication
				if (!empty($inserts))
1298
					$smcFunc['db_insert']('ignore',
1299
						'{db_prefix}log_search_subjects',
1300
						array('word' => 'string', 'id_topic' => 'int'),
1301
						$inserts,
1302
						array('word', 'id_topic')
1303
					);
1304
			},
1305
			'message_function' => function ($row)
1306
			{
1307
				global $txt, $context;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1308
1309
				if (count(text2words($row['subject'])) != 0)
1310
				{
1311
					$context['repair_errors'][] = sprintf($txt['repair_missing_cached_subject'], $row['id_topic']);
1312
					return true;
1313
				}
1314
1315
				return false;
1316
			},
1317
		),
1318
		'missing_topic_for_cache' => array(
1319
			'substeps' => array(
1320
				'step_size' => 50,
1321
				'step_max' => '
1322
					SELECT MAX(id_topic)
1323
					FROM {db_prefix}log_search_subjects'
1324
			),
1325
			'check_query' => '
1326
				SELECT lss.id_topic, lss.word
1327
				FROM {db_prefix}log_search_subjects AS lss
1328
					LEFT JOIN {db_prefix}topics AS t ON (t.id_topic = lss.id_topic)
1329
				WHERE lss.id_topic BETWEEN {STEP_LOW} AND {STEP_HIGH}
1330
					AND t.id_topic IS NULL',
1331
			'fix_collect' => array(
1332
				'index' => 'id_topic',
1333
				'process' => function ($deleteTopics)
1334
				{
1335
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1336
					$smcFunc['db_query']('', '
1337
						DELETE FROM {db_prefix}log_search_subjects
1338
						WHERE id_topic IN ({array_int:deleteTopics})',
1339
						array(
1340
							'deleteTopics' => $deleteTopics,
1341
						)
1342
					);
1343
				},
1344
			),
1345
			'messages' => array('repair_missing_topic_for_cache', 'word'),
1346
		),
1347
		'missing_member_vote' => array(
1348
			'substeps' => array(
1349
				'step_size' => 500,
1350
				'step_max' => '
1351
					SELECT MAX(id_member)
1352
					FROM {db_prefix}log_polls'
1353
			),
1354
			'check_query' => '
1355
				SELECT lp.id_poll, lp.id_member
1356
				FROM {db_prefix}log_polls AS lp
1357
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lp.id_member)
1358
				WHERE lp.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
1359
					AND lp.id_member > 0
1360
					AND mem.id_member IS NULL',
1361
			'fix_collect' => array(
1362
				'index' => 'id_member',
1363
				'process' => function ($members)
1364
				{
1365
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1366
					$smcFunc['db_query']('', '
1367
						DELETE FROM {db_prefix}log_polls
1368
						WHERE id_member IN ({array_int:members})',
1369
						array(
1370
							'members' => $members,
1371
						)
1372
					);
1373
				},
1374
			),
1375
			'messages' => array('repair_missing_log_poll_member', 'id_poll', 'id_member'),
1376
		),
1377
		'missing_log_poll_vote' => array(
1378
			'substeps' => array(
1379
				'step_size' => 500,
1380
				'step_max' => '
1381
					SELECT MAX(id_poll)
1382
					FROM {db_prefix}log_polls'
1383
			),
1384
			'check_query' => '
1385
				SELECT lp.id_poll, lp.id_member
1386
				FROM {db_prefix}log_polls AS lp
1387
					LEFT JOIN {db_prefix}polls AS p ON (p.id_poll = lp.id_poll)
1388
				WHERE lp.id_poll BETWEEN {STEP_LOW} AND {STEP_HIGH}
1389
					AND p.id_poll IS NULL',
1390
			'fix_collect' => array(
1391
				'index' => 'id_poll',
1392
				'process' => function ($polls)
1393
				{
1394
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1395
					$smcFunc['db_query']('', '
1396
						DELETE FROM {db_prefix}log_polls
1397
						WHERE id_poll IN ({array_int:polls})',
1398
						array(
1399
							'polls' => $polls,
1400
						)
1401
					);
1402
				},
1403
			),
1404
			'messages' => array('repair_missing_log_poll_vote', 'id_member', 'id_poll'),
1405
		),
1406
		'report_missing_comments' => array(
1407
			'substeps' => array(
1408
				'step_size' => 500,
1409
				'step_max' => '
1410
					SELECT MAX(id_report)
1411
					FROM {db_prefix}log_reported'
1412
			),
1413
			'check_query' => '
1414
				SELECT lr.id_report, lr.subject
1415
				FROM {db_prefix}log_reported AS lr
1416
					LEFT JOIN {db_prefix}log_reported_comments AS lrc ON (lrc.id_report = lr.id_report)
1417
				WHERE lr.id_report BETWEEN {STEP_LOW} AND {STEP_HIGH}
1418
					AND lrc.id_report IS NULL',
1419
			'fix_collect' => array(
1420
				'index' => 'id_report',
1421
				'process' => function ($reports)
1422
				{
1423
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1424
					$smcFunc['db_query']('', '
1425
						DELETE FROM {db_prefix}log_reported
1426
						WHERE id_report IN ({array_int:reports})',
1427
						array(
1428
							'reports' => $reports,
1429
						)
1430
					);
1431
				},
1432
			),
1433
			'messages' => array('repair_report_missing_comments', 'id_report', 'subject'),
1434
		),
1435
		'comments_missing_report' => array(
1436
			'substeps' => array(
1437
				'step_size' => 200,
1438
				'step_max' => '
1439
					SELECT MAX(id_report)
1440
					FROM {db_prefix}log_reported_comments'
1441
			),
1442
			'check_query' => '
1443
				SELECT lrc.id_report, lrc.membername
1444
				FROM {db_prefix}log_reported_comments AS lrc
1445
					LEFT JOIN {db_prefix}log_reported AS lr ON (lr.id_report = lrc.id_report)
1446
				WHERE lrc.id_report BETWEEN {STEP_LOW} AND {STEP_HIGH}
1447
					AND lr.id_report IS NULL',
1448
			'fix_collect' => array(
1449
				'index' => 'id_report',
1450
				'process' => function ($reports)
1451
				{
1452
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1453
					$smcFunc['db_query']('', '
1454
						DELETE FROM {db_prefix}log_reported_comments
1455
						WHERE id_report IN ({array_int:reports})',
1456
						array(
1457
							'reports' => $reports,
1458
						)
1459
					);
1460
				},
1461
			),
1462
			'messages' => array('repair_comments_missing_report', 'id_report', 'membername'),
1463
		),
1464
		'group_request_missing_member' => array(
1465
			'substeps' => array(
1466
				'step_size' => 200,
1467
				'step_max' => '
1468
					SELECT MAX(id_member)
1469
					FROM {db_prefix}log_group_requests'
1470
			),
1471
			'check_query' => '
1472
				SELECT lgr.id_member
1473
				FROM {db_prefix}log_group_requests AS lgr
1474
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lgr.id_member)
1475
				WHERE lgr.id_member BETWEEN {STEP_LOW} AND {STEP_HIGH}
1476
					AND mem.id_member IS NULL
1477
				GROUP BY lgr.id_member',
1478
			'fix_collect' => array(
1479
				'index' => 'id_member',
1480
				'process' => function ($members)
1481
				{
1482
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1483
					$smcFunc['db_query']('', '
1484
						DELETE FROM {db_prefix}log_group_requests
1485
						WHERE id_member IN ({array_int:members})',
1486
						array(
1487
							'members' => $members,
1488
						)
1489
					);
1490
				},
1491
			),
1492
			'messages' => array('repair_group_request_missing_member', 'id_member'),
1493
		),
1494
		'group_request_missing_group' => array(
1495
			'substeps' => array(
1496
				'step_size' => 200,
1497
				'step_max' => '
1498
					SELECT MAX(id_group)
1499
					FROM {db_prefix}log_group_requests'
1500
			),
1501
			'check_query' => '
1502
				SELECT lgr.id_group
1503
				FROM {db_prefix}log_group_requests AS lgr
1504
					LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = lgr.id_group)
1505
				WHERE lgr.id_group BETWEEN {STEP_LOW} AND {STEP_HIGH}
1506
					AND mg.id_group IS NULL
1507
				GROUP BY lgr.id_group',
1508
			'fix_collect' => array(
1509
				'index' => 'id_group',
1510
				'process' => function ($groups)
1511
				{
1512
					global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1513
					$smcFunc['db_query']('', '
1514
						DELETE FROM {db_prefix}log_group_requests
1515
						WHERE id_group IN ({array_int:groups})',
1516
						array(
1517
							'groups' => $groups,
1518
						)
1519
					);
1520
				},
1521
			),
1522
			'messages' => array('repair_group_request_missing_group', 'id_group'),
1523
		),
1524
	);
1525
}
1526
1527
/**
1528
 * Checks for errors in steps, until 5 seconds have passed.
1529
 * It keeps track of the errors it did find, so that the actual repair
1530
 * won't have to recheck everything.
1531
 *
1532
 * @param bool $do_fix Whether to actually fix the errors or just return the info
1533
 * @return array, the errors found.
0 ignored issues
show
Documentation introduced by
The doc-type array, could not be parsed: Expected "|" or "end of type", but got "," at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1534
 */
1535
function findForumErrors($do_fix = false)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1536
{
1537
	global $context, $txt, $smcFunc, $errorTests, $db_cache, $db_temp_cache;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1538
1539
	// This may take some time...
1540
	@set_time_limit(600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1541
1542
	$to_fix = !empty($_SESSION['repairboards_to_fix']) ? $_SESSION['repairboards_to_fix'] : array();
1543
	$context['repair_errors'] = isset($_SESSION['repairboards_to_fix2']) ? $_SESSION['repairboards_to_fix2'] : array();
1544
1545
	$_GET['step'] = empty($_GET['step']) ? 0 : (int) $_GET['step'];
1546
	$_GET['substep'] = empty($_GET['substep']) ? 0 : (int) $_GET['substep'];
1547
1548
	// Don't allow the cache to get too full.
1549
	$db_temp_cache = $db_cache;
1550
	$db_cache = array();
1551
1552
	$context['total_steps'] = count($errorTests);
1553
1554
	// For all the defined error types do the necessary tests.
1555
	$current_step = -1;
1556
	$total_queries = 0;
1557
	foreach ($errorTests as $error_type => $test)
1558
	{
1559
		$current_step++;
1560
1561
		// Already done this?
1562
		if ($_GET['step'] > $current_step)
1563
			continue;
1564
1565
		// If we're fixing it but it ain't broke why try?
1566
		if ($do_fix && !in_array($error_type, $to_fix))
1567
		{
1568
			$_GET['step']++;
1569
			continue;
1570
		}
1571
1572
		// Has it got substeps?
1573
		if (isset($test['substeps']))
1574
		{
1575
			$step_size = isset($test['substeps']['step_size']) ? $test['substeps']['step_size'] : 100;
1576
			$request = $smcFunc['db_query']('',
1577
				$test['substeps']['step_max'],
1578
				array(
1579
				)
1580
			);
1581
			list ($step_max) = $smcFunc['db_fetch_row']($request);
1582
1583
			$total_queries++;
1584
			$smcFunc['db_free_result']($request);
1585
		}
1586
1587
		// We in theory keep doing this... the substeps.
1588
		$done = false;
1589
		while (!$done)
1590
		{
1591
			// Make sure there's at least one ID to test.
1592
			if (isset($test['substeps']) && empty($step_max))
1593
				break;
1594
1595
			// What is the testing query (Changes if we are testing or fixing)
1596
			if (!$do_fix)
1597
				$test_query = 'check_query';
1598
			else
1599
				$test_query = isset($test['fix_query']) ? 'fix_query' : 'check_query';
1600
1601
			// Do the test...
1602
			$request = $smcFunc['db_query']('',
1603
				isset($test['substeps']) ? strtr($test[$test_query], array('{STEP_LOW}' => $_GET['substep'], '{STEP_HIGH}' => $_GET['substep'] + $step_size - 1)) : $test[$test_query],
0 ignored issues
show
Bug introduced by
The variable $step_size does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1604
				array(
1605
				)
1606
			);
1607
1608
			// Does it need a fix?
1609 View Code Duplication
			if (!empty($test['check_type']) && $test['check_type'] == 'count')
1610
				list ($needs_fix) = $smcFunc['db_fetch_row']($request);
1611
			else
1612
				$needs_fix = $smcFunc['db_num_rows']($request);
1613
1614
			$total_queries++;
1615
1616
			if ($needs_fix)
1617
			{
1618
				// What about a message to the user?
1619
				if (!$do_fix)
1620
				{
1621
					// Assume need to fix.
1622
					$found_errors = true;
1623
1624
					if (isset($test['message']))
1625
						$context['repair_errors'][] = $txt[$test['message']];
1626
1627
					// One per row!
1628
					elseif (isset($test['messages']))
1629
					{
1630
						while ($row = $smcFunc['db_fetch_assoc']($request))
1631
						{
1632
							$variables = $test['messages'];
1633
							foreach ($variables as $k => $v)
1634
							{
1635
								if ($k == 0 && isset($txt[$v]))
1636
									$variables[$k] = $txt[$v];
1637
								elseif ($k > 0 && isset($row[$v]))
1638
									$variables[$k] = $row[$v];
1639
							}
1640
							$context['repair_errors'][] = call_user_func_array('sprintf', $variables);
1641
						}
1642
					}
1643
1644
					// A function to process?
1645
					elseif (isset($test['message_function']))
1646
					{
1647
						// Find out if there are actually errors.
1648
						$found_errors = false;
1649
						while ($row = $smcFunc['db_fetch_assoc']($request))
1650
							$found_errors |= $test['message_function']($row);
1651
					}
1652
1653
					// Actually have something to fix?
1654
					if ($found_errors)
1655
						$to_fix[] = $error_type;
1656
				}
1657
1658
				// We want to fix, we need to fix - so work out what exactly to do!
1659
				else
1660
				{
1661
					// Are we simply getting a collection of ids?
1662
					if (isset($test['fix_collect']))
1663
					{
1664
						$ids = array();
1665
						while ($row = $smcFunc['db_fetch_assoc']($request))
1666
							$ids[] = $row[$test['fix_collect']['index']];
1667
						if (!empty($ids))
1668
						{
1669
							// Fix it!
1670
							$test['fix_collect']['process']($ids);
1671
						}
1672
					}
1673
1674
					// Simply executing a fix it query?
1675
					elseif (isset($test['fix_it_query']))
1676
						$smcFunc['db_query']('',
1677
							$test['fix_it_query'],
1678
							array(
1679
							)
1680
						);
1681
1682
					// Do we have some processing to do?
1683
					elseif (isset($test['fix_processing']))
1684
					{
1685
						while ($row = $smcFunc['db_fetch_assoc']($request))
1686
							$test['fix_processing']($row);
1687
					}
1688
1689
					// What about the full set of processing?
1690
					elseif (isset($test['fix_full_processing']))
1691
						$test['fix_full_processing']($request);
1692
1693
					// Do we have other things we need to fix as a result?
1694
					if (!empty($test['force_fix']))
1695
					{
1696
						foreach ($test['force_fix'] as $item)
1697
							if (!in_array($item, $to_fix))
1698
								$to_fix[] = $item;
1699
					}
1700
				}
1701
			}
1702
1703
			// Free the result.
1704
			$smcFunc['db_free_result']($request);
1705
			// Keep memory down.
1706
			$db_cache = array();
1707
1708
			// Are we done yet?
1709
			if (isset($test['substeps']))
1710
			{
1711
				$_GET['substep'] += $step_size;
1712
				// Not done?
1713
				if ($_GET['substep'] <= $step_max)
1714
				{
1715
					pauseRepairProcess($to_fix, $error_type, $step_max);
0 ignored issues
show
Bug introduced by
The variable $step_max does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1716
				}
1717
				else
1718
					$done = true;
1719
			}
1720
			else
1721
				$done = true;
1722
1723
			// Don't allow more than 1000 queries at a time.
1724
			if ($total_queries >= 1000)
1725
				pauseRepairProcess($to_fix, $error_type, $step_max, true);
1726
		}
1727
1728
		// Keep going.
1729
		$_GET['step']++;
1730
		$_GET['substep'] = 0;
1731
1732
		$to_fix = array_unique($to_fix);
1733
1734
		// If we're doing fixes and this needed a fix and we're all done then don't do it again.
1735
		if ($do_fix)
1736
		{
1737
			$key = array_search($error_type, $to_fix);
1738
			if ($key !== false && isset($to_fix[$key]))
1739
				unset($to_fix[$key]);
1740
		}
1741
1742
		// Are we done?
1743
		pauseRepairProcess($to_fix, $error_type);
1744
	}
1745
1746
	// Restore the cache.
1747
	$db_cache = $db_temp_cache;
1748
1749
	return $to_fix;
1750
}
1751
1752
/**
1753
 * Create a salvage area for repair purposes, if one doesn't already exist.
1754
 * Uses the forum's default language, and checks based on that name.
1755
 */
1756
function createSalvageArea()
1757
{
1758
	global $txt, $language, $salvageBoardID, $salvageCatID, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1759
	static $createOnce = false;
1760
1761
	// Have we already created it?
1762
	if ($createOnce)
1763
		return;
1764
	else
1765
		$createOnce = true;
1766
1767
	// Back to the forum's default language.
1768
	loadLanguage('Admin', $language);
1769
1770
	// Check to see if a 'Salvage Category' exists, if not => insert one.
1771
	$result = $smcFunc['db_query']('', '
1772
		SELECT id_cat
1773
		FROM {db_prefix}categories
1774
		WHERE name = {string:cat_name}
1775
		LIMIT 1',
1776
		array(
1777
			'cat_name' => $txt['salvaged_category_name'],
1778
		)
1779
	);
1780 View Code Duplication
	if ($smcFunc['db_num_rows']($result) != 0)
1781
		list ($salvageCatID) = $smcFunc['db_fetch_row']($result);
1782
	$smcFunc['db_free_result']($result);
1783
1784
	if (empty($salvageCatID))
1785
	{
1786
		$salvageCatID = $smcFunc['db_insert']('',
1787
			'{db_prefix}categories',
1788
			array('name' => 'string-255', 'cat_order' => 'int', 'description' => 'string-255'),
1789
			array($txt['salvaged_category_name'], -1, $txt['salvaged_category_description']),
1790
			array('id_cat'),
1791
			1
1792
		);
1793
1794
		if ($smcFunc['db_affected_rows']() <= 0)
1795
		{
1796
			loadLanguage('Admin');
1797
			fatal_lang_error('salvaged_category_error', false);
1798
		}
1799
	}
1800
1801
	// Check to see if a 'Salvage Board' exists, if not => insert one.
1802
	$result = $smcFunc['db_query']('', '
1803
		SELECT id_board
1804
		FROM {db_prefix}boards
1805
		WHERE id_cat = {int:id_cat}
1806
			AND name = {string:board_name}
1807
		LIMIT 1',
1808
		array(
1809
			'id_cat' => $salvageCatID,
1810
			'board_name' => $txt['salvaged_board_name'],
1811
		)
1812
	);
1813 View Code Duplication
	if ($smcFunc['db_num_rows']($result) != 0)
1814
		list ($salvageBoardID) = $smcFunc['db_fetch_row']($result);
1815
	$smcFunc['db_free_result']($result);
1816
1817
	if (empty($salvageBoardID))
1818
	{
1819
		$salvageBoardID = $smcFunc['db_insert']('',
1820
			'{db_prefix}boards',
1821
			array('name' => 'string-255', 'description' => 'string-255', 'id_cat' => 'int', 'member_groups' => 'string', 'board_order' => 'int', 'redirect' => 'string'),
1822
			array($txt['salvaged_board_name'], $txt['salvaged_board_description'], $salvageCatID, '1', -1, ''),
1823
			array('id_board'),
1824
			1
1825
		);
1826
1827
		if ($smcFunc['db_affected_rows']() <= 0)
1828
		{
1829
			loadLanguage('Admin');
1830
			fatal_lang_error('salvaged_board_error', false);
1831
		}
1832
	}
1833
1834
	// Restore the user's language.
1835
	loadLanguage('Admin');
1836
}
1837
1838
?>