1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Forum maintenance. Important stuff. |
5
|
|
|
* |
6
|
|
|
* @package ElkArte Forum |
7
|
|
|
* @copyright ElkArte Forum contributors |
8
|
|
|
* @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
9
|
|
|
* |
10
|
|
|
* This file contains code covered by: |
11
|
|
|
* copyright: 2011 Simple Machines (http://www.simplemachines.org) |
12
|
|
|
* |
13
|
|
|
* @version 2.0 dev |
14
|
|
|
* |
15
|
|
|
*/ |
16
|
|
|
|
17
|
|
|
namespace ElkArte\AdminController; |
18
|
|
|
|
19
|
|
|
use ElkArte\AbstractController; |
20
|
|
|
use ElkArte\Action; |
21
|
|
|
use ElkArte\Cache\Cache; |
22
|
|
|
use ElkArte\Debug; |
23
|
|
|
use ElkArte\EventManager; |
24
|
|
|
use ElkArte\Exceptions\Exception; |
25
|
|
|
use ElkArte\Helper\DataValidator; |
26
|
|
|
use ElkArte\Http\FtpConnection; |
27
|
|
|
use ElkArte\Languages\Txt; |
28
|
|
|
use ElkArte\User; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Entry point class for all maintenance, routine, members, database, |
32
|
|
|
* attachments, topics and hooks |
33
|
|
|
* |
34
|
|
|
* @package Maintenance |
35
|
|
|
*/ |
36
|
|
|
class Maintenance extends AbstractController |
37
|
|
|
{ |
38
|
|
|
/** @var int Maximum topic counter */ |
39
|
|
|
public $max_topics; |
40
|
|
|
|
41
|
|
|
/** @var int How many actions to take for a maintenance actions */ |
42
|
|
|
public $increment; |
43
|
|
|
|
44
|
|
|
/** @var int Total steps for a given maintenance action */ |
45
|
|
|
public $total_steps; |
46
|
|
|
|
47
|
|
|
/** @var int reStart pointer for paused maintenance actions */ |
48
|
|
|
public $start; |
49
|
|
|
|
50
|
|
|
/** @var int Loop counter for paused maintenance actions */ |
51
|
|
|
public $step; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Main dispatcher, the maintenance access point. |
55
|
|
|
* |
56
|
|
|
* What it does: |
57
|
|
|
* |
58
|
|
|
* - This, as usual, checks permissions, loads language files, |
59
|
|
|
* and forwards to the actual workers. |
60
|
|
|
* |
61
|
|
|
* @see AbstractController::action_index |
62
|
|
|
*/ |
63
|
|
|
public function action_index() |
64
|
|
|
{ |
65
|
|
|
global $txt, $context; |
66
|
|
|
|
67
|
|
|
// You absolutely must be an admin by here! |
68
|
|
|
isAllowedTo('admin_forum'); |
69
|
|
|
|
70
|
|
|
// Need something to talk about? |
71
|
|
|
Txt::load('Maintenance'); |
72
|
|
|
theme()->getTemplates()->load('Maintenance'); |
73
|
|
|
|
74
|
|
|
// This uses admin tabs - as it should! |
75
|
|
|
// Create the tabs |
76
|
|
|
$context[$context['admin_menu_name']]['object']->prepareTabData([ |
77
|
|
|
'title' => 'maintain_title', |
78
|
|
|
'description' => 'maintain_info', |
79
|
|
|
'class' => 'i-cog'] |
80
|
|
|
); |
81
|
|
|
|
82
|
|
|
// So many things you can do - but frankly I won't let you - just these! |
83
|
|
|
$subActions = array( |
84
|
|
|
'routine' => array( |
85
|
|
|
'controller' => $this, |
86
|
|
|
'function' => 'action_routine', |
87
|
|
|
'activities' => array( |
88
|
|
|
'version' => 'action_version_display', |
89
|
|
|
'repair' => 'action_repair_display', |
90
|
|
|
'recount' => 'action_recount_display', |
91
|
|
|
'logs' => 'action_logs_display', |
92
|
|
|
'cleancache' => 'action_cleancache_display', |
93
|
|
|
), |
94
|
|
|
), |
95
|
|
|
'database' => array( |
96
|
|
|
'controller' => $this, |
97
|
|
|
'function' => 'action_database', |
98
|
|
|
'activities' => array( |
99
|
|
|
'optimize' => 'action_optimize_display', |
100
|
|
|
'backup' => 'action_backup_display', |
101
|
|
|
'convertmsgbody' => 'action_convertmsgbody_display', |
102
|
|
|
), |
103
|
|
|
), |
104
|
|
|
'members' => array( |
105
|
|
|
'controller' => $this, |
106
|
|
|
'function' => 'action_members', |
107
|
|
|
'activities' => array( |
108
|
|
|
'reattribute' => 'action_reattribute_display', |
109
|
|
|
'purgeinactive' => 'action_purgeinactive_display', |
110
|
|
|
'recountposts' => 'action_recountposts_display', |
111
|
|
|
), |
112
|
|
|
), |
113
|
|
|
'topics' => array( |
114
|
|
|
'controller' => $this, |
115
|
|
|
'function' => 'action_topics', |
116
|
|
|
'activities' => array( |
117
|
|
|
'massmove' => 'action_massmove_display', |
118
|
|
|
'pruneold' => 'action_pruneold_display', |
119
|
|
|
), |
120
|
|
|
), |
121
|
|
|
'hooks' => array( |
122
|
|
|
'controller' => $this, |
123
|
|
|
'function' => 'action_hooks', |
124
|
|
|
), |
125
|
|
|
'attachments' => array( |
126
|
|
|
'controller' => ManageAttachments::class, |
127
|
|
|
'function' => 'action_maintenance', |
128
|
|
|
), |
129
|
|
|
); |
130
|
|
|
|
131
|
|
|
// Set up the action handler |
132
|
|
|
$action = new Action('manage_maintenance'); |
133
|
|
|
|
134
|
|
|
// Yep, sub-action time and call integrate_sa_manage_maintenance as well |
135
|
|
|
$subAction = $action->initialize($subActions, 'routine'); |
136
|
|
|
|
137
|
|
|
// Doing something special, does it exist? |
138
|
|
|
$activity = $this->_req->getQuery('activity', 'trim|strval', ''); |
139
|
|
|
|
140
|
|
|
// Set a few things. |
141
|
|
|
$context[$context['admin_menu_name']]['current_subsection'] = $subAction; |
142
|
|
|
$context['page_title'] = $txt['maintain_title']; |
143
|
|
|
$context['sub_action'] = $subAction; |
144
|
|
|
|
145
|
|
|
// Finally fall through to what we are doing. |
146
|
|
|
$action->dispatch($subAction); |
147
|
|
|
|
148
|
|
|
// Any special activity defined, then go to it. |
149
|
|
|
if (isset($subActions[$subAction]['activities'][$activity])) |
150
|
|
|
{ |
151
|
|
|
if (is_string($subActions[$subAction]['activities'][$activity]) && method_exists($this, $subActions[$subAction]['activities'][$activity])) |
152
|
|
|
{ |
153
|
|
|
$this->{$subActions[$subAction]['activities'][$activity]}(); |
154
|
|
|
} |
155
|
|
|
elseif (is_string($subActions[$subAction]['activities'][$activity])) |
156
|
|
|
{ |
157
|
|
|
$subActions[$subAction]['activities'][$activity](); |
158
|
|
|
} |
159
|
|
|
elseif (is_array($subActions[$subAction]['activities'][$activity])) |
160
|
|
|
{ |
161
|
|
|
$activity_obj = new $subActions[$subAction]['activities'][$activity]['class'](); |
162
|
|
|
$activity_obj->{$subActions[$subAction]['activities'][$activity]['method']}(); |
163
|
|
|
} |
164
|
|
|
else |
165
|
|
|
{ |
166
|
|
|
$subActions[$subAction]['activities'][$activity](); |
167
|
|
|
} |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
// Create a maintenance token. Kinda hard to do it any other way. |
171
|
|
|
createToken('admin-maint'); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* Supporting function for the routine maintenance area. |
176
|
|
|
* |
177
|
|
|
* @event integrate_routine_maintenance, passed $context['routine_actions'] array to allow |
178
|
|
|
* addons to add more options |
179
|
|
|
* @uses Template Maintenance, sub template maintain_routine |
180
|
|
|
*/ |
181
|
|
|
public function action_routine() |
182
|
|
|
{ |
183
|
|
|
global $context, $txt; |
184
|
|
|
|
185
|
|
|
if ($this->_req->compareQuery('done', 'recount', 'trim|strval')) |
186
|
|
|
{ |
187
|
|
|
$context['maintenance_finished'] = $txt['maintain_recount']; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
// set up the sub-template |
191
|
|
|
$context['sub_template'] = 'maintain_routine'; |
192
|
|
|
$context['routine_actions'] = array( |
193
|
|
|
'version' => array( |
194
|
|
|
'url' => getUrl('admin', ['action' => 'admin', 'area' => 'maintain', 'sa' => 'routine', 'activity' => 'version']), |
195
|
|
|
'title' => $txt['maintain_version'], |
196
|
|
|
'description' => $txt['maintain_version_info'], |
197
|
|
|
'submit' => $txt['maintain_run_now'], |
198
|
|
|
'hidden' => array( |
199
|
|
|
'session_var' => 'session_id', |
200
|
|
|
) |
201
|
|
|
), |
202
|
|
|
'repair' => array( |
203
|
|
|
'url' => getUrl('admin', ['action' => 'admin', 'area' => 'repairboards']), |
204
|
|
|
'title' => $txt['maintain_errors'], |
205
|
|
|
'description' => $txt['maintain_errors_info'], |
206
|
|
|
'submit' => $txt['maintain_run_now'], |
207
|
|
|
'hidden' => array( |
208
|
|
|
'session_var' => 'session_id', |
209
|
|
|
'admin-maint_token_var' => 'admin-maint_token', |
210
|
|
|
) |
211
|
|
|
), |
212
|
|
|
'recount' => array( |
213
|
|
|
'url' => getUrl('admin', ['action' => 'admin', 'area' => 'maintain', 'sa' => 'routine', 'activity' => 'recount']), |
214
|
|
|
'title' => $txt['maintain_recount'], |
215
|
|
|
'description' => $txt['maintain_recount_info'], |
216
|
|
|
'submit' => $txt['maintain_run_now'], |
217
|
|
|
'hidden' => array( |
218
|
|
|
'session_var' => 'session_id', |
219
|
|
|
'admin-maint_token_var' => 'admin-maint_token', |
220
|
|
|
) |
221
|
|
|
), |
222
|
|
|
'logs' => array( |
223
|
|
|
'url' => getUrl('admin', ['action' => 'admin', 'area' => 'maintain', 'sa' => 'routine', 'activity' => 'logs']), |
224
|
|
|
'title' => $txt['maintain_logs'], |
225
|
|
|
'description' => $txt['maintain_logs_info'], |
226
|
|
|
'submit' => $txt['maintain_run_now'], |
227
|
|
|
'hidden' => array( |
228
|
|
|
'session_var' => 'session_id', |
229
|
|
|
'admin-maint_token_var' => 'admin-maint_token', |
230
|
|
|
) |
231
|
|
|
), |
232
|
|
|
'cleancache' => array( |
233
|
|
|
'url' => getUrl('admin', ['action' => 'admin', 'area' => 'maintain', 'sa' => 'routine', 'activity' => 'cleancache']), |
234
|
|
|
'title' => $txt['maintain_cache'], |
235
|
|
|
'description' => $txt['maintain_cache_info'], |
236
|
|
|
'submit' => $txt['maintain_run_now'], |
237
|
|
|
'hidden' => array( |
238
|
|
|
'session_var' => 'session_id', |
239
|
|
|
'admin-maint_token_var' => 'admin-maint_token', |
240
|
|
|
) |
241
|
|
|
), |
242
|
|
|
); |
243
|
|
|
|
244
|
|
|
call_integration_hook('integrate_routine_maintenance', array(&$context['routine_actions'])); |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Supporting function for the members maintenance area. |
249
|
|
|
*/ |
250
|
|
|
public function action_members() |
251
|
|
|
{ |
252
|
|
|
global $context, $txt; |
253
|
|
|
|
254
|
|
|
require_once(SUBSDIR . '/Membergroups.subs.php'); |
255
|
|
|
|
256
|
|
|
// Get all membergroups - for deleting members and the like. |
257
|
|
|
$context['membergroups'] = getBasicMembergroupData(array('all')); |
258
|
|
|
|
259
|
|
|
// Show that we completed this action |
260
|
|
|
if ($this->_req->compareQuery('done', 'recountposts', 'trim|strval')) |
261
|
|
|
{ |
262
|
|
|
$context['maintenance_finished'] = array( |
263
|
|
|
'errors' => array(sprintf($txt['maintain_done'], $txt['maintain_recountposts'])), |
264
|
|
|
); |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
loadJavascriptFile('suggest.js', array('defer' => true)); |
268
|
|
|
|
269
|
|
|
// Set up the sub-template |
270
|
|
|
$context['sub_template'] = 'maintain_members'; |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* Supporting function for the topics maintenance area. |
275
|
|
|
* |
276
|
|
|
* @event integrate_topics_maintenance, passed $context['topics_actions'] to allow addons |
277
|
|
|
* to add additonal topic maintance functions |
278
|
|
|
* @uses GenericBoards template, sub template maintain_topics |
279
|
|
|
*/ |
280
|
|
|
public function action_topics() |
281
|
|
|
{ |
282
|
|
|
global $context, $txt; |
283
|
|
|
|
284
|
|
|
require_once(SUBSDIR . '/Boards.subs.php'); |
285
|
|
|
|
286
|
|
|
// Let's load up the boards in case they are useful. |
287
|
|
|
$context += getBoardList(array('not_redirection' => true)); |
288
|
|
|
|
289
|
|
|
// Include a list of boards per category for easy toggling. |
290
|
|
|
foreach ($context['categories'] as $cat => &$category) |
291
|
|
|
{ |
292
|
|
|
$context['boards_in_category'][$cat] = count($category['boards']); |
293
|
|
|
$category['child_ids'] = array_keys($category['boards']); |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
// @todo Hacky! |
297
|
|
|
$txt['choose_board'] = $txt['maintain_old_all']; |
298
|
|
|
$context['boards_check_all'] = true; |
299
|
|
|
theme()->getTemplates()->load('GenericBoards'); |
300
|
|
|
|
301
|
|
|
$context['topics_actions'] = array( |
302
|
|
|
'pruneold' => array( |
303
|
|
|
'url' => getUrl('admin', ['action' => 'admin', 'area' => 'maintain', 'sa' => 'topics', 'activity' => 'pruneold']), |
304
|
|
|
'title' => $txt['maintain_old'], |
305
|
|
|
'submit' => $txt['maintain_old_remove'], |
306
|
|
|
'confirm' => $txt['maintain_old_confirm'], |
307
|
|
|
'hidden' => array( |
308
|
|
|
'session_var' => 'session_id', |
309
|
|
|
'admin-maint_token_var' => 'admin-maint_token', |
310
|
|
|
) |
311
|
|
|
), |
312
|
|
|
'massmove' => array( |
313
|
|
|
'url' => getUrl('admin', ['action' => 'admin', 'area' => 'maintain', 'sa' => 'topics', 'activity' => 'massmove']), |
314
|
|
|
'title' => $txt['move_topics_maintenance'], |
315
|
|
|
'submit' => $txt['move_topics_now'], |
316
|
|
|
'confirm' => $txt['move_topics_confirm'], |
317
|
|
|
'hidden' => array( |
318
|
|
|
'session_var' => 'session_id', |
319
|
|
|
'admin-maint_token_var' => 'admin-maint_token', |
320
|
|
|
) |
321
|
|
|
), |
322
|
|
|
); |
323
|
|
|
|
324
|
|
|
call_integration_hook('integrate_topics_maintenance', array(&$context['topics_actions'])); |
325
|
|
|
|
326
|
|
|
if ($this->_req->compareQuery('done', 'purgeold', 'trim|strval')) |
327
|
|
|
{ |
328
|
|
|
$context['maintenance_finished'] = array( |
329
|
|
|
'errors' => array(sprintf($txt['maintain_done'], $txt['maintain_old'])), |
330
|
|
|
); |
331
|
|
|
} |
332
|
|
|
elseif ($this->_req->compareQuery('done', 'massmove', 'trim|strval')) |
333
|
|
|
{ |
334
|
|
|
$context['maintenance_finished'] = array( |
335
|
|
|
'errors' => array(sprintf($txt['maintain_done'], $txt['move_topics_maintenance'])), |
336
|
|
|
); |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
// Set up the sub-template |
340
|
|
|
$context['sub_template'] = 'maintain_topics'; |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
/** |
344
|
|
|
* Find and try to fix all errors on the forum. |
345
|
|
|
* |
346
|
|
|
* - Forwards to repair boards controller. |
347
|
|
|
*/ |
348
|
|
|
public function action_repair_display() |
349
|
|
|
{ |
350
|
|
|
// Honestly, this should be done in the sub function. |
351
|
|
|
validateToken('admin-maint'); |
352
|
|
|
|
353
|
|
|
$controller = new RepairBoards(new EventManager()); |
354
|
|
|
$controller->setUser(User::$info); |
355
|
|
|
$controller->pre_dispatch(); |
356
|
|
|
$controller->action_repairboards(); |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
/** |
360
|
|
|
* Wipes the current cache entries as best it can. |
361
|
|
|
* |
362
|
|
|
* - This only applies to our own cache entries, opcache and data. |
363
|
|
|
* - This action, like other maintenance tasks, may be called automatically |
364
|
|
|
* by the task scheduler or manually by the admin in Maintenance area. |
365
|
|
|
*/ |
366
|
|
|
public function action_cleancache_display() |
367
|
|
|
{ |
368
|
|
|
global $context, $txt; |
369
|
|
|
|
370
|
|
|
checkSession(); |
371
|
|
|
validateToken('admin-maint'); |
372
|
|
|
|
373
|
|
|
// Just wipe the whole cache directory! |
374
|
|
|
Cache::instance()->clean(); |
375
|
|
|
|
376
|
|
|
// Change the PWA stale so it will refresh (if enabled) |
377
|
|
|
setPWACacheStale(true); |
378
|
|
|
|
379
|
|
|
$context['maintenance_finished'] = $txt['maintain_cache']; |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
/** |
383
|
|
|
* Empties all unimportant logs. |
384
|
|
|
* |
385
|
|
|
* - This action may be called periodically, by the tasks scheduler, |
386
|
|
|
* or manually by the admin in Maintenance area. |
387
|
|
|
*/ |
388
|
|
|
public function action_logs_display() |
389
|
|
|
{ |
390
|
|
|
global $context, $txt; |
391
|
|
|
|
392
|
|
|
require_once(SUBSDIR . '/Maintenance.subs.php'); |
393
|
|
|
|
394
|
|
|
checkSession(); |
395
|
|
|
validateToken('admin-maint'); |
396
|
|
|
|
397
|
|
|
// Maintenance time was scheduled! |
398
|
|
|
// When there is no intelligent life on this planet. |
399
|
|
|
// Apart from me, I mean. |
400
|
|
|
flushLogTables(); |
401
|
|
|
|
402
|
|
|
updateSettings(array('search_pointer' => 0)); |
403
|
|
|
|
404
|
|
|
$context['maintenance_finished'] = $txt['maintain_logs']; |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* Convert the column "body" of the table {db_prefix}messages from TEXT to |
409
|
|
|
* MEDIUMTEXT and vice versa. |
410
|
|
|
* |
411
|
|
|
* What it does: |
412
|
|
|
* |
413
|
|
|
* - It requires the admin_forum permission. |
414
|
|
|
* - This is needed only for MySQL. |
415
|
|
|
* - During the conversion from MEDIUMTEXT to TEXT it check if any of the |
416
|
|
|
* posts exceed the TEXT length and if so it aborts. |
417
|
|
|
* - This action is linked from the maintenance screen (if it's applicable). |
418
|
|
|
* - Accessed by ?action=admin;area=maintain;sa=database;activity=convertmsgbody. |
419
|
|
|
* |
420
|
|
|
* @uses the convert_msgbody sub template of the Admin template. |
421
|
|
|
*/ |
422
|
|
|
public function action_convertmsgbody_display() |
423
|
|
|
{ |
424
|
|
|
global $context, $txt, $modSettings, $time_start; |
425
|
|
|
|
426
|
|
|
// Show me your badge! |
427
|
|
|
isAllowedTo('admin_forum'); |
428
|
|
|
$db = database(); |
429
|
|
|
|
430
|
|
|
if ($db->supportMediumtext() === false) |
|
|
|
|
431
|
|
|
{ |
432
|
|
|
return; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
$body_type = ''; |
436
|
|
|
|
437
|
|
|
// Find the body column "type" from the message table |
438
|
|
|
$colData = getMessageTableColumns(); |
439
|
|
|
foreach ($colData as $column) |
440
|
|
|
{ |
441
|
|
|
if ($column['name'] === 'body') |
442
|
|
|
{ |
443
|
|
|
$body_type = $column['type']; |
444
|
|
|
break; |
445
|
|
|
} |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
$context['convert_to'] = $body_type === 'text' ? 'mediumtext' : 'text'; |
449
|
|
|
if ($body_type === 'text' || isset($this->_req->post->do_conversion)) |
450
|
|
|
{ |
451
|
|
|
checkSession(); |
452
|
|
|
validateToken('admin-maint'); |
453
|
|
|
|
454
|
|
|
// Make it longer so we can do their limit. |
455
|
|
|
if ($body_type === 'text') |
456
|
|
|
{ |
457
|
|
|
resizeMessageTableBody('mediumtext'); |
458
|
|
|
} |
459
|
|
|
// Shorten the column so we can have a bit (literally per record) less space occupied |
460
|
|
|
else |
461
|
|
|
{ |
462
|
|
|
resizeMessageTableBody('text'); |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
$colData = getMessageTableColumns(); |
466
|
|
|
foreach ($colData as $column) |
467
|
|
|
{ |
468
|
|
|
if ($column['name'] === 'body') |
469
|
|
|
{ |
470
|
|
|
$body_type = $column['type']; |
471
|
|
|
} |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
$context['maintenance_finished'] = $txt[$context['convert_to'] . '_title']; |
475
|
|
|
$context['convert_to'] = $body_type === 'text' ? 'mediumtext' : 'text'; |
476
|
|
|
$context['convert_to_suggest'] = ($body_type !== 'text' && !empty($modSettings['max_messageLength']) && $modSettings['max_messageLength'] < 65536); |
477
|
|
|
|
478
|
|
|
return; |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
if (!isset($this->_req->post->do_conversion) || isset($this->_req->post->cont)) |
482
|
|
|
{ |
483
|
|
|
checkSession(); |
484
|
|
|
if (empty($this->_req->query->start)) |
485
|
|
|
{ |
486
|
|
|
validateToken('admin-maint'); |
487
|
|
|
} |
488
|
|
|
else |
489
|
|
|
{ |
490
|
|
|
validateToken('admin-convertMsg'); |
491
|
|
|
} |
492
|
|
|
|
493
|
|
|
$context['page_title'] = $txt['not_done_title']; |
494
|
|
|
$context['continue_post_data'] = ''; |
495
|
|
|
$context['continue_countdown'] = 3; |
496
|
|
|
$context['sub_template'] = 'not_done'; |
497
|
|
|
|
498
|
|
|
$increment = 500; |
499
|
|
|
$id_msg_exceeding = isset($this->_req->post->id_msg_exceeding) ? explode(',', $this->_req->post->id_msg_exceeding) : array(); |
500
|
|
|
$max_msgs = countMessages(); |
501
|
|
|
$start = $this->_req->query->start; |
502
|
|
|
|
503
|
|
|
// Try for as much time as possible. |
504
|
|
|
detectServer()->setTimeLimit(600); |
505
|
|
|
while ($start < $max_msgs) |
506
|
|
|
{ |
507
|
|
|
$id_msg_exceeding = detectExceedingMessages($start, $increment); |
508
|
|
|
|
509
|
|
|
$start += $increment; |
510
|
|
|
|
511
|
|
|
if (microtime(true) - $time_start > 3) |
512
|
|
|
{ |
513
|
|
|
createToken('admin-convertMsg'); |
514
|
|
|
$context['continue_post_data'] = ' |
515
|
|
|
<input type="hidden" name="' . $context['admin-convertMsg_token_var'] . '" value="' . $context['admin-convertMsg_token'] . '" /> |
516
|
|
|
<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '" /> |
517
|
|
|
<input type="hidden" name="id_msg_exceeding" value="' . implode(',', $id_msg_exceeding) . '" />'; |
518
|
|
|
$context['continue_get_data'] = '?action=admin;area=maintain;sa=database;activity=convertmsgbody;start=' . $start; |
519
|
|
|
$context['continue_percent'] = round(100 * $start / $max_msgs); |
520
|
|
|
$context['not_done_title'] = $txt['not_done_title'] . ' (' . $context['continue_percent'] . '%)'; |
521
|
|
|
|
522
|
|
|
return; |
523
|
|
|
} |
524
|
|
|
} |
525
|
|
|
|
526
|
|
|
createToken('admin-maint'); |
527
|
|
|
$context['page_title'] = $txt[$context['convert_to'] . '_title']; |
528
|
|
|
$context['sub_template'] = 'convert_msgbody'; |
529
|
|
|
|
530
|
|
|
if (!empty($id_msg_exceeding)) |
531
|
|
|
{ |
532
|
|
|
if (count($id_msg_exceeding) > 100) |
533
|
|
|
{ |
534
|
|
|
$query_msg = array_slice($id_msg_exceeding, 0, 100); |
535
|
|
|
$context['exceeding_messages_morethan'] = sprintf($txt['exceeding_messages_morethan'], count($id_msg_exceeding)); |
536
|
|
|
} |
537
|
|
|
else |
538
|
|
|
{ |
539
|
|
|
$query_msg = $id_msg_exceeding; |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
$context['exceeding_messages'] = getExceedingMessages($query_msg); |
543
|
|
|
} |
544
|
|
|
} |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
/** |
548
|
|
|
* Optimizes all tables in the database and lists how much was saved. |
549
|
|
|
* |
550
|
|
|
* What it does: |
551
|
|
|
* |
552
|
|
|
* - It requires the admin_forum permission. |
553
|
|
|
* - It shows as the maintain_forum admin area. |
554
|
|
|
* - It is accessed from ?action=admin;area=maintain;sa=database;activity=optimize. |
555
|
|
|
* - It also updates the optimize scheduled task such that the tables are not automatically optimized again too soon. |
556
|
|
|
*/ |
557
|
|
|
public function action_optimize_display() |
558
|
|
|
{ |
559
|
|
|
global $txt, $context; |
560
|
|
|
|
561
|
|
|
isAllowedTo('admin_forum'); |
562
|
|
|
|
563
|
|
|
// Some validation |
564
|
|
|
checkSession('post'); |
565
|
|
|
validateToken('admin-maint'); |
566
|
|
|
|
567
|
|
|
ignore_user_abort(true); |
568
|
|
|
|
569
|
|
|
require_once(SUBSDIR . '/Maintenance.subs.php'); |
570
|
|
|
|
571
|
|
|
$context['page_title'] = $txt['database_optimize']; |
572
|
|
|
$context['sub_template'] = 'optimize'; |
573
|
|
|
|
574
|
|
|
$tables = getElkTables(); |
575
|
|
|
|
576
|
|
|
// If there aren't any tables then I believe that would mean the world has exploded... |
577
|
|
|
$context['num_tables'] = count($tables); |
578
|
|
|
if ($context['num_tables'] === 0) |
579
|
|
|
{ |
580
|
|
|
throw new Exception('You appear to be running ElkArte in a flat file mode... fantastic!', false); |
581
|
|
|
} |
582
|
|
|
|
583
|
|
|
// For each table.... |
584
|
|
|
$context['optimized_tables'] = array(); |
585
|
|
|
$db_table = db_table(); |
586
|
|
|
|
587
|
|
|
foreach ($tables as $table) |
588
|
|
|
{ |
589
|
|
|
// Optimize the table! We use backticks here because it might be a custom table. |
590
|
|
|
$data_freed = $db_table->optimize($table['table_name']); |
591
|
|
|
|
592
|
|
|
if ($data_freed > 0) |
593
|
|
|
{ |
594
|
|
|
$context['optimized_tables'][] = array( |
595
|
|
|
'name' => $table['table_name'], |
596
|
|
|
'data_freed' => $data_freed, |
597
|
|
|
); |
598
|
|
|
} |
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
// Number of tables, etc.... |
602
|
|
|
$txt['database_numb_tables'] = sprintf($txt['database_numb_tables'], $context['num_tables']); |
603
|
|
|
$context['num_tables_optimized'] = count($context['optimized_tables']); |
604
|
|
|
|
605
|
|
|
// Check that we don't auto optimise again too soon! |
606
|
|
|
require_once(SUBSDIR . '/ScheduledTasks.subs.php'); |
607
|
|
|
calculateNextTrigger('auto_optimize', true); |
608
|
|
|
} |
609
|
|
|
|
610
|
|
|
/** |
611
|
|
|
* Recount many forum totals that can be recounted automatically without harm. |
612
|
|
|
* |
613
|
|
|
* What it does: |
614
|
|
|
* |
615
|
|
|
* - it requires the admin_forum permission. |
616
|
|
|
* - It shows the maintain_forum admin area. |
617
|
|
|
* - The function redirects back to ?action=admin;area=maintain when complete. |
618
|
|
|
* - It is accessed via ?action=admin;area=maintain;sa=database;activity=recount. |
619
|
|
|
* |
620
|
|
|
* Totals recounted: |
621
|
|
|
* - fixes for topics with wrong num_replies. |
622
|
|
|
* - updates for num_posts and num_topics of all boards. |
623
|
|
|
* - recounts personal_messages but not unread_messages. |
624
|
|
|
* - repairs messages pointing to boards with topics pointing to other boards. |
625
|
|
|
* - updates the last message posted in boards and children. |
626
|
|
|
* - updates member count, latest member, topic count, and message count. |
627
|
|
|
*/ |
628
|
|
|
public function action_recount_display() |
629
|
|
|
{ |
630
|
|
|
global $txt, $context, $modSettings, $time_start; |
631
|
|
|
|
632
|
|
|
isAllowedTo('admin_forum'); |
633
|
|
|
checkSession(); |
634
|
|
|
|
635
|
|
|
// Functions |
636
|
|
|
require_once(SUBSDIR . '/Maintenance.subs.php'); |
637
|
|
|
require_once(SUBSDIR . '/Topic.subs.php'); |
638
|
|
|
|
639
|
|
|
// Validate the request or the loop |
640
|
|
|
if (!isset($this->_req->query->step)) |
641
|
|
|
{ |
642
|
|
|
validateToken('admin-maint'); |
643
|
|
|
} |
644
|
|
|
else |
645
|
|
|
{ |
646
|
|
|
validateToken('admin-boardrecount'); |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
// For the loop template |
650
|
|
|
$context['page_title'] = $txt['not_done_title']; |
651
|
|
|
$context['continue_post_data'] = ''; |
652
|
|
|
$context['continue_countdown'] = 3; |
653
|
|
|
$context['sub_template'] = 'not_done'; |
654
|
|
|
|
655
|
|
|
// Try for as much time as possible. |
656
|
|
|
detectServer()->setTimeLimit(600); |
657
|
|
|
|
658
|
|
|
// Step the number of topics at a time so things don't time out... |
659
|
|
|
$this->max_topics = getMaxTopicID(); |
660
|
|
|
$this->increment = (int) min(max(50, ceil($this->max_topics / 4)), 2000); |
661
|
|
|
|
662
|
|
|
// An 8 step process, should be 12 for the admin |
663
|
|
|
$this->total_steps = 8; |
664
|
|
|
$this->start = $this->_req->getQuery('start', 'inval', 0); |
665
|
|
|
$this->step = $this->_req->getQuery('step', 'intval', 0); |
666
|
|
|
|
667
|
|
|
// Get each topic with a wrong reply count and fix it |
668
|
|
|
if (empty($this->step)) |
669
|
|
|
{ |
670
|
|
|
// let's just do some at a time, though. |
671
|
|
|
while ($this->start < $this->max_topics) |
672
|
|
|
{ |
673
|
|
|
recountApprovedMessages($this->start, $this->increment); |
674
|
|
|
recountUnapprovedMessages($this->start, $this->increment); |
675
|
|
|
$this->start += $this->increment; |
676
|
|
|
|
677
|
|
|
if (microtime(true) - $time_start > 3) |
678
|
|
|
{ |
679
|
|
|
$percent = round((100 * $this->start / $this->max_topics) / $this->total_steps); |
680
|
|
|
$this->_buildContinue($percent, 0); |
681
|
|
|
|
682
|
|
|
return; |
683
|
|
|
} |
684
|
|
|
} |
685
|
|
|
|
686
|
|
|
// Done with step 0, reset start for the next one |
687
|
|
|
$this->start = 0; |
688
|
|
|
} |
689
|
|
|
|
690
|
|
|
// Update the post count of each board. |
691
|
|
|
if ($this->step <= 1) |
692
|
|
|
{ |
693
|
|
|
if (empty($this->start)) |
694
|
|
|
{ |
695
|
|
|
resetBoardsCounter('num_posts'); |
696
|
|
|
} |
697
|
|
|
|
698
|
|
|
while ($this->start < $this->max_topics) |
699
|
|
|
{ |
700
|
|
|
// Recount the posts |
701
|
|
|
updateBoardsCounter('posts', $this->start, $this->increment); |
702
|
|
|
$this->start += $this->increment; |
703
|
|
|
|
704
|
|
|
if (microtime(true) - $time_start > 3) |
705
|
|
|
{ |
706
|
|
|
$percent = round((200 + 100 * $this->start / $this->max_topics) / $this->total_steps); |
707
|
|
|
$this->_buildContinue($percent, 1); |
708
|
|
|
|
709
|
|
|
return; |
710
|
|
|
} |
711
|
|
|
} |
712
|
|
|
|
713
|
|
|
// Done with step 1, reset start for the next one |
714
|
|
|
$this->start = 0; |
715
|
|
|
} |
716
|
|
|
|
717
|
|
|
// Update the topic count of each board. |
718
|
|
|
if ($this->step <= 2) |
719
|
|
|
{ |
720
|
|
|
if (empty($this->start)) |
721
|
|
|
{ |
722
|
|
|
resetBoardsCounter('num_topics'); |
723
|
|
|
} |
724
|
|
|
|
725
|
|
|
while ($this->start < $this->max_topics) |
726
|
|
|
{ |
727
|
|
|
updateBoardsCounter('topics', $this->start, $this->increment); |
728
|
|
|
$this->start += $this->increment; |
729
|
|
|
|
730
|
|
|
if (microtime(true) - $time_start > 3) |
731
|
|
|
{ |
732
|
|
|
$percent = round((300 + 100 * $this->start / $this->max_topics) / $this->total_steps); |
733
|
|
|
$this->_buildContinue($percent, 2); |
734
|
|
|
|
735
|
|
|
return; |
736
|
|
|
} |
737
|
|
|
} |
738
|
|
|
|
739
|
|
|
// Done with step 2, reset start for the next one |
740
|
|
|
$this->start = 0; |
741
|
|
|
} |
742
|
|
|
|
743
|
|
|
// Update the unapproved post count of each board. |
744
|
|
|
if ($this->step <= 3) |
745
|
|
|
{ |
746
|
|
|
if (empty($this->start)) |
747
|
|
|
{ |
748
|
|
|
resetBoardsCounter('unapproved_posts'); |
749
|
|
|
} |
750
|
|
|
|
751
|
|
|
while ($this->start < $this->max_topics) |
752
|
|
|
{ |
753
|
|
|
updateBoardsCounter('unapproved_posts', $this->start, $this->increment); |
754
|
|
|
$this->start += $this->increment; |
755
|
|
|
|
756
|
|
|
if (microtime(true) - $time_start > 3) |
757
|
|
|
{ |
758
|
|
|
$percent = round((400 + 100 * $this->start / $this->max_topics) / $this->total_steps); |
759
|
|
|
$this->_buildContinue($percent, 3); |
760
|
|
|
|
761
|
|
|
return; |
762
|
|
|
} |
763
|
|
|
} |
764
|
|
|
|
765
|
|
|
// Done with step 3, reset start for the next one |
766
|
|
|
$this->start = 0; |
767
|
|
|
} |
768
|
|
|
|
769
|
|
|
// Update the unapproved topic count of each board. |
770
|
|
|
if ($this->step <= 4) |
771
|
|
|
{ |
772
|
|
|
if (empty($this->start)) |
773
|
|
|
{ |
774
|
|
|
resetBoardsCounter('unapproved_topics'); |
775
|
|
|
} |
776
|
|
|
|
777
|
|
|
while ($this->start < $this->max_topics) |
778
|
|
|
{ |
779
|
|
|
updateBoardsCounter('unapproved_topics', $this->start, $this->increment); |
780
|
|
|
$this->start += $this->increment; |
781
|
|
|
|
782
|
|
|
if (microtime(true) - $time_start > 3) |
783
|
|
|
{ |
784
|
|
|
$percent = round((500 + 100 * $this->start / $this->max_topics) / $this->total_steps); |
785
|
|
|
$this->_buildContinue($percent, 4); |
786
|
|
|
|
787
|
|
|
return; |
788
|
|
|
} |
789
|
|
|
} |
790
|
|
|
|
791
|
|
|
// Done with step 4, reset start for the next one |
792
|
|
|
$this->start = 0; |
793
|
|
|
} |
794
|
|
|
|
795
|
|
|
// Get all members with wrong number of personal messages. |
796
|
|
|
if ($this->step <= 5) |
797
|
|
|
{ |
798
|
|
|
updatePersonalMessagesCounter(); |
799
|
|
|
|
800
|
|
|
// Done with step 5, reset start for the next one |
801
|
|
|
$this->start = 0; |
802
|
|
|
if (microtime(true) - $time_start > 3) |
803
|
|
|
{ |
804
|
|
|
$percent = round(700 / $this->total_steps); |
805
|
|
|
$this->_buildContinue($percent, 6); |
806
|
|
|
|
807
|
|
|
return; |
808
|
|
|
} |
809
|
|
|
} |
810
|
|
|
|
811
|
|
|
// Any messages pointing to the wrong board? |
812
|
|
|
if ($this->step <= 6) |
813
|
|
|
{ |
814
|
|
|
while ($this->start < $modSettings['maxMsgID']) |
815
|
|
|
{ |
816
|
|
|
updateMessagesBoardID($this->_req->query->start, $this->increment); |
817
|
|
|
$this->start += $this->increment; |
818
|
|
|
|
819
|
|
|
if (microtime(true) - $time_start > 3) |
820
|
|
|
{ |
821
|
|
|
$percent = round((700 + 100 * $this->start / $modSettings['maxMsgID']) / $this->total_steps); |
822
|
|
|
$this->_buildContinue($percent, 6); |
823
|
|
|
|
824
|
|
|
return; |
825
|
|
|
} |
826
|
|
|
} |
827
|
|
|
|
828
|
|
|
// Done with step 6, reset start for the next one |
829
|
|
|
$this->start = 0; |
830
|
|
|
} |
831
|
|
|
|
832
|
|
|
updateBoardsLastMessage(); |
833
|
|
|
|
834
|
|
|
// Update all the basic statistics. |
835
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
836
|
|
|
updateMemberStats(); |
837
|
|
|
require_once(SUBSDIR . '/Messages.subs.php'); |
838
|
|
|
updateMessageStats(); |
839
|
|
|
require_once(SUBSDIR . '/Topic.subs.php'); |
840
|
|
|
updateTopicStats(); |
841
|
|
|
|
842
|
|
|
// Finally, update the latest event times. |
843
|
|
|
require_once(SUBSDIR . '/ScheduledTasks.subs.php'); |
844
|
|
|
calculateNextTrigger(); |
845
|
|
|
|
846
|
|
|
// Ta-da |
847
|
|
|
redirectexit('action=admin;area=maintain;sa=routine;done=recount'); |
848
|
|
|
} |
849
|
|
|
|
850
|
|
|
/** |
851
|
|
|
* Helper function for teh recount process, build the continue values for |
852
|
|
|
* the template |
853
|
|
|
* |
854
|
|
|
* @param int $percent percent done |
855
|
|
|
* @param int $step step we are on |
856
|
|
|
*/ |
857
|
|
|
private function _buildContinue($percent, $step) |
858
|
|
|
{ |
859
|
|
|
global $context, $txt; |
860
|
|
|
|
861
|
|
|
createToken('admin-boardrecount'); |
862
|
|
|
|
863
|
|
|
$context['continue_post_data'] = ' |
864
|
|
|
<input type="hidden" name="' . $context['admin-boardrecount_token_var'] . '" value="' . $context['admin-boardrecount_token'] . '" /> |
865
|
|
|
<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '" />'; |
866
|
|
|
$context['continue_get_data'] = '?action=admin;area=maintain;sa=routine;activity=recount;step=' . $step . ';start=' . $this->start; |
867
|
|
|
$context['continue_percent'] = $percent; |
868
|
|
|
$context['not_done_title'] = $txt['not_done_title'] . ' (' . $context['continue_percent'] . '%)'; |
869
|
|
|
} |
870
|
|
|
|
871
|
|
|
/** |
872
|
|
|
* Re-attribute posts to the user sent from the maintenance page. |
873
|
|
|
*/ |
874
|
|
|
public function action_reattribute_display() |
875
|
|
|
{ |
876
|
|
|
global $context, $txt; |
877
|
|
|
|
878
|
|
|
checkSession(); |
879
|
|
|
|
880
|
|
|
$validator = new DataValidator(); |
881
|
|
|
$validator->sanitation_rules(array('posts' => 'empty', 'type' => 'trim', 'from_email' => 'trim', 'from_name' => 'trim', 'to' => 'trim')); |
882
|
|
|
$validator->validation_rules(array('from_email' => 'valid_email', 'from_name' => 'required', 'to' => 'required', 'type' => 'contains[name,email]')); |
883
|
|
|
$validator->validate($this->_req->post); |
884
|
|
|
|
885
|
|
|
// Fetch the Mr. Clean values |
886
|
|
|
$our_post = array_replace((array) $this->_req->post, $validator->validation_data()); |
887
|
|
|
|
888
|
|
|
// Do we have a valid set of options to continue? |
889
|
|
|
if (($our_post['type'] === 'name' && !empty($our_post['from_name'])) || ($our_post['type'] === 'email' && !$validator->validation_errors('from_email'))) |
890
|
|
|
{ |
891
|
|
|
// Find the member. |
892
|
|
|
require_once(SUBSDIR . '/Auth.subs.php'); |
893
|
|
|
$members = findMembers($our_post['to']); |
894
|
|
|
|
895
|
|
|
// No members, no further |
896
|
|
|
if (empty($members)) |
897
|
|
|
{ |
898
|
|
|
throw new Exception('reattribute_cannot_find_member'); |
899
|
|
|
} |
900
|
|
|
|
901
|
|
|
$memID = array_shift($members); |
902
|
|
|
$memID = $memID['id']; |
903
|
|
|
|
904
|
|
|
$email = $our_post['type'] === 'email' ? $our_post['from_email'] : ''; |
905
|
|
|
$memberName = $our_post['type'] === 'name' ? $our_post['from_name'] : ''; |
906
|
|
|
|
907
|
|
|
// Now call the reattribute function. |
908
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
909
|
|
|
reattributePosts($memID, $email, $memberName, !$our_post['posts']); |
910
|
|
|
|
911
|
|
|
$context['maintenance_finished'] = array( |
912
|
|
|
'errors' => array(sprintf($txt['maintain_done'], $txt['maintain_reattribute_posts'])), |
913
|
|
|
); |
914
|
|
|
} |
915
|
|
|
else |
916
|
|
|
{ |
917
|
|
|
// Show them the correct error |
918
|
|
|
if ($our_post['type'] === 'name' && empty($our_post['from_name'])) |
919
|
|
|
{ |
920
|
|
|
$error = $validator->validation_errors(array('from_name', 'to')); |
921
|
|
|
} |
922
|
|
|
else |
923
|
|
|
{ |
924
|
|
|
$error = $validator->validation_errors(array('from_email', 'to')); |
925
|
|
|
} |
926
|
|
|
|
927
|
|
|
$context['maintenance_finished'] = array( |
928
|
|
|
'errors' => $error, |
929
|
|
|
'type' => 'minor', |
930
|
|
|
); |
931
|
|
|
} |
932
|
|
|
} |
933
|
|
|
|
934
|
|
|
/** |
935
|
|
|
* Handling function for the backup stuff. |
936
|
|
|
* |
937
|
|
|
* - It requires an administrator and the session hash by post. |
938
|
|
|
* - This method simply forwards to DumpDatabase2(). |
939
|
|
|
*/ |
940
|
|
|
public function action_backup_display() |
941
|
|
|
{ |
942
|
|
|
validateToken('admin-maint'); |
943
|
|
|
|
944
|
|
|
// Administrators only! |
945
|
|
|
if (!allowedTo('admin_forum')) |
946
|
|
|
{ |
947
|
|
|
throw new Exception('no_dump_database', 'critical'); |
948
|
|
|
} |
949
|
|
|
|
950
|
|
|
checkSession('post'); |
951
|
|
|
|
952
|
|
|
// Validate access |
953
|
|
|
if (!defined('I_KNOW_IT_MAY_BE_UNSAFE') && $this->_validate_access() === false) |
954
|
|
|
{ |
955
|
|
|
return $this->action_database(); |
956
|
|
|
} |
957
|
|
|
|
958
|
|
|
require_once(SUBSDIR . '/Admin.subs.php'); |
959
|
|
|
emailAdmins('admin_backup_database', array( |
960
|
|
|
'BAK_REALNAME' => $this->user->name |
961
|
|
|
)); |
962
|
|
|
|
963
|
|
|
logAction('database_backup', array('member' => $this->user->id), 'admin'); |
964
|
|
|
require_once(SOURCEDIR . '/DumpDatabase.php'); |
965
|
|
|
DumpDatabase2(); |
966
|
|
|
|
967
|
|
|
// Should not get here as DumpDatabase2 exits |
968
|
|
|
return true; |
969
|
|
|
} |
970
|
|
|
|
971
|
|
|
/** |
972
|
|
|
* Validates the user can make an FTP connection with the supplied uid/pass |
973
|
|
|
* |
974
|
|
|
* - Used as an extra layer of security when performing backups |
975
|
|
|
*/ |
976
|
|
|
private function _validate_access() |
977
|
|
|
{ |
978
|
|
|
global $context, $txt; |
979
|
|
|
|
980
|
|
|
$ftp = new FtpConnection($this->_req->post->ftp_server, $this->_req->post->ftp_port, $this->_req->post->ftp_username, $this->_req->post->ftp_password); |
981
|
|
|
|
982
|
|
|
// No errors on the connection, id/pass are good |
983
|
|
|
// I know, I know... but a lot of people want to type /home/xyz/... which is wrong, but logical. |
984
|
|
|
if ($ftp->error === false && !$ftp->chdir($this->_req->post->ftp_path)) |
985
|
|
|
{ |
986
|
|
|
$ftp->chdir(preg_replace('~^/home[2]?/[^/]+~', '', $this->_req->post->ftp_path)); |
987
|
|
|
} |
988
|
|
|
|
989
|
|
|
// If we had an error... |
990
|
|
|
if ($ftp->error !== false) |
991
|
|
|
{ |
992
|
|
|
Txt::load('Packages'); |
993
|
|
|
$ftp_error = $ftp->last_message ?? $txt['package_ftp_' . $ftp->error] ?? ''; |
|
|
|
|
994
|
|
|
|
995
|
|
|
// Fill the boxes for a FTP connection with data from the previous attempt |
996
|
|
|
$context['package_ftp'] = array( |
997
|
|
|
'form_elements_only' => 1, |
998
|
|
|
'server' => $this->_req->post->ftp_server, |
999
|
|
|
'port' => $this->_req->post->ftp_port, |
1000
|
|
|
'username' => $this->_req->post->ftp_username, |
1001
|
|
|
'path' => $this->_req->post->ftp_path, |
1002
|
|
|
'error' => empty($ftp_error) ? null : $ftp_error, |
1003
|
|
|
); |
1004
|
|
|
|
1005
|
|
|
return false; |
1006
|
|
|
} |
1007
|
|
|
|
1008
|
|
|
return true; |
1009
|
|
|
} |
1010
|
|
|
|
1011
|
|
|
/** |
1012
|
|
|
* Supporting function for the database maintenance area. |
1013
|
|
|
*/ |
1014
|
|
|
public function action_database() |
1015
|
|
|
{ |
1016
|
|
|
global $context, $modSettings, $maintenance; |
1017
|
|
|
|
1018
|
|
|
// We need this, really.. |
1019
|
|
|
require_once(SUBSDIR . '/Maintenance.subs.php'); |
1020
|
|
|
|
1021
|
|
|
// Set up the sub-template |
1022
|
|
|
$context['sub_template'] = 'maintain_database'; |
1023
|
|
|
$db = database(); |
1024
|
|
|
|
1025
|
|
|
if ($db->supportMediumtext()) |
1026
|
|
|
{ |
1027
|
|
|
$body_type = fetchBodyType(); |
1028
|
|
|
|
1029
|
|
|
$context['convert_to'] = $body_type === 'text' ? 'mediumtext' : 'text'; |
1030
|
|
|
$context['convert_to_suggest'] = ($body_type !== 'text' && !empty($modSettings['max_messageLength']) && $modSettings['max_messageLength'] < 65536); |
1031
|
|
|
} |
1032
|
|
|
|
1033
|
|
|
// Check few things to give advices before make a backup |
1034
|
|
|
// If safe mod is enable the external tool is *always* the best (and probably the only) solution |
1035
|
|
|
$context['safe_mode_enable'] = false; |
1036
|
|
|
|
1037
|
|
|
// This is just a...guess |
1038
|
|
|
$messages = countMessages(); |
1039
|
|
|
|
1040
|
|
|
// 256 is what we use in the backup script |
1041
|
|
|
detectServer()->setMemoryLimit('256M'); |
1042
|
|
|
$memory_limit = memoryReturnBytes(ini_get('memory_limit')) / (1024 * 1024); |
1043
|
|
|
|
1044
|
|
|
// Zip limit is set to more or less 1/4th the size of the available memory * 1500 |
1045
|
|
|
// 1500 is an estimate of the number of messages that generates a database of 1 MB (yeah I know IT'S AN ESTIMATION!!!) |
1046
|
|
|
// Why that? Because the only reliable zip package is the one sent out the first time, |
1047
|
|
|
// so when the backup takes 1/5th (just to stay on the safe side) of the memory available |
1048
|
|
|
$zip_limit = $memory_limit * 1500 / 5; |
1049
|
|
|
|
1050
|
|
|
// Here is more tricky: it depends on many factors, but the main idea is that |
1051
|
|
|
// if it takes "too long" the backup is not reliable. So, I know that on my computer it take |
1052
|
|
|
// 20 minutes to backup 2.5 GB, of course my computer is not representative, so I'll multiply by 4 the time. |
1053
|
|
|
// I would consider "too long" 5 minutes (I know it can be a long time, but let's start with that): |
1054
|
|
|
// 80 minutes for a 2.5 GB and a 5 minutes limit means 160 MB approx |
1055
|
|
|
$plain_limit = 240000; |
1056
|
|
|
|
1057
|
|
|
// Last thing: are we able to gain time? |
1058
|
|
|
$current_time_limit = (int) ini_get('max_execution_time'); |
1059
|
|
|
$new_time_limit = detectServer()->setTimeLimit(159); //something strange just to be sure |
1060
|
|
|
detectServer()->setTimeLimit($current_time_limit); |
1061
|
|
|
|
1062
|
|
|
$context['use_maintenance'] = 0; |
1063
|
|
|
|
1064
|
|
|
// External tool if: |
1065
|
|
|
// * cannot change the execution time OR |
1066
|
|
|
// * cannot reset timeout |
1067
|
|
|
if (empty($new_time_limit) || ($current_time_limit == $new_time_limit && !function_exists('apache_reset_timeout'))) |
1068
|
|
|
{ |
1069
|
|
|
$context['suggested_method'] = 'use_external_tool'; |
1070
|
|
|
} |
1071
|
|
|
elseif ($zip_limit < $plain_limit && $messages < $zip_limit) |
1072
|
|
|
{ |
1073
|
|
|
$context['suggested_method'] = 'zipped_file'; |
1074
|
|
|
} |
1075
|
|
|
elseif ($zip_limit > $plain_limit || ($zip_limit < $plain_limit && $plain_limit < $messages)) |
1076
|
|
|
{ |
1077
|
|
|
$context['suggested_method'] = 'use_external_tool'; |
1078
|
|
|
$context['use_maintenance'] = empty($maintenance) ? 2 : 0; |
1079
|
|
|
} |
1080
|
|
|
else |
1081
|
|
|
{ |
1082
|
|
|
$context['use_maintenance'] = 1; |
1083
|
|
|
$context['suggested_method'] = 'plain_text'; |
1084
|
|
|
} |
1085
|
|
|
|
1086
|
|
|
theme()->getTemplates()->load('Packages'); |
1087
|
|
|
Txt::load('Packages'); |
1088
|
|
|
|
1089
|
|
|
// $context['package_ftp'] may be set action_backup_display when an error occur |
1090
|
|
|
if (!isset($context['package_ftp'])) |
1091
|
|
|
{ |
1092
|
|
|
$context['package_ftp'] = array( |
1093
|
|
|
'form_elements_only' => true, |
1094
|
|
|
'server' => '', |
1095
|
|
|
'port' => '', |
1096
|
|
|
'username' => $modSettings['package_username'] ?? '', |
1097
|
|
|
'path' => '', |
1098
|
|
|
'error' => '', |
1099
|
|
|
); |
1100
|
|
|
} |
1101
|
|
|
|
1102
|
|
|
$context['skip_security'] = defined('I_KNOW_IT_MAY_BE_UNSAFE'); |
1103
|
|
|
} |
1104
|
|
|
|
1105
|
|
|
/** |
1106
|
|
|
* Removing old and inactive members. |
1107
|
|
|
*/ |
1108
|
|
|
public function action_purgeinactive_display() |
1109
|
|
|
{ |
1110
|
|
|
global $context, $txt; |
1111
|
|
|
|
1112
|
|
|
checkSession(); |
1113
|
|
|
validateToken('admin-maint'); |
1114
|
|
|
|
1115
|
|
|
// Start with checking and cleaning what was sent |
1116
|
|
|
$validator = new DataValidator(); |
1117
|
|
|
$validator->sanitation_rules(array('maxdays' => 'intval')); |
1118
|
|
|
$validator->validation_rules(array('maxdays' => 'required', 'groups' => 'isarray', 'del_type' => 'required')); |
1119
|
|
|
|
1120
|
|
|
// Validator says, you can pass or not |
1121
|
|
|
if ($validator->validate($this->_req->post)) |
1122
|
|
|
{ |
1123
|
|
|
// Get the clean data |
1124
|
|
|
$our_post = array_replace((array) $this->_req->post, $validator->validation_data()); |
1125
|
|
|
|
1126
|
|
|
require_once(SUBSDIR . '/Maintenance.subs.php'); |
1127
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
1128
|
|
|
|
1129
|
|
|
$groups = array(); |
1130
|
|
|
foreach ($our_post['groups'] as $id => $dummy) |
1131
|
|
|
{ |
1132
|
|
|
$groups[] = (int) $id; |
1133
|
|
|
} |
1134
|
|
|
|
1135
|
|
|
$time_limit = (time() - ($our_post['maxdays'] * 24 * 3600)); |
1136
|
|
|
$members = purgeMembers($our_post['del_type'], $groups, $time_limit); |
1137
|
|
|
deleteMembers($members); |
1138
|
|
|
|
1139
|
|
|
$context['maintenance_finished'] = array( |
1140
|
|
|
'errors' => array(sprintf($txt['maintain_done'], $txt['maintain_members'])), |
1141
|
|
|
); |
1142
|
|
|
} |
1143
|
|
|
else |
1144
|
|
|
{ |
1145
|
|
|
$context['maintenance_finished'] = array( |
1146
|
|
|
'errors' => $validator->validation_errors(), |
1147
|
|
|
'type' => 'minor', |
1148
|
|
|
); |
1149
|
|
|
} |
1150
|
|
|
} |
1151
|
|
|
|
1152
|
|
|
/** |
1153
|
|
|
* This method takes care of removal of old posts. |
1154
|
|
|
* They're very very old, perhaps even older. |
1155
|
|
|
*/ |
1156
|
|
|
public function action_pruneold_display() |
1157
|
|
|
{ |
1158
|
|
|
validateToken('admin-maint'); |
1159
|
|
|
|
1160
|
|
|
isAllowedTo('admin_forum'); |
1161
|
|
|
checkSession('post', 'admin'); |
1162
|
|
|
|
1163
|
|
|
// No boards at all? Forget it then :/. |
1164
|
|
|
if (empty($this->_req->post->boards)) |
1165
|
|
|
{ |
1166
|
|
|
redirectexit('action=admin;area=maintain;sa=topics'); |
1167
|
|
|
} |
1168
|
|
|
|
1169
|
|
|
$boards = array_keys($this->_req->post->boards); |
1170
|
|
|
|
1171
|
|
|
if (!isset($this->_req->post->delete_type) || !in_array($this->_req->post->delete_type, array('moved', 'nothing', 'locked'))) |
1172
|
|
|
{ |
1173
|
|
|
$delete_type = 'nothing'; |
1174
|
|
|
} |
1175
|
|
|
else |
1176
|
|
|
{ |
1177
|
|
|
$delete_type = $this->_req->post->delete_type; |
1178
|
|
|
} |
1179
|
|
|
|
1180
|
|
|
$exclude_stickies = isset($this->_req->post->delete_old_not_sticky); |
1181
|
|
|
|
1182
|
|
|
// @todo what is the minimum for maxdays? Maybe throw an error? |
1183
|
|
|
$older_than = time() - 3600 * 24 * max($this->_req->getPost('maxdays', 'intval', 0), 1); |
1184
|
|
|
|
1185
|
|
|
require_once(SUBSDIR . '/Topic.subs.php'); |
1186
|
|
|
removeOldTopics($boards, $delete_type, $exclude_stickies, $older_than); |
1187
|
|
|
|
1188
|
|
|
// Log an action into the moderation log. |
1189
|
|
|
logAction('pruned', array('days' => max($this->_req->getPost('maxdays', 'intval', 0), 1))); |
1190
|
|
|
|
1191
|
|
|
redirectexit('action=admin;area=maintain;sa=topics;done=purgeold'); |
1192
|
|
|
} |
1193
|
|
|
|
1194
|
|
|
/** |
1195
|
|
|
* Moves topics from one board to another. |
1196
|
|
|
* |
1197
|
|
|
* @uses not_done template to pause the process. |
1198
|
|
|
*/ |
1199
|
|
|
public function action_massmove_display() |
1200
|
|
|
{ |
1201
|
|
|
global $context, $txt, $time_start; |
1202
|
|
|
|
1203
|
|
|
// Only admins. |
1204
|
|
|
isAllowedTo('admin_forum'); |
1205
|
|
|
|
1206
|
|
|
// And valid requests |
1207
|
|
|
checkSession(); |
1208
|
|
|
|
1209
|
|
|
// Set up to the context. |
1210
|
|
|
$context['page_title'] = $txt['not_done_title']; |
1211
|
|
|
$context['continue_countdown'] = 3; |
1212
|
|
|
$context['continue_post_data'] = ''; |
1213
|
|
|
$context['continue_get_data'] = ''; |
1214
|
|
|
$context['sub_template'] = 'not_done'; |
1215
|
|
|
$context['start'] = $this->_req->getQuery('start', 'intval', 0); |
1216
|
|
|
|
1217
|
|
|
// First time we do this? |
1218
|
|
|
$id_board_from = $this->_req->getPost('id_board_from', 'intval', $this->_req->getQuery('id_board_from', 'intval', 0)); |
1219
|
|
|
$id_board_to = $this->_req->getPost('id_board_to', 'intval', $this->_req->getQuery('id_board_to', 'intval', 0)); |
1220
|
|
|
|
1221
|
|
|
// No boards then this is your stop. |
1222
|
|
|
if (empty($id_board_from) || empty($id_board_to)) |
1223
|
|
|
{ |
1224
|
|
|
return; |
1225
|
|
|
} |
1226
|
|
|
|
1227
|
|
|
// These will be needed |
1228
|
|
|
require_once(SUBSDIR . '/Maintenance.subs.php'); |
1229
|
|
|
require_once(SUBSDIR . '/Topic.subs.php'); |
1230
|
|
|
|
1231
|
|
|
// How many topics are we moving? |
1232
|
|
|
$total_topics = $this->_req->getQuery('totaltopics', 'intval', 0); |
1233
|
|
|
if (empty($total_topics) || $context['start'] === 0) |
1234
|
|
|
{ |
1235
|
|
|
validateToken('admin-maint'); |
1236
|
|
|
$total_topics = countTopicsFromBoard($id_board_from); |
1237
|
|
|
} |
1238
|
|
|
else |
1239
|
|
|
{ |
1240
|
|
|
$total_topics = (int) $this->_req->query->totaltopics; |
1241
|
|
|
validateToken('admin_movetopics'); |
1242
|
|
|
} |
1243
|
|
|
|
1244
|
|
|
// We have topics to move so start the process. |
1245
|
|
|
if (!empty($total_topics)) |
1246
|
|
|
{ |
1247
|
|
|
while ($context['start'] <= $total_topics) |
1248
|
|
|
{ |
1249
|
|
|
// Lets get the next 10 topics. |
1250
|
|
|
$topics = getTopicsToMove($id_board_from); |
1251
|
|
|
|
1252
|
|
|
// Just return if we don't have any topics left to move. |
1253
|
|
|
if (empty($topics)) |
1254
|
|
|
{ |
1255
|
|
|
break; |
1256
|
|
|
} |
1257
|
|
|
|
1258
|
|
|
// Lets move them. |
1259
|
|
|
moveTopics($topics, $id_board_to); |
1260
|
|
|
|
1261
|
|
|
// Increase the counter |
1262
|
|
|
$context['start'] += 10; |
1263
|
|
|
|
1264
|
|
|
// If this is really taking some time, show the pause screen |
1265
|
|
|
if (microtime(true) - $time_start > 3) |
1266
|
|
|
{ |
1267
|
|
|
// What's the percent? |
1268
|
|
|
$context['continue_percent'] = round(100 * ($context['start'] / $total_topics), 1); |
1269
|
|
|
|
1270
|
|
|
// Set up for the form |
1271
|
|
|
$context['continue_get_data'] = '?action=admin;area=maintain;sa=topics;activity=massmove;id_board_from=' . $id_board_from . ';id_board_to=' . $id_board_to . ';totaltopics=' . $total_topics . ';start=' . $context['start']; |
1272
|
|
|
$context['continue_post_data'] = ' |
1273
|
|
|
<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '" />'; |
1274
|
|
|
|
1275
|
|
|
// Let the template system do it's thang. |
1276
|
|
|
return; |
1277
|
|
|
} |
1278
|
|
|
} |
1279
|
|
|
} |
1280
|
|
|
|
1281
|
|
|
// Don't confuse admins by having an out of date cache. |
1282
|
|
|
Cache::instance()->remove('board-' . $id_board_from); |
1283
|
|
|
Cache::instance()->remove('board-' . $id_board_to); |
1284
|
|
|
|
1285
|
|
|
redirectexit('action=admin;area=maintain;sa=topics;done=massmove'); |
1286
|
|
|
} |
1287
|
|
|
|
1288
|
|
|
/** |
1289
|
|
|
* Generates a list of integration hooks for display |
1290
|
|
|
* |
1291
|
|
|
* - Accessed through ?action=admin;area=maintain;sa=hooks; |
1292
|
|
|
* - Allows for removal or disabling of selected hooks |
1293
|
|
|
*/ |
1294
|
|
|
public function action_hooks() |
1295
|
|
|
{ |
1296
|
|
|
global $context, $txt; |
1297
|
|
|
|
1298
|
|
|
require_once(SUBSDIR . '/AddonSettings.subs.php'); |
1299
|
|
|
|
1300
|
|
|
$context['filter_url'] = ''; |
1301
|
|
|
$context['current_filter'] = ''; |
1302
|
|
|
|
1303
|
|
|
// Get the list of the current system hooks, filter them if needed |
1304
|
|
|
$currentHooks = get_integration_hooks(); |
1305
|
|
|
if (isset($this->_req->query->filter) && array_key_exists($this->_req->query->filter, $currentHooks)) |
1306
|
|
|
{ |
1307
|
|
|
$context['filter_url'] = ';filter=' . $this->_req->query->filter; |
1308
|
|
|
$context['current_filter'] = $this->_req->query->filter; |
1309
|
|
|
} |
1310
|
|
|
|
1311
|
|
|
$list_options = array( |
1312
|
|
|
'id' => 'list_integration_hooks', |
1313
|
|
|
'title' => $txt['maintain_sub_hooks_list'], |
1314
|
|
|
'items_per_page' => 20, |
1315
|
|
|
'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'maintain', 'sa' => 'hooks', $context['filter_url'], '{session_data}']), |
1316
|
|
|
'default_sort_col' => 'hook_name', |
1317
|
|
|
'get_items' => array( |
1318
|
|
|
'function' => fn($start, $items_per_page, $sort) => $this->list_getIntegrationHooks($start, $items_per_page, $sort), |
1319
|
|
|
), |
1320
|
|
|
'get_count' => array( |
1321
|
|
|
'function' => fn() => $this->list_getIntegrationHooksCount(), |
1322
|
|
|
), |
1323
|
|
|
'no_items_label' => $txt['hooks_no_hooks'], |
1324
|
|
|
'columns' => array( |
1325
|
|
|
'hook_name' => array( |
1326
|
|
|
'header' => array( |
1327
|
|
|
'value' => $txt['hooks_field_hook_name'], |
1328
|
|
|
), |
1329
|
|
|
'data' => array( |
1330
|
|
|
'db' => 'hook_name', |
1331
|
|
|
), |
1332
|
|
|
'sort' => array( |
1333
|
|
|
'default' => 'hook_name', |
1334
|
|
|
'reverse' => 'hook_name DESC', |
1335
|
|
|
), |
1336
|
|
|
), |
1337
|
|
|
'function_name' => array( |
1338
|
|
|
'header' => array( |
1339
|
|
|
'value' => $txt['hooks_field_function_name'], |
1340
|
|
|
), |
1341
|
|
|
'data' => array( |
1342
|
|
|
'function' => static function ($data) { |
1343
|
|
|
global $txt; |
1344
|
|
|
|
1345
|
|
|
if (!empty($data['included_file'])) |
1346
|
|
|
{ |
1347
|
|
|
return $txt['hooks_field_function'] . ': ' . $data['real_function'] . '<br />' . $txt['hooks_field_included_file'] . ': ' . $data['included_file']; |
1348
|
|
|
} |
1349
|
|
|
|
1350
|
|
|
return $data['real_function']; |
1351
|
|
|
}, |
1352
|
|
|
), |
1353
|
|
|
'sort' => array( |
1354
|
|
|
'default' => 'function_name', |
1355
|
|
|
'reverse' => 'function_name DESC', |
1356
|
|
|
), |
1357
|
|
|
), |
1358
|
|
|
'file_name' => array( |
1359
|
|
|
'header' => array( |
1360
|
|
|
'value' => $txt['hooks_field_file_name'], |
1361
|
|
|
), |
1362
|
|
|
'data' => array( |
1363
|
|
|
'db' => 'file_name', |
1364
|
|
|
), |
1365
|
|
|
'sort' => array( |
1366
|
|
|
'default' => 'file_name', |
1367
|
|
|
'reverse' => 'file_name DESC', |
1368
|
|
|
), |
1369
|
|
|
), |
1370
|
|
|
'status' => array( |
1371
|
|
|
'header' => array( |
1372
|
|
|
'value' => $txt['hooks_field_hook_exists'], |
1373
|
|
|
'class' => 'nowrap', |
1374
|
|
|
), |
1375
|
|
|
'data' => array( |
1376
|
|
|
'function' => static fn($data) => '<i class="icon i-post_moderation_' . $data['status'] . '" title="' . $data['img_text'] . '"></i>', |
1377
|
|
|
'class' => 'centertext', |
1378
|
|
|
), |
1379
|
|
|
'sort' => array( |
1380
|
|
|
'default' => 'status', |
1381
|
|
|
'reverse' => 'status DESC', |
1382
|
|
|
), |
1383
|
|
|
), |
1384
|
|
|
), |
1385
|
|
|
'additional_rows' => array( |
1386
|
|
|
array( |
1387
|
|
|
'position' => 'after_title', |
1388
|
|
|
'value' => $txt['hooks_disable_legend'] . ': |
1389
|
|
|
<ul> |
1390
|
|
|
<li> |
1391
|
|
|
<i class="icon i-post_moderation_allow" title="' . $txt['hooks_active'] . '"></i>' . $txt['hooks_disable_legend_exists'] . ' |
1392
|
|
|
</li> |
1393
|
|
|
<li> |
1394
|
|
|
<i class="icon i-post_moderation_deny col" title="' . $txt['hooks_missing'] . '"></i>' . $txt['hooks_disable_legend_missing'] . ' |
1395
|
|
|
</li> |
1396
|
|
|
</ul>' |
1397
|
|
|
), |
1398
|
|
|
), |
1399
|
|
|
); |
1400
|
|
|
|
1401
|
|
|
createList($list_options); |
1402
|
|
|
|
1403
|
|
|
$context['page_title'] = $txt['maintain_sub_hooks_list']; |
1404
|
|
|
$context['sub_template'] = 'show_list'; |
1405
|
|
|
$context['default_list'] = 'list_integration_hooks'; |
1406
|
|
|
} |
1407
|
|
|
|
1408
|
|
|
/** |
1409
|
|
|
* Callback for createList(). Called by action_hooks |
1410
|
|
|
* |
1411
|
|
|
* @param int $start The item to start with (for pagination purposes) |
1412
|
|
|
* @param int $items_per_page The number of items to show per page |
1413
|
|
|
* @param string $sort A string indicating how to sort the results |
1414
|
|
|
* |
1415
|
|
|
* @return array |
1416
|
|
|
*/ |
1417
|
|
|
public function list_getIntegrationHooks($start, $items_per_page, $sort) |
1418
|
|
|
{ |
1419
|
|
|
return list_integration_hooks_data($start, $items_per_page, $sort); |
1420
|
|
|
} |
1421
|
|
|
|
1422
|
|
|
/** |
1423
|
|
|
* Simply returns the total count of integration hooks |
1424
|
|
|
* Callback for createList(). |
1425
|
|
|
* |
1426
|
|
|
* @return int |
1427
|
|
|
*/ |
1428
|
|
|
public function list_getIntegrationHooksCount() |
1429
|
|
|
{ |
1430
|
|
|
global $context; |
1431
|
|
|
|
1432
|
|
|
$context['filter'] = $this->_req->getQuery('filter', 'trim|strval', false); |
1433
|
|
|
|
1434
|
|
|
return integration_hooks_count($context['filter']); |
1435
|
|
|
} |
1436
|
|
|
|
1437
|
|
|
/** |
1438
|
|
|
* Recalculate all members post counts |
1439
|
|
|
* |
1440
|
|
|
* What it does: |
1441
|
|
|
* |
1442
|
|
|
* - It requires the admin_forum permission. |
1443
|
|
|
* - Recounts all posts for members found in the message table |
1444
|
|
|
* - Updates the members post count record in the members table |
1445
|
|
|
* - Honors the boards post count flag |
1446
|
|
|
* - Does not count posts in the recycle bin |
1447
|
|
|
* - Zeros post counts for all members with no posts in the message table |
1448
|
|
|
* - Runs as a delayed loop to avoid server overload |
1449
|
|
|
* - Uses the not_done template in Admin.template |
1450
|
|
|
* - Redirects back to action=admin;area=maintain;sa=members when complete. |
1451
|
|
|
* - Accessed via ?action=admin;area=maintain;sa=members;activity=recountposts |
1452
|
|
|
*/ |
1453
|
|
|
public function action_recountposts_display() |
1454
|
|
|
{ |
1455
|
|
|
global $txt, $context; |
1456
|
|
|
|
1457
|
|
|
// Check the session |
1458
|
|
|
checkSession(); |
1459
|
|
|
|
1460
|
|
|
// Set up to the context for the pause screen |
1461
|
|
|
$context['page_title'] = $txt['not_done_title']; |
1462
|
|
|
$context['continue_countdown'] = 3; |
1463
|
|
|
$context['continue_get_data'] = ''; |
1464
|
|
|
$context['sub_template'] = 'not_done'; |
1465
|
|
|
|
1466
|
|
|
// Init, do 200 members in a bunch |
1467
|
|
|
$increment = 200; |
1468
|
|
|
$start = $this->_req->getQuery('start', 'intval', 0); |
1469
|
|
|
|
1470
|
|
|
// Ask for some extra time, on big boards this may take a bit |
1471
|
|
|
detectServer()->setTimeLimit(600); |
1472
|
|
|
Debug::instance()->off(); |
1473
|
|
|
|
1474
|
|
|
// The functions here will come in handy |
1475
|
|
|
require_once(SUBSDIR . '/Maintenance.subs.php'); |
1476
|
|
|
|
1477
|
|
|
// Only run this query if we don't have the total number of members that have posted |
1478
|
|
|
if (!isset($_SESSION['total_members']) || $start === 0) |
1479
|
|
|
{ |
1480
|
|
|
validateToken('admin-maint'); |
1481
|
|
|
$total_members = countContributors(); |
1482
|
|
|
$_SESSION['total_member'] = $total_members; |
1483
|
|
|
} |
1484
|
|
|
else |
1485
|
|
|
{ |
1486
|
|
|
validateToken('admin-recountposts'); |
1487
|
|
|
$total_members = $this->_req->session->total_members; |
1488
|
|
|
} |
1489
|
|
|
|
1490
|
|
|
// Lets get the next group of members and determine their post count |
1491
|
|
|
// (from the boards that have post count enabled of course). |
1492
|
|
|
$total_rows = updateMembersPostCount($start, $increment); |
1493
|
|
|
|
1494
|
|
|
// Continue? |
1495
|
|
|
if ($total_rows === $increment) |
1496
|
|
|
{ |
1497
|
|
|
createToken('admin-recountposts'); |
1498
|
|
|
|
1499
|
|
|
$start += $increment; |
1500
|
|
|
$context['continue_get_data'] = '?action=admin;area=maintain;sa=members;activity=recountposts;start=' . $start; |
1501
|
|
|
$context['continue_percent'] = round(100 * $start / $total_members); |
1502
|
|
|
$context['not_done_title'] = $txt['not_done_title'] . ' (' . $context['continue_percent'] . '%)'; |
1503
|
|
|
$context['continue_post_data'] = '<input type="hidden" name="' . $context['admin-recountposts_token_var'] . '" value="' . $context['admin-recountposts_token'] . '" /> |
1504
|
|
|
<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '" />'; |
1505
|
|
|
|
1506
|
|
|
Debug::instance()->on(); |
1507
|
|
|
|
1508
|
|
|
return; |
1509
|
|
|
} |
1510
|
|
|
|
1511
|
|
|
// No countable posts? set posts counter to 0 |
1512
|
|
|
updateZeroPostMembers(); |
1513
|
|
|
|
1514
|
|
|
Debug::instance()->on(); |
1515
|
|
|
|
1516
|
|
|
// All done, clean up and go back to maintenance |
1517
|
|
|
unset($_SESSION['total_members']); |
1518
|
|
|
redirectexit('action=admin;area=maintain;sa=members;done=recountposts'); |
1519
|
|
|
} |
1520
|
|
|
} |
1521
|
|
|
|