Passed
Push — development ( c0e368...7fdcd4 )
by Spuds
01:05 queued 23s
created

ManageMail::action_test_email()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 37
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
cc 6
eloc 20
nc 3
nop 0
dl 0
loc 37
ccs 0
cts 0
cp 0
crap 42
rs 8.9777
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Handles mail configuration, displays the queue and allows for the removal of specific items
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\Helper\Util;
22
use ElkArte\Languages\Loader;
23
use ElkArte\Languages\Txt;
24
use ElkArte\SettingsForm\SettingsForm;
25
use ElkArte\User;
26
27
/**
28
 * This class is the administration mailing controller.
29
 *
30
 * What it does:
31
 *
32
 * - It handles mail configuration,
33
 * - It displays and allows to remove items from the mail queue.
34
 * - It handles sending a test email
35
 *
36
 * @package Mail
37
 */
38
class ManageMail extends AbstractController
39
{
40
	/**
41
	 * Main dispatcher.
42
	 *
43
	 * - This function checks permissions and passes control through to the relevant section.
44
	 *
45
	 * @event integrate_sa_manage_mail Used to add more sub actions
46
	 * @see AbstractController::action_index()
47
	 * @uses Help and MangeMail language files
48
	 */
49
	public function action_index()
50
	{
51
		global $context, $txt;
52
53
		Txt::load('Help+ManageMail');
54
55
		$subActions = array(
56
			'browse' => array($this, 'action_browse', 'permission' => 'admin_forum'),
57
			'clear' => array($this, 'action_clear', 'permission' => 'admin_forum'),
58
			'settings' => array($this, 'action_mailSettings_display', 'permission' => 'admin_forum'),
59
			'test' => array($this, 'action_test_email', 'permission' => 'admin_forum'),
60
		);
61
62
		// Action control
63
		$action = new Action('manage_mail');
64
65
		// By default, we want to browse, call integrate_sa_manage_mail
66
		$subAction = $action->initialize($subActions, 'browse');
67
68
		// Final bits
69
		$context['sub_action'] = $subAction;
70
		$context['page_title'] = $txt['mailqueue_title'];
71
72
		// Load up all the tabs...
73
		$context[$context['admin_menu_name']]['object']->prepareTabData([
74
			'title' => 'mailqueue_title',
75
			'class' => 'i-envelope',
76
			'description' => 'mailqueue_desc',
77
			'tabs' => [
78
				'test' => [
79
					'description' => $txt['mail_send_desc'],
80
				],
81
			]
82
		]);
83
84
		// Call the right function for this sub-action.
85
		$action->dispatch($subAction);
86
	}
87
88
	/**
89
	 * Allows to view and modify the mail settings.
90
	 *
91
	 * @event integrate_save_mail_settings
92
	 * @uses show_settings sub template
93
	 */
94
	public function action_mailSettings_display()
95
	{
96
		global $txt, $context, $txtBirthdayEmails;
97
98
		// Some important context stuff
99
		$context['page_title'] = $txt['mail_settings'];
100
		$context['sub_template'] = 'show_settings';
101
102
		// Initialize the form
103
		$settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER);
104
105
		// Initialize it with our settings
106
		$config_vars = $this->_settings();
107
		$settingsForm->setConfigVars($config_vars);
108
109
		// Piece of redundant code, for the javascript
110
		$processedBirthdayEmails = array();
111
		foreach ($txtBirthdayEmails as $key => $value)
112
		{
113
			$index = substr($key, 0, strrpos($key, '_'));
114
			$element = substr($key, strrpos($key, '_') + 1);
115
			$processedBirthdayEmails[$index][$element] = $value;
116
		}
117
118
		// Saving?
119
		if (isset($this->_req->query->save))
120
		{
121
			// Make the SMTP password a little harder to see in a backup etc.
122
			if (!empty($this->_req->post->smtp_password[1]))
123
			{
124
				$this->_req->post->smtp_password[0] = base64_encode($this->_req->post->smtp_password[0]);
125
				$this->_req->post->smtp_password[1] = base64_encode($this->_req->post->smtp_password[1]);
126
			}
127
128
			checkSession();
129
130
			// We don't want to save the subject and body previews.
131
			unset($config_vars['birthday_subject'], $config_vars['birthday_body']);
132
			$settingsForm->setConfigVars($config_vars);
133
			call_integration_hook('integrate_save_mail_settings');
134
135
			// You can not send more per page load than you can per minute
136
			if (!empty($this->_req->post->mail_batch_size))
137
			{
138
				$this->_req->post->mail_batch_size = min((int) $this->_req->post->mail_batch_size, (int) $this->_req->post->mail_period_limit);
139
			}
140
141
			// If not supplied, attempt to set a FQDN value for the SMTP client
142
			if (empty($this->_req->post->smtp_client) && $this->_req->post->mail_type === '1')
143
			{
144
				$this->_req->post->smtp_client = detectServer()->getFQDN();
145
			}
146
147
			$settingsForm->setConfigValues((array) $this->_req->post);
148
			$settingsForm->save();
149
			redirectexit('action=admin;area=mailqueue;sa=settings');
150
		}
151
152
		$context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'mailqueue', 'sa' => 'settings', 'save']);
