Completed
Pull Request — master (#3325)
by Emanuele
11:19
created

ManageAttachments_Controller::action_removeall()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 7
dl 0
loc 15
rs 10
c 0
b 0
f 0
nc 2
nop 0
ccs 0
cts 8
cp 0
crap 6
1
<?php
2
3
/**
4
 * Handles the job of attachment and avatar maintenance /management.
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * This file contains code covered by:
11
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
12
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
13
 *
14
 * @version 1.1.6
15
 *
16
 */
17
18
/**
19
 * This is the attachments and avatars controller class.
20
 * It is doing the job of attachments and avatars maintenance and management.
21
 *
22
 * @package Attachments
23
 */
24
class ManageAttachments_Controller extends Action_Controller
25
{
26
	/**
27
	 * Loop counter for paused attachment maintenance actions
28
	 * @var int
29
	 */
30
	public $step;
31
32
	/**
33
	 * substep counter for paused attachment maintenance actions
34
	 * @var int
35
	 */
36
	public $substep;
37
38
	/**
39
	 * Substep at the beginning of a maintenance loop
40
	 * @var int
41
	 */
42
	public $starting_substep;
43
44
	/**
45
	 * Current directory key being processed
46
	 * @var int
47
	 */
48
	public $current_dir;
49
50
	/**
51
	 * Current base directory key being processed
52
	 * @var int
53
	 */
54
	public $current_base_dir;
55
56
	/**
57
	 * Used during transfer of files
58
	 * @var string
59
	 */
60
	public $from;
61
62
	/**
63
	 * Type of attachment management in use
64
	 * @var string
65
	 */
66
	public $auto;
67
68
	/**
69
	 * Destination when transferring attachments
70
	 * @var string
71
	 */
72
	public $to;
73
74 1
	public function pre_dispatch()
75
	{
76
		// These get used often enough that it makes sense to include them for every action
77 1
		require_once(SUBSDIR . '/Attachments.subs.php');
78 1
		require_once(SUBSDIR . '/ManageAttachments.subs.php');
79 1
	}
80
81
	/**
82
	 * The main 'Attachments and Avatars' admin.
83
	 *
84
	 * What it does:
85
	 *
86
	 * - This method is the entry point for index.php?action=admin;area=manageattachments
87
	 * and it calls a function based on the sub-action.
88
	 * - It requires the manage_attachments permission.
89
	 *
90
	 * @event integrate_sa_manage_attachments
91
	 * @uses ManageAttachments template.
92
	 * @uses Admin language file.
93
	 * @uses template layer 'manage_files' for showing the tab bar.
94
	 *
95
	 * @see Action_Controller::action_index()
96
	 */
97
	public function action_index()
98
	{
99
		global $txt, $context;
100
101
		// You have to be able to moderate the forum to do this.
102
		isAllowedTo('manage_attachments');
103
104
		// Setup the template stuff we'll probably need.
105
		loadTemplate('ManageAttachments');
106
107
		// If they want to delete attachment(s), delete them. (otherwise fall through..)
108
		$subActions = array(
109
			'attachments' => array($this, 'action_attachSettings_display'),
110
			'avatars' => array(
111
				'controller' => 'ManageAvatars_Controller',
112
				'function' => 'action_index'),
113
			'attachpaths' => array($this, 'action_attachpaths'),
114
			'browse' => array($this, 'action_browse'),
115
			'byAge' => array($this, 'action_byAge'),
116
			'bySize' => array($this, 'action_bySize'),
117
			'maintenance' => array($this, 'action_maintenance'),
118
			'moveAvatars' => array($this, 'action_moveAvatars'),
119
			'repair' => array($this, 'action_repair'),
120
			'remove' => array($this, 'action_remove'),
121
			'removeall' => array($this, 'action_removeall'),
122
			'transfer' => array($this, 'action_transfer'),
123
		);
124
125
		// Get ready for some action
126
		$action = new Action('manage_attachments');
127
128
		// Default page title is good.
129
		$context['page_title'] = $txt['attachments_avatars'];
130
131
		// This uses admin tabs - as it should!
132
		$context[$context['admin_menu_name']]['tab_data'] = array(
133
			'title' => $txt['attachments_avatars'],
134
			'help' => 'manage_files',
135
			'description' => $txt['attachments_desc'],
136
		);
137
138
		// Get the subAction, call integrate_sa_manage_attachments
139
		$subAction = $action->initialize($subActions, 'browse');
140
		$context['sub_action'] = $subAction;
141
142
		// Finally go to where we want to go
143
		$action->dispatch($subAction);
144
	}
145
146
	/**
147
	 * Allows to show/change attachment settings.
148
	 *
149
	 * - This is the default sub-action of the 'Attachments and Avatars' center.
150
	 * - Called by index.php?action=admin;area=manageattachments;sa=attachments.
151
	 *
152
	 * @event integrate_save_attachment_settings
153
	 * @uses 'attachments' sub template.
154
	 */
155
	public function action_attachSettings_display()
156
	{
157
		global $modSettings, $scripturl, $context;
158
159
		// initialize the form
160
		$settingsForm = new Settings_Form(Settings_Form::DB_ADAPTER);
161
162
		// Initialize settings
163
		$settingsForm->setConfigVars($this->_settings());
164
165
		addInlineJavascript('
166
	var storing_type = document.getElementById(\'automanage_attachments\'),
167
		base_dir = document.getElementById(\'use_subdirectories_for_attachments\');
168
169
	createEventListener(storing_type)
170
	storing_type.addEventListener("change", toggleSubDir, false);
171
	createEventListener(base_dir)
172
	base_dir.addEventListener("change", toggleSubDir, false);
173
	toggleSubDir();', true);
174
175
		// Saving settings?
176
		if (isset($this->_req->query->save))
177
		{
178
			checkSession();
179
180
			if (!empty($this->_req->post->attachmentEnable))
181
			{
182
				enableModules('attachments', array('post'));
183
			}
184
			else
185
			{
186
				disableModules('attachments', array('post'));
187
			}
188
189
			// Changing the attachment upload directory
190
			if (isset($this->_req->post->attachmentUploadDir)
191
				|| !empty($this->_req->post->use_subdirectories_for_attachments))
192
			{
193
				if (!empty($this->_req->post->attachmentUploadDir) && file_exists($modSettings['attachmentUploadDir']) && $modSettings['attachmentUploadDir'] != $this->_req->post->attachmentUploadDir)
194
					rename($modSettings['attachmentUploadDir'], $this->_req->post->attachmentUploadDir);
195
196
				$modSettings['attachmentUploadDir'] = array(1 => $this->_req->post->attachmentUploadDir);
197
				$this->_req->post->attachmentUploadDir = serialize($modSettings['attachmentUploadDir']);
198
			}
199
200
			// Adding / changing the sub directory's for attachments
201
			if (!empty($this->_req->post->use_subdirectories_for_attachments))
202
			{
203
				// Make sure we have a base directory defined
204
				if (isset($this->_req->post->use_subdirectories_for_attachments) && empty($this->_req->post->basedirectory_for_attachments))
205
					$this->_req->post->basedirectory_for_attachments = (!empty($modSettings['basedirectory_for_attachments']) ? ($modSettings['basedirectory_for_attachments']) : BOARDDIR);
206
207
				if (!empty($modSettings['attachment_basedirectories']))
208
				{
209
					if (!is_array($modSettings['attachment_basedirectories']))
210
						$modSettings['attachment_basedirectories'] = Util::unserialize($modSettings['attachment_basedirectories']);
211
				}
212
				else
213
					$modSettings['attachment_basedirectories'] = array();
214
215
				if (!empty($this->_req->post->basedirectory_for_attachments) && !in_array($this->_req->post->basedirectory_for_attachments, $modSettings['attachment_basedirectories']))
216
				{
217
					$currentAttachmentUploadDir = $modSettings['currentAttachmentUploadDir'];
218
219
					if (!in_array($this->_req->post->basedirectory_for_attachments, $modSettings['attachmentUploadDir']))
220
					{
221
						if (!automanage_attachments_create_directory($this->_req->post->basedirectory_for_attachments))
222
							$this->_req->post->basedirectory_for_attachments = $modSettings['basedirectory_for_attachments'];
223
					}
224
225
					if (!in_array($this->_req->post->basedirectory_for_attachments, $modSettings['attachment_basedirectories']))
226
					{
227
						$modSettings['attachment_basedirectories'][$modSettings['currentAttachmentUploadDir']] = $this->_req->post->basedirectory_for_attachments;
228
						updateSettings(array(
229
							'attachment_basedirectories' => serialize($modSettings['attachment_basedirectories']),
230
							'currentAttachmentUploadDir' => $currentAttachmentUploadDir,
231
						));
232
233
						$this->_req->post->use_subdirectories_for_attachments = 1;
234
						$this->_req->post->attachmentUploadDir = serialize($modSettings['attachmentUploadDir']);
235
					}
236
				}
237
			}
238
239
			call_integration_hook('integrate_save_attachment_settings');
240
241
			$settingsForm->setConfigValues((array) $this->_req->post);
242
			$settingsForm->save();
243
			redirectexit('action=admin;area=manageattachments;sa=attachments');
244
		}
245
246
		$context['post_url'] = $scripturl . '?action=admin;area=manageattachments;save;sa=attachments';
247
		$settingsForm->prepare();
248
249
		$context['sub_template'] = 'show_settings';
250
	}
251
252
	/**
253
	 * Retrieve and return the administration settings for attachments.
254
	 *
255
	 * @event integrate_modify_attachment_settings
256
	 */
257 1
	private function _settings()
258
	{
259 1
		global $modSettings, $txt, $scripturl, $context;
260
261
		// Get the current attachment directory.
262 1
		$modSettings['attachmentUploadDir'] = Util::unserialize($modSettings['attachmentUploadDir']);
263 1
		$context['attachmentUploadDir'] = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
264
265
		// First time here?
266 1
		if (empty($modSettings['attachment_basedirectories']) && $modSettings['currentAttachmentUploadDir'] == 1 && (is_array($modSettings['attachmentUploadDir']) && count($modSettings['attachmentUploadDir']) == 1))
267 1
			$modSettings['attachmentUploadDir'] = $modSettings['attachmentUploadDir'][1];
268
269
		// If not set, show a default path for the base directory
270 1
		if (!isset($this->_req->query->save) && empty($modSettings['basedirectory_for_attachments']))
271 1
			$modSettings['basedirectory_for_attachments'] = $context['attachmentUploadDir'];
272
273 1
		$context['valid_upload_dir'] = is_dir($context['attachmentUploadDir']) && is_writable($context['attachmentUploadDir']);
274
275 1
		if (!empty($modSettings['automanage_attachments']))
276 1
			$context['valid_basedirectory'] = !empty($modSettings['basedirectory_for_attachments']) && is_writable($modSettings['basedirectory_for_attachments']);
277
		else
278 1
			$context['valid_basedirectory'] = true;
279
280
		// A bit of razzle dazzle with the $txt strings. :)
281 1
		$txt['basedirectory_for_attachments_warning'] = str_replace('{attach_repair_url}', $scripturl . '?action=admin;area=manageattachments;sa=attachpaths', $txt['basedirectory_for_attachments_warning']);
282 1
		$txt['attach_current_dir_warning'] = str_replace('{attach_repair_url}', $scripturl . '?action=admin;area=manageattachments;sa=attachpaths', $txt['attach_current_dir_warning']);
283
284 1
		$txt['attachment_path'] = $context['attachmentUploadDir'];
285 1
		$txt['basedirectory_for_attachments_path'] = isset($modSettings['basedirectory_for_attachments']) ? $modSettings['basedirectory_for_attachments'] : '';
286 1
		$txt['use_subdirectories_for_attachments_note'] = empty($modSettings['attachment_basedirectories']) || empty($modSettings['use_subdirectories_for_attachments']) ? $txt['use_subdirectories_for_attachments_note'] : '';
287 1
		$txt['attachmentUploadDir_multiple_configure'] = '<a class="linkbutton" href="' . $scripturl . '?action=admin;area=manageattachments;sa=attachpaths">' . $txt['attachmentUploadDir_multiple_configure'] . '</a>';
288 1
		$txt['attach_current_dir'] = empty($modSettings['automanage_attachments']) ? $txt['attach_current_dir'] : $txt['attach_last_dir'];
289 1
		$txt['attach_current_dir_warning'] = $txt['attach_current_dir'] . $txt['attach_current_dir_warning'];
290 1
		$txt['basedirectory_for_attachments_warning'] = $txt['basedirectory_for_attachments_current'] . $txt['basedirectory_for_attachments_warning'];
291
292
		// Perform a test to see if the GD module or ImageMagick are installed.
293 1
		$testImg = get_extension_funcs('gd') || class_exists('Imagick');
294
295
		// See if we can find if the server is set up to support the attachment limits
296 1
		$post_max_size = ini_get('post_max_size');
297 1
		$upload_max_filesize = ini_get('upload_max_filesize');
298 1
		$testPM = !empty($post_max_size) ? (memoryReturnBytes($post_max_size) >= (isset($modSettings['attachmentPostLimit']) ? $modSettings['attachmentPostLimit'] * 1024 : 0)) : true;
299 1
		$testUM = !empty($upload_max_filesize) ? (memoryReturnBytes($upload_max_filesize) >= (isset($modSettings['attachmentSizeLimit']) ? $modSettings['attachmentSizeLimit'] * 1024 : 0)) : true;
300 1
		$testImgRotate = class_exists('Imagick') || (get_extension_funcs('gd') && function_exists('exif_read_data'));
301
302
		$config_vars = array(
303 1
			array('title', 'attachment_manager_settings'),
304
				// Are attachments enabled?
305 1
				array('select', 'attachmentEnable', array($txt['attachmentEnable_deactivate'], $txt['attachmentEnable_enable_all'], $txt['attachmentEnable_disable_new'])),
306 1
			'',
307
				// Extension checks etc.
308 1
				array('check', 'attachmentRecodeLineEndings'),
309 1
			'',
310
				// Directory and size limits.
311 1
				array('select', 'automanage_attachments', array(0 => $txt['attachments_normal'], 1 => $txt['attachments_auto_space'], 2 => $txt['attachments_auto_years'], 3 => $txt['attachments_auto_months'], 4 => $txt['attachments_auto_16'])),
312 1
				array('check', 'use_subdirectories_for_attachments', 'subtext' => $txt['use_subdirectories_for_attachments_note']),
313 1
				(empty($modSettings['attachment_basedirectories'])
314 1
					? array('text', 'basedirectory_for_attachments', 40,)
315 1
					: array('var_message', 'basedirectory_for_attachments', 'message' => 'basedirectory_for_attachments_path', 'invalid' => empty($context['valid_basedirectory']), 'text_label' => (!empty($context['valid_basedirectory'])
316
						? $txt['basedirectory_for_attachments_current']
317
						: $txt['basedirectory_for_attachments_warning']))
318 1
				),
319 1
				empty($modSettings['attachment_basedirectories']) && $modSettings['currentAttachmentUploadDir'] == 1 && (is_array($modSettings['attachmentUploadDir']) && count($modSettings['attachmentUploadDir']) == 1)
320 1
					? array('text', 'attachmentUploadDir', 'postinput' => $txt['attachmentUploadDir_multiple_configure'], 40, 'invalid' => !$context['valid_upload_dir'])
321 1
					: array('var_message', 'attach_current_directory', 'postinput' => $txt['attachmentUploadDir_multiple_configure'], 'message' => 'attachment_path', 'invalid' => empty($context['valid_upload_dir']), 'text_label' => (!empty($context['valid_upload_dir'])
322 1
						? $txt['attach_current_dir']
323 1
						: $txt['attach_current_dir_warning'])
324 1
				),
325 1
				array('int', 'attachmentDirFileLimit', 'subtext' => $txt['zero_for_no_limit'], 6),
326 1
				array('int', 'attachmentDirSizeLimit', 'subtext' => $txt['zero_for_no_limit'], 6, 'postinput' => $txt['kilobyte']),
327 1
			'',
328
				// Posting limits
329 1
				array('warning', empty($testPM) ? 'attachment_postsize_warning' : ''),
330 1
				array('int', 'attachmentPostLimit', 'subtext' => $txt['zero_for_no_limit'], 6, 'postinput' => $txt['kilobyte']),
331 1
				array('warning', empty($testUM) ? 'attachment_filesize_warning' : ''),
332 1
				array('int', 'attachmentSizeLimit', 'subtext' => $txt['zero_for_no_limit'], 6, 'postinput' => $txt['kilobyte']),
333 1
				array('int', 'attachmentNumPerPostLimit', 'subtext' => $txt['zero_for_no_limit'], 6),
334 1
				array('check', 'attachment_autorotate', 'postinput' => empty($testImgRotate) ? $txt['attachment_autorotate_na'] : ''),
335
			// Security Items
336 1
			array('title', 'attachment_security_settings'),
337
				// Extension checks etc.
338 1
				array('check', 'attachmentCheckExtensions'),
339 1
				array('text', 'attachmentExtensions', 40),
340 1
			'',
341
				// Image checks.
342 1
				array('warning', empty($testImg) ? 'attachment_img_enc_warning' : ''),
343 1
				array('check', 'attachment_image_reencode'),
344 1
			'',
345 1
				array('warning', 'attachment_image_paranoid_warning'),
346 1
				array('check', 'attachment_image_paranoid'),
347
			// Thumbnail settings.
348 1
			array('title', 'attachment_thumbnail_settings'),
349 1
				array('check', 'attachmentShowImages'),
350 1
				array('check', 'attachmentThumbnails'),
351 1
				array('check', 'attachment_thumb_png'),
352 1
				array('check', 'attachment_thumb_memory', 'subtext' => $txt['attachment_thumb_memory_note1'], 'postinput' => $txt['attachment_thumb_memory_note2']),
353 1
				array('text', 'attachmentThumbWidth', 6),
354 1
				array('text', 'attachmentThumbHeight', 6),
355 1
			'',
356 1
				array('int', 'max_image_width', 'subtext' => $txt['zero_for_no_limit']),
357 1
				array('int', 'max_image_height', 'subtext' => $txt['zero_for_no_limit']),
358 1
		);
359
360
		// Add new settings with a nice hook, makes them available for admin settings search as well
361 1
		call_integration_hook('integrate_modify_attachment_settings', array(&$config_vars));
362
363 1
		return $config_vars;
364
	}
365
366
	/**
367
	 * Public method to return the config settings, used for admin search
368
	 */
369 1
	public function settings_search()
370
	{
371 1
		return $this->_settings();
372
	}
373
374
	/**
375
	 * Show a list of attachment or avatar files.
376
	 *
377
	 * - Called by ?action=admin;area=manageattachments;sa=browse for attachments
378
	 * and ?action=admin;area=manageattachments;sa=browse;avatars for avatars.
379
	 * - Allows sorting by name, date, size and member.
380
	 * - Paginates results.
381
	 *
382
	 *  @uses the 'browse' sub template
383
	 */
384
	public function action_browse()
385
	{
386
		global $context, $txt, $scripturl, $modSettings;
387
388
		// Attachments or avatars?
389
		$context['browse_type'] = isset($this->_req->query->avatars) ? 'avatars' : (isset($this->_req->query->thumbs) ? 'thumbs' : 'attachments');
390
391
		// Set the options for the list component.
392
		$listOptions = array(
393
			'id' => 'attach_browse',
394
			'title' => $txt['attachment_manager_browse_files'],
395
			'items_per_page' => $modSettings['defaultMaxMessages'],
396
			'base_href' => $scripturl . '?action=admin;area=manageattachments;sa=browse' . ($context['browse_type'] === 'avatars' ? ';avatars' : ($context['browse_type'] === 'thumbs' ? ';thumbs' : '')),
397
			'default_sort_col' => 'name',
398
			'no_items_label' => $txt['attachment_manager_' . ($context['browse_type'] === 'avatars' ? 'avatars' : ($context['browse_type'] === 'thumbs' ? 'thumbs' : 'attachments')) . '_no_entries'],
399
			'get_items' => array(
400
				'function' => 'list_getFiles',
401
				'params' => array(
402
					$context['browse_type'],
403
				),
404
			),
405
			'get_count' => array(
406
				'function' => 'list_getNumFiles',
407
				'params' => array(
408
					$context['browse_type'],
409
				),
410
			),
411
			'columns' => array(
412
				'name' => array(
413
					'header' => array(
414
						'value' => $txt['attachment_name'],
415
						'class' => 'grid50',
416
					),
417
					'data' => array(
418
						'function' => function ($rowData) {
419
							global $modSettings, $context, $scripturl;
420
421
							$link = '<a href="';
422
423
							// In case of a custom avatar URL attachments have a fixed directory.
424
							if ($rowData['attachment_type'] == 1)
425
								$link .= sprintf('%1$s/%2$s', $modSettings['custom_avatar_url'], $rowData['filename']);
426
427
							// By default avatars are downloaded almost as attachments.
428
							elseif ($context['browse_type'] == 'avatars')
429
								$link .= sprintf('%1$s?action=dlattach;type=avatar;attach=%2$d', $scripturl, $rowData['id_attach']);
430
431
							// Normal attachments are always linked to a topic ID.
432
							else
433
								$link .= sprintf('%1$s?action=dlattach;topic=%2$d.0;attach=%3$d', $scripturl, $rowData['id_topic'], $rowData['id_attach']);
434
435
							$link .= '"';
436
437
							// Show a popup on click if it's a picture and we know its dimensions.
438
							if (!empty($rowData['width']) && !empty($rowData['height']))
439
								$link .= sprintf(' onclick="return reqWin(this.href' . ($rowData['attachment_type'] == 1 ? '' : ' + \';image\'') . ', %1$d, %2$d, true);"', $rowData['width'] + 20, $rowData['height'] + 20);
440
441
							$link .= sprintf('>%1$s</a>', preg_replace('~&amp;#(\\\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\\\1;', htmlspecialchars($rowData['filename'], ENT_COMPAT, 'UTF-8')));
442
443
							// Show the dimensions.
444
							if (!empty($rowData['width']) && !empty($rowData['height']))
445
								$link .= sprintf(' <span class="smalltext">%1$dx%2$d</span>', $rowData['width'], $rowData['height']);
446
447
							return $link;
448
						},
449
					),
450
					'sort' => array(
451
						'default' => 'a.filename',
452
						'reverse' => 'a.filename DESC',
453
					),
454
				),
455
				'filesize' => array(
456
					'header' => array(
457
						'value' => $txt['attachment_file_size'],
458
						'class' => 'nowrap',
459
					),
460
					'data' => array(
461
						'function' => function ($rowData) {
462
							return byte_format($rowData['size']);
463
						},
464
					),
465
					'sort' => array(
466
						'default' => 'a.size',
467
						'reverse' => 'a.size DESC',
468
					),
469
				),
470
				'member' => array(
471
					'header' => array(
472
						'value' => $context['browse_type'] == 'avatars' ? $txt['attachment_manager_member'] : $txt['posted_by'],
473
						'class' => 'nowrap',
474
					),
475
					'data' => array(
476
						'function' => function ($rowData) {
477
							global $scripturl;
478
479
							// In case of an attachment, return the poster of the attachment.
480
							if (empty($rowData['id_member']))
481
								return htmlspecialchars($rowData['poster_name'], ENT_COMPAT, 'UTF-8');
482
483
							// Otherwise it must be an avatar, return the link to the owner of it.
484
							else
485
								return sprintf('<a href="%1$s?action=profile;u=%2$d">%3$s</a>', $scripturl, $rowData['id_member'], $rowData['poster_name']);
486
						},
487
					),
488
					'sort' => array(
489
						'default' => 'mem.real_name',
490
						'reverse' => 'mem.real_name DESC',
491
					),
492
				),
493
				'date' => array(
494
					'header' => array(
495
						'value' => $context['browse_type'] == 'avatars' ? $txt['attachment_manager_last_active'] : $txt['date'],
496
						'class' => 'nowrap',
497
					),
498
					'data' => array(
499
						'function' => function ($rowData) {
500
							global $txt, $context, $scripturl;
501
502
							// The date the message containing the attachment was posted or the owner of the avatar was active.
503
							$date = empty($rowData['poster_time']) ? $txt['never'] : standardTime($rowData['poster_time']);
504
505
							// Add a link to the topic in case of an attachment.
506
							if ($context['browse_type'] !== 'avatars')
507
								$date .= sprintf('<br />%1$s <a href="%2$s?topic=%3$d.msg%4$d#msg%4$d">%5$s</a>', $txt['in'], $scripturl, $rowData['id_topic'], $rowData['id_msg'], $rowData['subject']);
508
509
							return $date;
510
							},
511
					),
512
					'sort' => array(
513
						'default' => $context['browse_type'] === 'avatars' ? 'mem.last_login' : 'm.id_msg',
514
						'reverse' => $context['browse_type'] === 'avatars' ? 'mem.last_login DESC' : 'm.id_msg DESC',
515
					),
516
				),
517
				'downloads' => array(
518
					'header' => array(
519
						'value' => $txt['downloads'],
520
						'class' => 'nowrap',
521
					),
522
					'data' => array(
523
						'db' => 'downloads',
524
						'comma_format' => true,
525
					),
526
					'sort' => array(
527
						'default' => 'a.downloads',
528
						'reverse' => 'a.downloads DESC',
529
					),
530
				),
531
				'check' => array(
532
					'header' => array(
533
						'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />',
534
						'class' => 'centertext',
535
					),
536
					'data' => array(
537
						'sprintf' => array(
538
							'format' => '<input type="checkbox" name="remove[%1$d]" class="input_check" />',
539
							'params' => array(
540
								'id_attach' => false,
541
							),
542
						),
543
						'class' => 'centertext',
544
					),
545
				),
546
			),
547
			'form' => array(
548
				'href' => $scripturl . '?action=admin;area=manageattachments;sa=remove' . ($context['browse_type'] === 'avatars' ? ';avatars' : ($context['browse_type'] === 'thumbs' ? ';thumbs' : '')),
549
				'include_sort' => true,
550
				'include_start' => true,
551
				'hidden_fields' => array(
552
					'type' => $context['browse_type'],
553
				),
554
			),
555
			'additional_rows' => array(
556
				array(
557
					'position' => 'below_table_data',
558
					'value' => '<input type="submit" name="remove_submit" class="right_submit" value="' . $txt['quickmod_delete_selected'] . '" onclick="return confirm(\'' . $txt['confirm_delete_attachments'] . '\');" />',
559
				),
560
			),
561
			'list_menu' => array(
562
				'show_on' => 'top',
563
				'links' => array(
564
					array(
565
						'href' => $scripturl . '?action=admin;area=manageattachments;sa=browse',
566
						'is_selected' => $context['browse_type'] === 'attachments',
567
						'label' => $txt['attachment_manager_attachments']
568
					),
569
					array(
570
						'href' => $scripturl . '?action=admin;area=manageattachments;sa=browse;avatars',
571
						'is_selected' => $context['browse_type'] === 'avatars',
572
						'label' => $txt['attachment_manager_avatars']
573
					),
574
					array(
575
						'href' => $scripturl . '?action=admin;area=manageattachments;sa=browse;thumbs',
576
						'is_selected' => $context['browse_type'] === 'thumbs',
577
						'label' => $txt['attachment_manager_thumbs']
578
					),
579
				),
580
			),
581
		);
582
583
		// Create the list.
584
		createList($listOptions);
585
	}
586
587
	/**
588
	 * Show several file maintenance options.
589
	 *
590
	 * What it does:
591
	 *
592
	 * - Called by ?action=admin;area=manageattachments;sa=maintain.
593
	 * - Calculates file statistics (total file size, number of attachments,
594
	 * number of avatars, attachment space available).
595
	 *
596
	 * @uses the 'maintenance' sub template.
597
	 */
598
	public function action_maintenance()
599
	{
600
		global $context, $modSettings;
601
602
		loadTemplate('ManageAttachments');
603
		$context['sub_template'] = 'maintenance';
604
605
		// We need our attachments directories...
606
		$attach_dirs = getAttachmentDirs();
607
608
		// Get the number of attachments...
609
		$context['num_attachments'] = comma_format(getAttachmentCount(), 0);
610
611
		// Also get the avatar amount...
612
		$context['num_avatars'] = comma_format(getAvatarCount(), 0);
613
614
		// Total size of attachments
615
		$context['attachment_total_size'] = overallAttachmentsSize();
616
617
		// Total size and files from the current attachments dir.
618
		$current_dir = currentAttachDirProperties();
619
620
		// If they specified a limit only....
621
		if (!empty($modSettings['attachmentDirSizeLimit']))
622
			$context['attachment_space'] = comma_format(max($modSettings['attachmentDirSizeLimit'] - $current_dir['size'], 0), 2);
623
		$context['attachment_current_size'] = byte_format($current_dir['size']);
624
625
		if (!empty($modSettings['attachmentDirFileLimit']))
626
			$context['attachment_files'] = comma_format(max($modSettings['attachmentDirFileLimit'] - $current_dir['files'], 0), 0);
627
		$context['attachment_current_files'] = comma_format($current_dir['files'], 0);
628
629
		$context['attach_multiple_dirs'] = count($attach_dirs) > 1 ? true : false;
630
		$context['attach_dirs'] = $attach_dirs;
631
		$context['base_dirs'] = !empty($modSettings['attachment_basedirectories']) ? Util::unserialize($modSettings['attachment_basedirectories']) : array();
632
		$context['checked'] = $this->_req->getSession('checked', true);
633
		if (!empty($this->_req->session->results))
634
		{
635
			$context['results'] = implode('<br />', $this->_req->session->results);
636
			unset($_SESSION['results']);
637
		}
638
	}
639
640
	/**
641
	 * Move avatars from their current location, to the custom_avatar_dir folder.
642
	 *
643
	 * - Called from the maintenance screen by ?action=admin;area=manageattachments;sa=action_moveAvatars.
644
	 */
645
	public function action_moveAvatars()
646
	{
647
		global $modSettings;
648
649
		// First make sure the custom avatar dir is writable.
650
		if (!is_writable($modSettings['custom_avatar_dir']))
651
		{
652
			// Try to fix it.
653
			@chmod($modSettings['custom_avatar_dir'], 0777);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

653
			/** @scrutinizer ignore-unhandled */ @chmod($modSettings['custom_avatar_dir'], 0777);

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...
654
655
			// Guess that didn't work?
656
			if (!is_writable($modSettings['custom_avatar_dir']))
657
				throw new Elk_Exception('attachments_no_write', 'critical');
658
		}
659
660
		// Finally move the attachments..
661
		moveAvatars();
662
663
		redirectexit('action=admin;area=manageattachments;sa=maintenance');
664
	}
665
666
	/**
667
	 * Remove attachments older than a given age.
668
	 *
669
	 * - Called from the maintenance screen by ?action=admin;area=manageattachments;sa=byAge.
670
	 * - It optionally adds a certain text to the messages the attachments were removed from.
671
	 * @todo refactor this silly superglobals use...
672
	 */
673
	public function action_byAge()
674
	{
675
		checkSession('post', 'admin');
676
677
		// @todo Ignore messages in topics that are stickied?
678
679
		// Deleting an attachment?
680
		if ($this->_req->getQuery('type', 'strval') !== 'avatars')
681
		{
682
			// Get rid of all the old attachments.
683
			$messages = removeAttachments(array('attachment_type' => 0, 'poster_time' => (time() - 24 * 60 * 60 * $this->_req->post->age)), 'messages', true);
684
685
			// Update the messages to reflect the change.
686
			if (!empty($messages) && !empty($this->_req->post->notice))
687
				setRemovalNotice($messages, $this->_req->post->notice);
688
		}
689
		else
690
		{
691
			// Remove all the old avatars.
692
			removeAttachments(array('not_id_member' => 0, 'last_login' => (time() - 24 * 60 * 60 * $this->_req->post->age)), 'members');
693
		}
694
695
		redirectexit('action=admin;area=manageattachments' . (empty($this->_req->query->avatars) ? ';sa=maintenance' : ';avatars'));
696
	}
697
698
	/**
699
	 * Remove attachments larger than a given size.
700
	 *
701
	 * - Called from the maintenance screen by ?action=admin;area=manageattachments;sa=bySize.
702
	 * - Optionally adds a certain text to the messages the attachments were removed from.
703
	 */
704
	public function action_bySize()
705
	{
706
		checkSession('post', 'admin');
707
708
		// Find humongous attachments.
709
		$messages = removeAttachments(array('attachment_type' => 0, 'size' => 1024 * $this->_req->post->size), 'messages', true);
710
711
		// And make a note on the post.
712
		if (!empty($messages) && !empty($this->_req->post->notice))
713
			setRemovalNotice($messages, $this->_req->post->notice);
714
715
		redirectexit('action=admin;area=manageattachments;sa=maintenance');
716
	}
717
718
	/**
719
	 * Remove a selection of attachments or avatars.
720
	 *
721
	 * - Called from the browse screen as submitted form by ?action=admin;area=manageattachments;sa=remove
722
	 */
723
	public function action_remove()
724
	{
725
		global $txt, $language, $user_info;
726
727
		checkSession('post');
728
729
		if (!empty($this->_req->post->remove))
730
		{
731
			// There must be a quicker way to pass this safety test??
732
			$attachments = array();
733
			foreach ($this->_req->post->remove as $removeID => $dummy)
734
				$attachments[] = (int) $removeID;
735
736
			if ($this->_req->query->type == 'avatars' && !empty($attachments))
737
				removeAttachments(array('id_attach' => $attachments));
738
			elseif (!empty($attachments))
739
			{
740
				$messages = removeAttachments(array('id_attach' => $attachments), 'messages', true);
741
742
				// And change the message to reflect this.
743
				if (!empty($messages))
744
				{
745
					loadLanguage('index', $language, true);
746
					setRemovalNotice($messages, $txt['attachment_delete_admin']);
747
					loadLanguage('index', $user_info['language'], true);
748
				}
749
			}
750
		}
751
752
		$sort = $this->_req->getQuery('sort', 'strval', 'date');
753
		redirectexit('action=admin;area=manageattachments;sa=browse;' . $this->_req->query->type . ';sort=' . $sort . (isset($this->_req->query->desc) ? ';desc' : '') . ';start=' . $this->_req->query->start);
754
	}
755
756
	/**
757
	 * Removes all attachments in a single click
758
	 *
759
	 * - Called from the maintenance screen by ?action=admin;area=manageattachments;sa=removeall.
760
	 */
761
	public function action_removeall()
762
	{
763
		global $txt;
764
765
		checkSession('get', 'admin');
766
767
		$messages = removeAttachments(array('attachment_type' => 0), '', true);
768
769
		$notice = $this->_req->getPost('notice', 'strval', $txt['attachment_delete_admin']);
770
771
		// Add the notice on the end of the changed messages.
772
		if (!empty($messages))
773
			setRemovalNotice($messages, $notice);
774
775
		redirectexit('action=admin;area=manageattachments;sa=maintenance');
776
	}
777
778
	/**
779
	 * This function will performs many attachment checks and provides ways to fix them
780
	 *
781
	 * What it does:
782
	 *
783
	 * - Checks for the following common issues
784
	 * - Orphan Thumbnails
785
	 * - Attachments that have no thumbnails
786
	 * - Attachments that list thumbnails, but actually, don't have any
787
	 * - Attachments list in the wrong_folder
788
	 * - Attachments that don't exists on disk any longer
789
	 * - Attachments that are zero size
790
	 * - Attachments that file size does not match the DB size
791
	 * - Attachments that no longer have a message
792
	 * - Avatars with no members associated with them.
793
	 * - Attachments that are in the attachment folder, but not listed in the DB
794
	 */
795
	public function action_repair()
796
	{
797
		global $modSettings, $context, $txt;
798
799
		checkSession('get');
800
801
		// If we choose cancel, redirect right back.
802
		if (isset($this->_req->post->cancel))
803
			redirectexit('action=admin;area=manageattachments;sa=maintenance');
804
805
		// Try give us a while to sort this out...
806
		detectServer()->setTimeLimit(600);
807
808
		$this->step = $this->_req->getQuery('step', 'intval', 0);
809
		$this->substep = $this->_req->getQuery('substep', 'intval', 0);
810
		$this->starting_substep = $this->substep;
811
812
		// Don't recall the session just in case.
813
		if ($this->step === 0 && $this->substep === 0)
814
		{
815
			unset($_SESSION['attachments_to_fix'], $_SESSION['attachments_to_fix2']);
816
817
			// If we're actually fixing stuff - work out what.
818
			if (isset($this->_req->query->fixErrors))
819
			{
820
				// Nothing?
821
				if (empty($this->_req->post->to_fix))
822
					redirectexit('action=admin;area=manageattachments;sa=maintenance');
823
824
				foreach ($this->_req->post->to_fix as $key => $value)
825
					$_SESSION['attachments_to_fix'][] = $value;
826
			}
827
		}
828
829
		// All the valid problems are here:
830
		$context['repair_errors'] = array(
831
			'missing_thumbnail_parent' => 0,
832
			'parent_missing_thumbnail' => 0,
833
			'file_missing_on_disk' => 0,
834
			'file_wrong_size' => 0,
835
			'file_size_of_zero' => 0,
836
			'attachment_no_msg' => 0,
837
			'avatar_no_member' => 0,
838
			'wrong_folder' => 0,
839
			'missing_extension' => 0,
840
			'files_without_attachment' => 0,
841
		);
842
843
		$to_fix = !empty($this->_req->session->attachments_to_fix) ? $this->_req->session->attachments_to_fix : array();
844
		$context['repair_errors'] = $this->_req->getSession('attachments_to_fix2', $context['repair_errors']);
845
		$fix_errors = isset($this->_req->query->fixErrors) ? true : false;
846
847
		// Get stranded thumbnails.
848
		if ($this->step <= 0)
849
		{
850
			$thumbnails = getMaxThumbnail();
851
852
			for (; $this->substep < $thumbnails; $this->substep += 500)
853
			{
854
				$removed = findOrphanThumbnails($this->substep, $fix_errors, $to_fix);
855
				$context['repair_errors']['missing_thumbnail_parent'] += count($removed);
856
857
				$this->_pauseAttachmentMaintenance($to_fix, $thumbnails);
858
			}
859
860
			// Done here, on to the next
861
			$this->step = 1;
862
			$this->substep = 0;
863
			$this->_pauseAttachmentMaintenance($to_fix);
864
		}
865
866
		// Find parents which think they have thumbnails, but actually, don't.
867
		if ($this->step <= 1)
868
		{
869
			$thumbnails = maxNoThumb();
870
871
			for (; $this->substep < $thumbnails; $this->substep += 500)
872
			{
873
				$to_update = findParentsOrphanThumbnails($this->substep, $fix_errors, $to_fix);
874
				$context['repair_errors']['parent_missing_thumbnail'] += count($to_update);
875
876
				$this->_pauseAttachmentMaintenance($to_fix, $thumbnails);
877
			}
878
879
			// Another step done, but many to go
880
			$this->step = 2;
881
			$this->substep = 0;
882
			$this->_pauseAttachmentMaintenance($to_fix);
883
		}
884
885
		// This may take forever I'm afraid, but life sucks... recount EVERY attachments!
886
		if ($this->step <= 2)
887
		{
888
			$thumbnails = maxAttachment();
889
890
			for (; $this->substep < $thumbnails; $this->substep += 250)
891
			{
892
				$repair_errors = repairAttachmentData($this->substep, $fix_errors, $to_fix);
893
894
				foreach ($repair_errors as $key => $value)
895
					$context['repair_errors'][$key] += $value;
896
897
				$this->_pauseAttachmentMaintenance($to_fix, $thumbnails);
898
			}
899
900
			// And onward we go
901
			$this->step = 3;
902
			$this->substep = 0;
903
			$this->_pauseAttachmentMaintenance($to_fix);
904
		}
905
906
		// Get avatars with no members associated with them.
907
		if ($this->step <= 3)
908
		{
909
			$thumbnails = maxAttachment();
910
911
			for (; $this->substep < $thumbnails; $this->substep += 500)
912
			{
913
				$to_remove = findOrphanAvatars($this->substep, $fix_errors, $to_fix);
914
				$context['repair_errors']['avatar_no_member'] += count($to_remove);
915
916
				$this->_pauseAttachmentMaintenance($to_fix, $thumbnails);
917
			}
918
919
			$this->step = 4;
920
			$this->substep = 0;
921
			$this->_pauseAttachmentMaintenance($to_fix);
922
		}
923
924
		// What about attachments, who are missing a message :'(
925
		if ($this->step <= 4)
926
		{
927
			$thumbnails = maxAttachment();
928
929
			for (; $this->substep < $thumbnails; $this->substep += 500)
930
			{
931
				$to_remove = findOrphanAttachments($this->substep, $fix_errors, $to_fix);
932
				$context['repair_errors']['attachment_no_msg'] += count($to_remove);
933
934
				$this->_pauseAttachmentMaintenance($to_fix, $thumbnails);
935
			}
936
937
			$this->step = 5;
938
			$this->substep = 0;
939
			$this->_pauseAttachmentMaintenance($to_fix);
940
		}
941
942
		// What about files who are not recorded in the database?
943
		if ($this->step <= 5)
944
		{
945
			// Just use the current path for temp files.
946
			if (!is_array($modSettings['attachmentUploadDir']))
947
				$modSettings['attachmentUploadDir'] = Util::unserialize($modSettings['attachmentUploadDir']);
948
949
			$attach_dirs = $modSettings['attachmentUploadDir'];
950
			$current_check = 0;
951
			$max_checks = 500;
952
953
			$files_checked = empty($this->substep) ? 0 : $this->substep;
954
			foreach ($attach_dirs as $attach_dir)
955
			{
956
				try
957
				{
958
					$files = new FilesystemIterator($attach_dir, FilesystemIterator::SKIP_DOTS);
959
					foreach ($files as $file)
960
					{
961
						if ($file->getFilename() === '.htaccess')
962
							continue;
963
964
						if ($files_checked <= $current_check)
965
						{
966
							// Temporary file, get rid of it!
967
							if (strpos($file->getFilename(), 'post_tmp_') !== false)
968
							{
969
								// Temp file is more than 5 hours old!
970
								if ($file->getMTime() < time() - 18000)
971
									@unlink($file->getPathname());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

971
									/** @scrutinizer ignore-unhandled */ @unlink($file->getPathname());

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...
972
							}
973
							// That should be an attachment, let's check if we have it in the database
974
							elseif (strpos($file->getFilename(), '_') !== false)
975
							{
976
								$attachID = (int) substr($file->getFilename(), 0, strpos($file->getFilename(), '_'));
977
								if (!empty($attachID))
978
								{
979
									if (!validateAttachID($attachID))
980
									{
981
										if ($fix_errors && in_array('files_without_attachment', $to_fix))
982
											@unlink($file->getPathname());
983
										else
984
											$context['repair_errors']['files_without_attachment']++;
985
									}
986
								}
987
							}
988
							elseif ($file->getFilename() !== 'index.php' && !$file->isDir())
989
							{
990
								if ($fix_errors && in_array('files_without_attachment', $to_fix))
991
									@unlink($file->getPathname());
992
								else
993
									$context['repair_errors']['files_without_attachment']++;
994
							}
995
						}
996
						$current_check++;
997
						$this->substep = (int) $current_check;
998
999
						if ($current_check - $files_checked >= $max_checks)
1000
							$this->_pauseAttachmentMaintenance($to_fix);
1001
					}
1002
				}
1003
				catch (UnexpectedValueException $e)
1004
				{
1005
					// @todo for now do nothing...
1006
				}
1007
			}
1008
1009
			$this->step = 5;
1010
			$this->substep = 0;
1011
			$this->_pauseAttachmentMaintenance($to_fix);
1012
		}
1013
1014
		// Got here we must be doing well - just the template! :D
1015
		$context['page_title'] = $txt['repair_attachments'];
1016
		$context[$context['admin_menu_name']]['current_subsection'] = 'maintenance';
1017
		$context['sub_template'] = 'attachment_repair';
1018
1019
		// What stage are we at?
1020
		$context['completed'] = $fix_errors ? true : false;
1021
		$context['errors_found'] = false;
1022
		foreach ($context['repair_errors'] as $number)
1023
		{
1024
			if (!empty($number))
1025
			{
1026
				$context['errors_found'] = true;
1027
				break;
1028
			}
1029
		}
1030
	}
1031
1032
	/**
1033
	 * This function lists and allows updating of multiple attachments paths.
1034
	 */
1035
	public function action_attachpaths()
1036
	{
1037
		global $modSettings, $scripturl, $context, $txt;
1038
1039
		// Since this needs to be done eventually.
1040
		if (!is_array($modSettings['attachmentUploadDir']))
1041
			$modSettings['attachmentUploadDir'] = Util::unserialize($modSettings['attachmentUploadDir']);
1042
1043
		if (!isset($modSettings['attachment_basedirectories']))
1044
			$modSettings['attachment_basedirectories'] = array();
1045
		elseif (!is_array($modSettings['attachment_basedirectories']))
1046
			$modSettings['attachment_basedirectories'] = Util::unserialize($modSettings['attachment_basedirectories']);
1047
1048
		$errors = array();
1049
1050
		// Saving?
1051
		if (isset($this->_req->post->save))
1052
		{
1053
			checkSession();
1054
1055
			$this->current_dir = $this->_req->getPost('current_dir', 'intval', 0);
1056
			$new_dirs = array();
1057
1058
			require_once(SUBSDIR . '/Themes.subs.php');
1059
			$themes = installedThemes();
1060
			$reserved_dirs = array(BOARDDIR, SOURCEDIR, SUBSDIR, CONTROLLERDIR, CACHEDIR, EXTDIR, LANGUAGEDIR, ADMINDIR);
1061
			foreach ($themes as $theme)
1062
				$reserved_dirs[] = $theme['theme_dir'];
1063
1064
			foreach ($this->_req->post->dirs as $id => $path)
1065
			{
1066
				$error = '';
1067
				$id = (int) $id;
1068
				if ($id < 1)
1069
					continue;
1070
1071
				$real_path = rtrim(trim($path), DIRECTORY_SEPARATOR);
1072
1073
				// If it doesn't look like a directory, probably is not a directory
1074
				if (preg_match('~[/\\\\]~', $real_path) !== 1)
1075
					$real_path = realpath(BOARDDIR . DIRECTORY_SEPARATOR . ltrim($real_path, DIRECTORY_SEPARATOR));
1076
1077
				// Hmm, a new path maybe?
1078
				if (!array_key_exists($id, $modSettings['attachmentUploadDir']))
1079
				{
1080
					// or is it?
1081
					if (in_array($path, $modSettings['attachmentUploadDir']) || in_array(BOARDDIR . DIRECTORY_SEPARATOR . $path, $modSettings['attachmentUploadDir']))
1082
					{
1083
						$errors[] = $path . ': ' . $txt['attach_dir_duplicate_msg'];
1084
						continue;
1085
					}
1086
1087
					// or is it a system dir?
1088
					if (in_array($real_path, $reserved_dirs))
1089
					{
1090
						$errors[] = $real_path . ': ' . $txt['attach_dir_reserved'];
1091
						continue;
1092
					}
1093
1094
					// OK, so let's try to create it then.
1095
					if (automanage_attachments_create_directory($path))
1096
						$this->current_dir = $modSettings['currentAttachmentUploadDir'];
1097
					else
1098
						$errors[] = $path . ': ' . $txt[$context['dir_creation_error']];
1099
				}
1100
1101
				// Changing a directory name?
1102
				if (!empty($modSettings['attachmentUploadDir'][$id]) && !empty($path) && $real_path != $modSettings['attachmentUploadDir'][$id])
1103
				{
1104
					if ($real_path != $modSettings['attachmentUploadDir'][$id] && !is_dir($real_path))
1105
					{
1106
						if (!@rename($modSettings['attachmentUploadDir'][$id], $real_path))
1107
						{
1108
							$errors[] = $real_path . ': ' . $txt['attach_dir_no_rename'];
1109
							$real_path = $modSettings['attachmentUploadDir'][$id];
1110
						}
1111
					}
1112
					else
1113
					{
1114
						$errors[] = $real_path . ': ' . $txt['attach_dir_exists_msg'];
1115
						$real_path = $modSettings['attachmentUploadDir'][$id];
1116
					}
1117
1118
					// Update the base directory path
1119
					if (!empty($modSettings['attachment_basedirectories']) && array_key_exists($id, $modSettings['attachment_basedirectories']))
1120
					{
1121
						$base = $modSettings['basedirectory_for_attachments'] == $modSettings['attachmentUploadDir'][$id] ? $real_path : $modSettings['basedirectory_for_attachments'];
1122
1123
						$modSettings['attachment_basedirectories'][$id] = $real_path;
1124
						updateSettings(array(
1125
							'attachment_basedirectories' => serialize($modSettings['attachment_basedirectories']),
1126
							'basedirectory_for_attachments' => $base,
1127
						));
1128
						$modSettings['attachment_basedirectories'] = Util::unserialize($modSettings['attachment_basedirectories']);
1129
					}
1130
				}
1131
1132
				if (empty($path))
1133
				{
1134
					$real_path = $modSettings['attachmentUploadDir'][$id];
1135
1136
					// It's not a good idea to delete the current directory.
1137
					if ($id == (!empty($this->current_dir) ? $this->current_dir : $modSettings['currentAttachmentUploadDir']))
1138
						$errors[] = $real_path . ': ' . $txt['attach_dir_is_current'];
1139
					// Or the current base directory
1140
					elseif (!empty($modSettings['basedirectory_for_attachments']) && $modSettings['basedirectory_for_attachments'] == $modSettings['attachmentUploadDir'][$id])
1141
						$errors[] = $real_path . ': ' . $txt['attach_dir_is_current_bd'];
1142
					else
1143
					{
1144
						// Let's not try to delete a path with files in it.
1145
						$num_attach = countAttachmentsInFolders($id);
1146
1147
						// A check to see if it's a used base dir.
1148
						if (!empty($modSettings['attachment_basedirectories']))
1149
						{
1150
							// Count any sub-folders.
1151
							foreach ($modSettings['attachmentUploadDir'] as $sub)
1152
								if (strpos($sub, $real_path . DIRECTORY_SEPARATOR) !== false)
1153
									$num_attach++;
1154
						}
1155
1156
						// It's safe to delete. So try to delete the folder also
1157
						if ($num_attach == 0)
1158
						{
1159
							if (is_dir($real_path))
1160
								$doit = true;
1161
							elseif (is_dir(BOARDDIR . DIRECTORY_SEPARATOR . $real_path))
1162
							{
1163
								$doit = true;
1164
								$real_path = BOARDDIR . DIRECTORY_SEPARATOR . $real_path;
1165
							}
1166
1167
							if (isset($doit))
1168
							{
1169
								unlink($real_path . '/.htaccess');
1170
								unlink($real_path . '/index.php');
1171
								if (!@rmdir($real_path))
1172
									$error = $real_path . ': ' . $txt['attach_dir_no_delete'];
1173
							}
1174
1175
							// Remove it from the base directory list.
1176
							if (empty($error) && !empty($modSettings['attachment_basedirectories']))
1177
							{
1178
								unset($modSettings['attachment_basedirectories'][$id]);
1179
								updateSettings(array('attachment_basedirectories' => serialize($modSettings['attachment_basedirectories'])));
1180
								$modSettings['attachment_basedirectories'] = Util::unserialize($modSettings['attachment_basedirectories']);
1181
							}
1182
						}
1183
						else
1184
							$error = $real_path . ': ' . $txt['attach_dir_no_remove'];
1185
1186
						if (empty($error))
1187
							continue;
1188
						else
1189
							$errors[] = $error;
1190
					}
1191
				}
1192
1193
				$new_dirs[$id] = $real_path;
1194
			}
1195
1196
			// We need to make sure the current directory is right.
1197
			if (empty($this->current_dir) && !empty($modSettings['currentAttachmentUploadDir']))
1198
				$this->current_dir = $modSettings['currentAttachmentUploadDir'];
1199
1200
			// Find the current directory if there's no value carried,
1201
			if (empty($this->current_dir) || empty($new_dirs[$this->current_dir]))
1202
			{
1203
				if (array_key_exists($modSettings['currentAttachmentUploadDir'], $modSettings['attachmentUploadDir']))
1204
					$this->current_dir = $modSettings['currentAttachmentUploadDir'];
1205
				else
1206
					$this->current_dir = max(array_keys($modSettings['attachmentUploadDir']));
1207
			}
1208
1209
			// If the user wishes to go back, update the last_dir array
1210
			if ($this->current_dir != $modSettings['currentAttachmentUploadDir'] && !empty($modSettings['last_attachments_directory']) && (isset($modSettings['last_attachments_directory'][$this->current_dir]) || isset($modSettings['last_attachments_directory'][0])))
1211
			{
1212
				if (!is_array($modSettings['last_attachments_directory']))
1213
					$modSettings['last_attachments_directory'] = Util::unserialize($modSettings['last_attachments_directory']);
1214
1215
				$num = substr(strrchr($modSettings['attachmentUploadDir'][$this->current_dir], '_'), 1);
1216
				if (is_numeric($num))
1217
				{
1218
					// Need to find the base folder.
1219
					$bid = -1;
1220
					$use_subdirectories_for_attachments = 0;
1221
					if (!empty($modSettings['attachment_basedirectories']))
1222
						foreach ($modSettings['attachment_basedirectories'] as $bid => $base)
1223
						{
1224
							if (strpos($modSettings['attachmentUploadDir'][$this->current_dir], $base . DIRECTORY_SEPARATOR) !== false)
1225
							{
1226
								$use_subdirectories_for_attachments = 1;
1227
								break;
1228
							}
1229
						}
1230
1231
					if ($use_subdirectories_for_attachments == 0 && strpos($modSettings['attachmentUploadDir'][$this->current_dir], BOARDDIR . DIRECTORY_SEPARATOR) !== false)
1232
						$bid = 0;
1233
1234
					$modSettings['last_attachments_directory'][$bid] = (int) $num;
1235
					$modSettings['basedirectory_for_attachments'] = !empty($modSettings['basedirectory_for_attachments']) ? $modSettings['basedirectory_for_attachments'] : '';
1236
					$modSettings['use_subdirectories_for_attachments'] = !empty($modSettings['use_subdirectories_for_attachments']) ? $modSettings['use_subdirectories_for_attachments'] : 0;
1237
					updateSettings(array(
1238
						'last_attachments_directory' => serialize($modSettings['last_attachments_directory']),
1239
						'basedirectory_for_attachments' => $bid == 0 ? $modSettings['basedirectory_for_attachments'] : $modSettings['attachment_basedirectories'][$bid],
1240
						'use_subdirectories_for_attachments' => $use_subdirectories_for_attachments,
1241
					));
1242
				}
1243
			}
1244
1245
			// Going back to just one path?
1246
			if (count($new_dirs) == 1)
1247
			{
1248
				// We might need to reset the paths. This loop will just loop through once.
1249
				foreach ($new_dirs as $id => $dir)
1250
				{
1251
					if ($id != 1)
1252
						updateAttachmentIdFolder($id, 1);
1253
1254
					$update = array(
1255
						'currentAttachmentUploadDir' => 1,
1256
						'attachmentUploadDir' => serialize(array(1 => $dir)),
1257
					);
1258
				}
1259
			}
1260
			else
1261
			{
1262
				// Save it to the database.
1263
				$update = array(
1264
					'currentAttachmentUploadDir' => $this->current_dir,
1265
					'attachmentUploadDir' => serialize($new_dirs),
1266
				);
1267
			}
1268
1269
			if (!empty($update))
1270
				updateSettings($update);
1271
1272
			if (!empty($errors))
1273
				$_SESSION['errors']['dir'] = $errors;
1274
1275
			redirectexit('action=admin;area=manageattachments;sa=attachpaths;' . $context['session_var'] . '=' . $context['session_id']);
1276
		}
1277
1278
		// Saving a base directory?
1279
		if (isset($this->_req->post->save2))
1280
		{
1281
			checkSession();
1282
1283
			// Changing the current base directory?
1284
			$this->current_base_dir = $this->_req->getQuery('current_base_dir', 'intval');
1285
			if (empty($this->_req->post->new_base_dir) && !empty($this->current_base_dir))
1286
			{
1287
				if ($modSettings['basedirectory_for_attachments'] != $modSettings['attachmentUploadDir'][$this->current_base_dir])
1288
					$update = (array(
1289
						'basedirectory_for_attachments' => $modSettings['attachmentUploadDir'][$this->current_base_dir],
1290
					));
1291
			}
1292
1293
			if (isset($this->_req->post->base_dir))
1294
			{
1295
				foreach ($this->_req->post->base_dir as $id => $dir)
1296
				{
1297
					if (!empty($dir) && $dir != $modSettings['attachmentUploadDir'][$id])
1298
					{
1299
						if (@rename($modSettings['attachmentUploadDir'][$id], $dir))
1300
						{
1301
							$modSettings['attachmentUploadDir'][$id] = $dir;
1302
							$modSettings['attachment_basedirectories'][$id] = $dir;
1303
							$update = (array(
1304
								'attachmentUploadDir' => serialize($modSettings['attachmentUploadDir']),
1305
								'attachment_basedirectories' => serialize($modSettings['attachment_basedirectories']),
1306
								'basedirectory_for_attachments' => $modSettings['attachmentUploadDir'][$this->current_base_dir],
1307
							));
1308
						}
1309
					}
1310
1311
					if (empty($dir))
1312
					{
1313
						if ($id == $this->current_base_dir)
1314
						{
1315
							$errors[] = $modSettings['attachmentUploadDir'][$id] . ': ' . $txt['attach_dir_is_current'];
1316
							continue;
1317
						}
1318
1319
						unset($modSettings['attachment_basedirectories'][$id]);
1320
						$update = (array(
1321
							'attachment_basedirectories' => serialize($modSettings['attachment_basedirectories']),
1322
							'basedirectory_for_attachments' => $modSettings['attachmentUploadDir'][$this->current_base_dir],
1323
						));
1324
					}
1325
				}
1326
			}
1327
1328
			// Or adding a new one?
1329
			if (!empty($this->_req->post->new_base_dir))
1330
			{
1331
				$this->_req->post->new_base_dir = htmlspecialchars($this->_req->post->new_base_dir, ENT_QUOTES, 'UTF-8');
1332
1333
				$current_dir = $modSettings['currentAttachmentUploadDir'];
1334
1335
				if (!in_array($this->_req->post->new_base_dir, $modSettings['attachmentUploadDir']))
1336
				{
1337
					if (!automanage_attachments_create_directory($this->_req->post->new_base_dir))
1338
						$errors[] = $this->_req->post->new_base_dir . ': ' . $txt['attach_dir_base_no_create'];
1339
				}
1340
1341
				$modSettings['currentAttachmentUploadDir'] = array_search($this->_req->post->new_base_dir, $modSettings['attachmentUploadDir']);
1342
				if (!in_array($this->_req->post->new_base_dir, $modSettings['attachment_basedirectories']))
1343
					$modSettings['attachment_basedirectories'][$modSettings['currentAttachmentUploadDir']] = $this->_req->post->new_base_dir;
1344
				ksort($modSettings['attachment_basedirectories']);
1345
1346
				$update = (array(
1347
					'attachment_basedirectories' => serialize($modSettings['attachment_basedirectories']),
1348
					'basedirectory_for_attachments' => $this->_req->post->new_base_dir,
1349
					'currentAttachmentUploadDir' => $current_dir,
1350
				));
1351
			}
1352
1353
			if (!empty($errors))
1354
				$_SESSION['errors']['base'] = $errors;
1355
1356
			if (!empty($update))
1357
				updateSettings($update);
1358
1359
			redirectexit('action=admin;area=manageattachments;sa=attachpaths;' . $context['session_var'] . '=' . $context['session_id']);
1360
		}
1361
1362
		if (isset($this->_req->session->errors))
1363
		{
1364
			if (is_array($this->_req->session->errors))
1365
			{
1366
				$errors = array();
1367
				if (!empty($this->_req->session->errors['dir']))
1368
					foreach ($this->_req->session->errors['dir'] as $error)
1369
						$errors['dir'][] = Util::htmlspecialchars($error, ENT_QUOTES);
1370
1371
				if (!empty($this->_req->session->errors['base']))
1372
					foreach ($this->_req->session->errors['base'] as $error)
1373
						$errors['base'][] = Util::htmlspecialchars($error, ENT_QUOTES);
1374
			}
1375
			unset($_SESSION['errors'], $this->_req->session->errors);
1376
		}
1377
1378
		$listOptions = array(
1379
			'id' => 'attach_paths',
1380
			'base_href' => $scripturl . '?action=admin;area=manageattachments;sa=attachpaths;' . $context['session_var'] . '=' . $context['session_id'],
1381
			'title' => $txt['attach_paths'],
1382
			'get_items' => array(
1383
				'function' => 'list_getAttachDirs',
1384
			),
1385
			'columns' => array(
1386
				'current_dir' => array(
1387
					'header' => array(
1388
						'value' => $txt['attach_current'],
1389
						'class' => 'centertext',
1390
					),
1391
					'data' => array(
1392
						'function' => function ($rowData) {
1393
							return '<input type="radio" name="current_dir" value="' . $rowData['id'] . '" ' . ($rowData['current'] ? ' checked="checked"' : '') . (!empty($rowData['disable_current']) ? ' disabled="disabled"' : '') . ' class="input_radio" />';
1394
						},
1395
						'style' => 'width: 10%;',
1396
						'class' => 'centertext',
1397
					),
1398
				),
1399
				'path' => array(
1400
					'header' => array(
1401
						'value' => $txt['attach_path'],
1402
					),
1403
					'data' => array(
1404
						'function' => function ($rowData) {
1405
							return '<input type="hidden" name="dirs[' . $rowData['id'] . ']" value="' . $rowData['path'] . '" /><input type="text" size="40" name="dirs[' . $rowData['id'] . ']" value="' . $rowData['path'] . '"' . (!empty($rowData['disable_base_dir']) ? ' disabled="disabled"' : '') . ' class="input_text"/>';
1406
						},
1407
						'style' => 'width: 40%;',
1408
					),
1409
				),
1410
				'current_size' => array(
1411
					'header' => array(
1412
						'value' => $txt['attach_current_size'],
1413
					),
1414
					'data' => array(
1415
						'db' => 'current_size',
1416
						'style' => 'width: 15%;',
1417
					),
1418
				),
1419
				'num_files' => array(
1420
					'header' => array(
1421
						'value' => $txt['attach_num_files'],
1422
					),
1423
					'data' => array(
1424
						'db' => 'num_files',
1425
						'style' => 'width: 15%;',
1426
					),
1427
				),
1428
				'status' => array(
1429
					'header' => array(
1430
						'value' => $txt['attach_dir_status'],
1431
					),
1432
					'data' => array(
1433
						'db' => 'status',
1434
						'style' => 'width: 25%;',
1435
					),
1436
				),
1437
			),
1438
			'form' => array(
1439
				'href' => $scripturl . '?action=admin;area=manageattachments;sa=attachpaths;' . $context['session_var'] . '=' . $context['session_id'],
1440
			),
1441
			'additional_rows' => array(
1442
				array(
1443
					'class' => 'submitbutton',
1444
					'position' => 'below_table_data',
1445
					'value' => '
1446
					<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '" />
1447
					<input type="submit" name="save" value="' . $txt['save'] . '" class="right_submit" />
1448
					<input type="submit" name="new_path" value="' . $txt['attach_add_path'] . '" class="right_submit" />',
1449
				),
1450
				empty($errors['dir']) ? array(
1451
					'position' => 'top_of_list',
1452
					'value' => $txt['attach_dir_desc'],
1453
					'style' => 'padding: 5px 10px;',
1454
					'class' => 'smalltext'
1455
				) : array(
1456
					'position' => 'top_of_list',
1457
					'value' => $txt['attach_dir_save_problem'] . '<br />' . implode('<br />', $errors['dir']),
1458
					'style' => 'padding-left: 35px;',
1459
					'class' => 'warningbox',
1460
				),
1461
			),
1462
		);
1463
		createList($listOptions);
1464
1465
		if (!empty($modSettings['attachment_basedirectories']))
1466
		{
1467
			$listOptions2 = array(
1468
				'id' => 'base_paths',
1469
				'base_href' => $scripturl . '?action=admin;area=manageattachments;sa=attachpaths;' . $context['session_var'] . '=' . $context['session_id'],
1470
				'title' => $txt['attach_base_paths'],
1471
				'get_items' => array(
1472
					'function' => 'list_getBaseDirs',
1473
				),
1474
				'columns' => array(
1475
					'current_dir' => array(
1476
						'header' => array(
1477
							'value' => $txt['attach_current'],
1478
							'class' => 'centertext',
1479
						),
1480
						'data' => array(
1481
							'function' => function ($rowData) {
1482
								return '<input type="radio" name="current_base_dir" value="' . $rowData['id'] . '" ' . ($rowData['current'] ? ' checked="checked"' : '') . ' class="input_radio" />';
1483
							},
1484
							'style' => 'width: 10%;',
1485
							'class' => 'centertext',
1486
						),
1487
					),
1488
					'path' => array(
1489
						'header' => array(
1490
							'value' => $txt['attach_path'],
1491
						),
1492
						'data' => array(
1493
							'db' => 'path',
1494
							'style' => 'width: 45%;',
1495
						),
1496
					),
1497
					'num_dirs' => array(
1498
						'header' => array(
1499
							'value' => $txt['attach_num_dirs'],
1500
						),
1501
						'data' => array(
1502
							'db' => 'num_dirs',
1503
							'style' => 'width: 15%;',
1504
						),
1505
					),
1506
					'status' => array(
1507
						'header' => array(
1508
							'value' => $txt['attach_dir_status'],
1509
						),
1510
						'data' => array(
1511
							'db' => 'status',
1512
							'style' => 'width: 15%;',
1513
						),
1514
					),
1515
				),
1516
				'form' => array(
1517
					'href' => $scripturl . '?action=admin;area=manageattachments;sa=attachpaths;' . $context['session_var'] . '=' . $context['session_id'],
1518
				),
1519
				'additional_rows' => array(
1520
					array(
1521
						'class' => 'submitbutton',
1522
						'position' => 'below_table_data',
1523
						'value' => '<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '" />
1524
						<input type="submit" name="save2" value="' . $txt['save'] . '" class="right_submit" />
1525
						<input type="submit" name="new_base_path" value="' . $txt['attach_add_path'] . '" class="right_submit" />',
1526
					),
1527
					empty($errors['base']) ? array(
1528
						'position' => 'top_of_list',
1529
						'value' => $txt['attach_dir_base_desc'],
1530
						'style' => 'padding: 5px 10px;',
1531
						'class' => 'smalltext'
1532
					) : array(
1533
						'position' => 'top_of_list',
1534
						'value' => $txt['attach_dir_save_problem'] . '<br />' . implode('<br />', $errors['base']),
1535
						'style' => 'padding-left: 35px',
1536
						'class' => 'warningbox',
1537
					),
1538
				),
1539
			);
1540
			createList($listOptions2);
1541
		}
1542
1543
		// Fix up our template.
1544
		$context[$context['admin_menu_name']]['current_subsection'] = 'attachpaths';
1545
		$context['page_title'] = $txt['attach_path_manage'];
1546
	}
1547
1548
	/**
1549
	 * Maintenance function to move attachments from one directory to another
1550
	 */
1551
	public function action_transfer()
1552
	{
1553
		global $modSettings, $txt;
1554
1555
		checkSession();
1556
1557
		// The list(s) of directory's that are available.
1558
		$modSettings['attachmentUploadDir'] = Util::unserialize($modSettings['attachmentUploadDir']);
1559
		if (!empty($modSettings['attachment_basedirectories']))
1560
			$modSettings['attachment_basedirectories'] = Util::unserialize($modSettings['attachment_basedirectories']);
1561
		else
1562
			$modSettings['basedirectory_for_attachments'] = array();
1563
1564
		// Clean the inputs
1565
		$this->from = $this->_req->getPost('from', 'intval');
1566
		$this->auto = $this->_req->getPost('auto', 'intval', 0);
1567
		$this->to = $this->_req->getPost('to', 'intval');
1568
		$start = !empty($this->_req->post->empty_it) ? 0 : $modSettings['attachmentDirFileLimit'];
1569
		$_SESSION['checked'] = !empty($this->_req->post->empty_it) ? true : false;
1570
1571
		// Prepare for the moving
1572
		$limit = 501;
1573
		$results = array();
1574
		$dir_files = 0;
1575
		$current_progress = 0;
1576
		$total_moved = 0;
1577
		$total_not_moved = 0;
1578
		$total_progress = 0;
1579
1580
		// Need to know where we are moving things from
1581
		if (empty($this->from) || (empty($this->auto) && empty($this->to)))
1582
			$results[] = $txt['attachment_transfer_no_dir'];
1583
1584
		// Same location, that's easy
1585
		if ($this->from == $this->to)
1586
			$results[] = $txt['attachment_transfer_same_dir'];
1587
1588
		// No errors so determine how many we may have to move
1589
		if (empty($results))
1590
		{
1591
			// Get the total file count for the progress bar.
1592
			$total_progress = getFolderAttachmentCount($this->from);
1593
			$total_progress -= $start;
1594
1595
			if ($total_progress < 1)
1596
				$results[] = $txt['attachment_transfer_no_find'];
1597
		}
1598
1599
		// Nothing to move (no files in source or below the max limit)
1600
		if (empty($results))
1601
		{
1602
			// Moving them automatically?
1603
			if (!empty($this->auto))
1604
			{
1605
				$modSettings['automanage_attachments'] = 1;
1606
1607
				// Create sub directory's off the root or from an attachment directory?
1608
				$modSettings['use_subdirectories_for_attachments'] = $this->auto == -1 ? 0 : 1;
1609
				$modSettings['basedirectory_for_attachments'] = $this->auto > 0 ? $modSettings['attachmentUploadDir'][$this->auto] : $modSettings['basedirectory_for_attachments'];
1610
1611
				// Finally, where do they need to go
1612
				automanage_attachments_check_directory();
1613
				$new_dir = $modSettings['currentAttachmentUploadDir'];
1614
			}
1615
			// Or to a specified directory
1616
			else
1617
				$new_dir = $this->to;
1618
1619
			$modSettings['currentAttachmentUploadDir'] = $new_dir;
1620
			$break = false;
1621
			while ($break === false)
1622
			{
1623
				detectServer()->setTimeLimit(300);
1624
1625
				// If limits are set, get the file count and size for the destination folder
1626
				if ($dir_files <= 0 && (!empty($modSettings['attachmentDirSizeLimit']) || !empty($modSettings['attachmentDirFileLimit'])))
1627
				{
1628
					$current_dir = attachDirProperties($new_dir);
1629
					$dir_files = $current_dir['files'];
1630
					$dir_size = $current_dir['size'];
1631
				}
1632
1633
				// Find some attachments to move
1634
				list ($tomove_count, $tomove) = findAttachmentsToMove($this->from, $start, $limit);
1635
1636
				// Nothing found to move
1637
				if ($tomove_count === 0)
1638
				{
1639
					if (empty($current_progress))
1640
						$results[] = $txt['attachment_transfer_no_find'];
1641
					break;
1642
				}
1643
1644
				// No more to move after this batch then set the finished flag.
1645
				if ($tomove_count < $limit)
1646
					$break = true;
1647
1648
				// Move them
1649
				$moved = array();
1650
				$dir_size = empty($dir_size) ? 0 : $dir_size;
1651
				foreach ($tomove as $row)
1652
				{
1653
					$source = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']);
1654
					$dest = $modSettings['attachmentUploadDir'][$new_dir] . '/' . basename($source);
1655
1656
					// Size and file count check
1657
					if (!empty($modSettings['attachmentDirSizeLimit']) || !empty($modSettings['attachmentDirFileLimit']))
1658
					{
1659
						$dir_files++;
1660
						$dir_size += !empty($row['size']) ? $row['size'] : filesize($source);
1661
1662
						// If we've reached a directory limit. Do something if we are in auto mode, otherwise set an error.
1663
						if (!empty($modSettings['attachmentDirSizeLimit']) && $dir_size > $modSettings['attachmentDirSizeLimit'] * 1024 || (!empty($modSettings['attachmentDirFileLimit']) && $dir_files > $modSettings['attachmentDirFileLimit']))
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! empty($modSettings['a...ttachmentDirFileLimit'], Probably Intended Meaning: ! empty($modSettings['at...tachmentDirFileLimit'])
Loading history...
1664
						{
1665
							// Since we're in auto mode. Create a new folder and reset the counters.
1666
							if (!empty($this->auto))
1667
							{
1668
								automanage_attachments_by_space();
1669
1670
								$results[] = sprintf($txt['attachments_transfered'], $total_moved, $modSettings['attachmentUploadDir'][$new_dir]);
1671
								if (!empty($total_not_moved))
1672
									$results[] = sprintf($txt['attachments_not_transfered'], $total_not_moved);
1673
1674
								$dir_files = 0;
1675
								$total_moved = 0;
1676
								$total_not_moved = 0;
1677
1678
								$break = false;
1679
								break;
1680
							}
1681
							// Hmm, not in auto. Time to bail out then...
1682
							else
1683
							{
1684
								$results[] = $txt['attachment_transfer_no_room'];
1685
								$break = true;
1686
								break;
1687
							}
1688
						}
1689
					}
1690
1691
					// Actually move the file
1692
					if (@rename($source, $dest))
1693
					{
1694
						$total_moved++;
1695
						$current_progress++;
1696
						$moved[] = $row['id_attach'];
1697
					}
1698
					else
1699
						$total_not_moved++;
1700
				}
1701
1702
				// Update the database to reflect the new file location
1703
				if (!empty($moved))
1704
					moveAttachments($moved, $new_dir);
1705
1706
				$new_dir = $modSettings['currentAttachmentUploadDir'];
1707
1708
				// Create / update the progress bar.
1709
				// @todo why was this done this way?
1710
				if (!$break)
1711
				{
1712
					$percent_done = min(round($current_progress / $total_progress * 100, 0), 100);
1713
					$prog_bar = '
1714
						<div class="progress_bar">
1715
							<div class="full_bar">' . $percent_done . '%</div>
1716
							<div class="green_percent" style="width: ' . $percent_done . '%;">&nbsp;</div>
1717
						</div>';
1718
1719
					// Write it to a file so it can be displayed
1720
					$fp = fopen(BOARDDIR . '/progress.php', 'w');
1721
					fwrite($fp, $prog_bar);
1722
					fclose($fp);
1723
					usleep(500000);
1724
				}
1725
			}
1726
1727
			$results[] = sprintf($txt['attachments_transfered'], $total_moved, $modSettings['attachmentUploadDir'][$new_dir]);
1728
			if (!empty($total_not_moved))
1729
				$results[] = sprintf($txt['attachments_not_transfered'], $total_not_moved);
1730
		}
1731
1732
		// All done, time to clean up
1733
		$_SESSION['results'] = $results;
1734
		if (file_exists(BOARDDIR . '/progress.php'))
1735
			unlink(BOARDDIR . '/progress.php');
1736
1737
		redirectexit('action=admin;area=manageattachments;sa=maintenance#transfer');
1738
	}
1739
1740
	/**
1741
	 * Function called in-between each round of attachments and avatar repairs.
1742
	 *
1743
	 * What it does:
1744
	 *
1745
	 * - Called by repairAttachments().
1746
	 * - If repairAttachments() has more steps added, this function needs updated!
1747
	 *
1748
	 * @package Attachments
1749
	 * @param mixed[] $to_fix attachments to fix
1750
	 * @param int $max_substep = 0
1751
	 * @todo Move to ManageAttachments.subs.php
1752
	 * @throws Elk_Exception
1753
	 */
1754
	private function _pauseAttachmentMaintenance($to_fix, $max_substep = 0)
1755
	{
1756
		global $context, $txt, $time_start;
1757
1758
		// Try get more time...
1759
		detectServer()->setTimeLimit(600);
1760
1761
		// Have we already used our maximum time?
1762
		if (microtime(true) - $time_start < 3 || $this->starting_substep == $this->substep)
1763
			return;
1764
1765
		$context['continue_get_data'] = '?action=admin;area=manageattachments;sa=repair' . (isset($this->_req->query->fixErrors) ? ';fixErrors' : '') . ';step=' . $this->step . ';substep=' . $this->substep . ';' . $context['session_var'] . '=' . $context['session_id'];
1766
		$context['page_title'] = $txt['not_done_title'];
1767
		$context['continue_post_data'] = '';
1768
		$context['continue_countdown'] = '2';
1769
		$context['sub_template'] = 'not_done';
1770
1771
		// Specific stuff to not break this template!
1772
		$context[$context['admin_menu_name']]['current_subsection'] = 'maintenance';
1773
1774
		// Change these two if more steps are added!
1775
		if (empty($max_substep))
1776
			$context['continue_percent'] = round(($this->step * 100) / 25);
1777
		else
1778
			$context['continue_percent'] = round(($this->step * 100 + ($this->substep * 100) / $max_substep) / 25);
1779
1780
		// Never more than 100%!
1781
		$context['continue_percent'] = min($context['continue_percent'], 100);
1782
1783
		// Save the needed information for the next look
1784
		$_SESSION['attachments_to_fix'] = $to_fix;
1785
		$_SESSION['attachments_to_fix2'] = $context['repair_errors'];
1786
1787
		obExit();
1788
	}
1789
}
1790