153
		$context['settings_title'] = $txt['mailqueue_settings'];
154
155
		// Prepare the config form
156
		$settingsForm->prepare();
157
158
		// Build a little JS so the birthday mail can be seen
159
		$javascript = '
160
			var bDay = {';
161
162
		$i = 0;
163
		foreach ($processedBirthdayEmails as $index => $email)
164
		{
165
			$is_last = ++$i === count($processedBirthdayEmails);
166
			$javascript .= '
167
				' . $index . ': {
168
				subject: ' . JavaScriptEscape($email['subject']) . ',
169
				body: ' . JavaScriptEscape(nl2br($email['body'])) . '
170
			}' . ($is_last ? '' : ',');
171
		}
172
173
		theme()->addInlineJavascript($javascript . '
174 2
		};
175
		
176 2
		function fetch_birthday_preview()
177
		{
178
			var index = document.getElementById(\'birthday_email\').value;
179 2
180
			document.getElementById(\'birthday_subject\').innerHTML = bDay[index].subject;
181 2
			document.getElementById(\'birthday_body\').innerHTML = bDay[index].body;
182 2
		}', true);
183
	}
184 2
185 2
	/**
186 2
	 * Retrieve and return mail administration settings.
187
	 *
188 2
	 * @event integrate_modify_mail_settings Add new settings
189 2
	 */
190 2
	private function _settings()
191
	{
192
		global $txt, $modSettings, $txtBirthdayEmails;
193 2
194
		// We need $txtBirthdayEmails
195 2
		if (empty($txtBirthdayEmails))
196
		{
197
			$txtBirthdayEmails = [];
198
		}
199
200 2
		$lang_loader = new Loader(null, $txtBirthdayEmails, database(), 'txtBirthdayEmails');
201
		$lang_loader->load('EmailTemplates');
202
203 2
		$body = $txtBirthdayEmails[(empty($modSettings['birthday_email']) ? 'happy_birthday' : $modSettings['birthday_email']) . '_body'];
204
		$subject = $txtBirthdayEmails[(empty($modSettings['birthday_email']) ? 'happy_birthday' : $modSettings['birthday_email']) . '_subject'];
205 2
206
		$emails = array();
207
		$processedBirthdayEmails = array();
208
		foreach ($txtBirthdayEmails as $key => $value)
209
		{
210
			$index = substr($key, 0, strrpos($key, '_'));
211 2
			$element = substr($key, strrpos($key, '_') + 1);
212 2
			$processedBirthdayEmails[$index][$element] = $value;
213 2
		}
214 2
215
		foreach (array_keys($processedBirthdayEmails) as $index)
216
		{
217
			$emails[$index] = $index;
218 2
		}
219
220 2
		$config_vars = array(
221
			// Mail queue stuff, this rocks ;)
222
			array('check', 'mail_queue'),
223
			array('int', 'mail_period_limit'),
224
			array('int', 'mail_batch_size'),
225
			'',
226 2
			// SMTP stuff.
227
			array('select', 'mail_type', array($txt['mail_type_default'], 'SMTP')),
228 2
			array('text', 'smtp_host'),
229
			array('text', 'smtp_client'),
230
			array('text', 'smtp_port'),
231
			array('check', 'smtp_starttls'),
232
			array('text', 'smtp_username'),
233
			array('password', 'smtp_password'),
234
			'',
235
			array('select', 'birthday_email', $emails, 'value' => array('subject' => $subject, 'body' => $body), 'javascript' => 'onchange="fetch_birthday_preview()"'),
236
			'birthday_subject' => array('var_message', 'birthday_subject', 'message' => $processedBirthdayEmails[empty($modSettings['birthday_email']) ? 'happy_birthday' : $modSettings['birthday_email']]['subject'], 'disabled' => true, 'size' => strlen($subject) + 3),
237
			'birthday_body' => array('var_message', 'birthday_body', 'message' => nl2br($body), 'disabled' => true, 'size' => ceil(strlen($body) / 25)),
238
		);
239
240
		// Add new settings with a nice hook, makes them available for admin settings search as well
241
		call_integration_hook('integrate_modify_mail_settings', array(&$config_vars));
242
243
		return $config_vars;
244
	}
245
246
	/**
247
	 * Return the form settings for use in admin search
248
	 */
249
	public function settings_search()
250
	{
251
		return $this->_settings();
252
	}
253
254
	/**
255
	 * This function clears the mail queue of all emails, and at the end redirects to browse.
256
	 *
257
	 * - Note force clearing the queue may cause a site to exceed hosting mail limit quotas
258
	 * - Some hosts simple loose these excess emails, others queue them server side, up to a limit
259
	 */
260
	public function action_clear()
261
	{
262
		global $modSettings;
263
264
		checkSession('get');
265
266
		// This is certainly needed!
267
		require_once(SUBSDIR . '/Mail.subs.php');
268
269
		// Set a number to send each loop
270
		$number_to_send = empty($modSettings['mail_period_limit']) ? 25 : $modSettings['mail_period_limit'];
271
272
		// If we don't yet have the total to clear, find it.
273
		$all_emails = $this->_req->getQuery('te', 'intval', list_getMailQueueSize());
274
275
		// If we don't know how many we sent, it must be because... we didn't send any!
276
		$sent_emails = $this->_req->getQuery('sent', 'intval', 0);
277
278
		// Send this batch, then go for a short break...
279
		while (reduceMailQueue($number_to_send, true, true) === true)
280
		{
281
			// Sent another batch
282
			$sent_emails += $number_to_send;
283
			$this->_pauseMailQueueClear($all_emails, $sent_emails);
284
		}
285
286
		return $this->action_browse();
287
	}
288
289
	/**
290
	 * Used for pausing the mail queue.
291
	 *
292
	 * @param int $all_emails total emails to be sent
293
	 * @param int $sent_emails number of emails sent so far
294
	 */
295
	private function _pauseMailQueueClear($all_emails, $sent_emails)
296
	{
297
		global $context, $txt, $time_start;
298
299
		// Try get more time...
300
		detectServer()->setTimeLimit(600);
301
302
		// Have we already used our maximum time?
303
		if (time() - array_sum(explode(' ', $time_start)) < 5)
304
		{
305
			return;
306
		}
307
308
		$context['continue_get_data'] = '?action=admin;area=mailqueue;sa=clear;te=' . $all_emails . ';sent=' . $sent_emails . ';' . $context['session_var'] . '=' . $context['session_id'];
309
		$context['page_title'] = $txt['not_done_title'];
310
		$context['continue_post_data'] = '';
311
		$context['continue_countdown'] = '10';
312
		$context['sub_template'] = 'not_done';
313
314
		// Keep browse selected.
315
		$context['selected'] = 'browse';
316
317
		// What percent through are we?
318
		$context['continue_percent'] = round(($sent_emails / $all_emails) * 100, 1);
319
320
		// Never more than 100%!
321
		$context['continue_percent'] = min($context['continue_percent'], 100);
322
323
		obExit();
324
	}
325
326
	/**
327
	 * Display the mail queue...
328
	 *
329
	 * @uses ManageMail template
330
	 */
331
	public function action_browse()
332
	{
333
		global $context, $txt;
334
335
		require_once(SUBSDIR . '/Mail.subs.php');
336
		theme()->getTemplates()->load('ManageMail');
337
338
		// First, are we deleting something from the queue?
339
		if (isset($this->_req->post->delete))
340
		{
341
			checkSession('post');
342
			deleteMailQueueItems($this->_req->post->delete);
343
		}
344
345
		// Fetch the number of items in the current queue
346
		$status = list_MailQueueStatus();
347
348
		$context['oldest_mail'] = empty($status['mailOldest']) ? $txt['mailqueue_oldest_not_available'] : time_since(time() - $status['mailOldest']);
349
		$context['mail_queue_size'] = comma_format($status['mailQueueSize']);
350
351
		// Build our display list
352
		$listOptions = array(
353
			'id' => 'mail_queue',
354
			'title' => $txt['mailqueue_browse'],
355
			'items_per_page' => 20,
356
			'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'mailqueue']),
357
			'default_sort_col' => 'age',
358
			'no_items_label' => $txt['mailqueue_no_items'],
359
			'get_items' => array(
360
				'function' => 'list_getMailQueue',
361
			),
362
			'get_count' => array(
363
				'function' => 'list_getMailQueueSize',
364
			),
365
			'columns' => array(
366
				'subject' => array(
367
					'header' => array(
368
						'value' => $txt['mailqueue_subject'],
369
					),
370
					'data' => array(
371
						'function' => static fn($rowData) => Util::shorten_text(Util::htmlspecialchars($rowData['subject'], 50)),
372
						'class' => 'smalltext',
373
					),
374
					'sort' => array(
375
						'default' => 'subject',
376
						'reverse' => 'subject DESC',
377
					),
378
				),
379
				'recipient' => array(
380
					'header' => array(
381
						'value' => $txt['mailqueue_recipient'],
382
					),
383
					'data' => array(
384
						'sprintf' => array(
385
							'format' => '<a href="mailto:%1$s">%1$s</a>',
386
							'params' => array(
387
								'recipient' => true,
388
							),
389
						),
390
					),
391
					'sort' => array(
392
						'default' => 'recipient',
393
						'reverse' => 'recipient DESC',
394
					),
395
				),
396
				'priority' => array(
397
					'header' => array(
398
						'value' => $txt['mailqueue_priority'],
399
						'class' => 'centertext',
400
					),
401
					'data' => array(
402
						'function' => static function ($rowData) {
403
							global $txt;
404
405
							// We probably have a text label with your priority.
406
							$txtKey = sprintf('mq_mpriority_%1$s', $rowData['priority']);
407
408
							// But if not, revert to priority 0.
409
							return $txt[$txtKey] ?? $txt['mq_mpriority_1'];
410
						},
411
						'class' => 'centertext smalltext',
412
					),
413
					'sort' => array(
414
						'default' => 'priority',
415
						'reverse' => 'priority DESC',
416
					),
417
				),
418
				'age' => array(
419
					'header' => array(
420
						'value' => $txt['mailqueue_age'],
421
					),
422
					'data' => array(
423
						'function' => static fn($rowData) => time_since(time() - $rowData['time_sent']),
424
						'class' => 'smalltext',
425
					),
426
					'sort' => array(
427
						'default' => 'time_sent',
428
						'reverse' => 'time_sent DESC',
429
					),
430
				),
431
				'check' => array(
432
					'header' => array(
433
						'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />',
434
					),
435
					'data' => array(
436
						'function' => static fn($rowData) => '<input type="checkbox" name="delete[]" value="' . $rowData['id_mail'] . '" class="input_check" />',
437
						'class' => 'centertext',
438
					),
439
				),
440
			),
441
			'form' => array(
442
				'href' => getUrl('admin', ['action' => 'admin', 'area' => 'mailqueue']),
443
				'include_start' => true,
444
				'include_sort' => true,
445
			),
446
			'additional_rows' => array(
447
				array(
448
					'position' => 'bottom_of_list',
449
					'class' => 'submitbutton',
450
					'value' => '
451
						<input type="submit" name="delete_redirects" value="' . $txt['quickmod_delete_selected'] . '" onclick="return confirm(\'' . $txt['quickmod_confirm'] . '\');" />
452
						<a class="linkbutton" href="' . getUrl('admin', ['action' => 'admin', 'area' => 'mailqueue', 'sa' => 'clear', '{session_data}']) . '" onclick="return confirm(\'' . $txt['mailqueue_clear_list_warning'] . '\');">' . $txt['mailqueue_clear_list'] . '</a> ',
453
				),
454
			),
455
		);
456
457
		createList($listOptions);
458
	}
459
460
	/**
461
	 * Test email action
462
	 */
463
	public function action_test_email()
464
	{
465
		global $context, $txt;
466
467
		require_once(SUBSDIR . '/Mail.subs.php');
468
469
		theme()->getTemplates()->load('ManageMail');
470
		$context['page_title'] = $txt['mail_test'];
471
		$context['sub_template'] = 'mail_test';
472
473
		if (isset($this->_req->post->send))
474
		{
475
			checkSession();
476
			validateToken('admin-mailtest');
477
478
			$sendTo = $this->_req->getPost('send_to', 'trim');
479
			$subject = $this->_req->getPost('subject', 'Util::htmlspecialchars', '');
480
			$message = $this->_req->getPost('message', 'Util::htmlspecialchars', '');
481
482
			$sendTo = $sendTo ?: User::$info->email;
483
			if (empty($subject) || empty($message))
484
			{
485
				$result = false;
486
			}
487
			else
488
			{
489
				// Let 'er rip!
490
				$result = sendmail($sendTo, $subject, $message, null, null, true, 0);
491
			}
492
493
			redirectexit('action=admin;area=mailqueue;sa=test;result=' . ($result ? 'pass' : 'fail'));
494
		}
495
496
		createToken('admin-mailtest');
497
498
		$result = $this->_req->getQuery('result', 'trim', '');
499
		$context['result'] = $result;
500
	}
501
}
502