Passed
Push — release-2.1 ( 0c2197...207d2d )
by Jeremy
05:47
created

ManageNews.php ➔ ComposeMailing()   F

Complexity

Conditions 35
Paths 4096

Size

Total Lines 188

Duplication

Lines 19
Ratio 10.11 %

Importance

Changes 0
Metric Value
cc 35
nc 4096
nop 0
dl 19
loc 188
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file manages... the news. :P
5
 *
6
 * Simple Machines Forum (SMF)
7
 *
8
 * @package SMF
9
 * @author Simple Machines http://www.simplemachines.org
10
 * @copyright 2018 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 4
14
 */
15
16
if (!defined('SMF'))
17
	die('No direct access...');
18
19
/**
20
 * The news dispatcher; doesn't do anything, just delegates.
21
 * This is the entrance point for all News and Newsletter screens.
22
 * Called by ?action=admin;area=news.
23
 * It does the permission checks, and calls the appropriate function
24
 * based on the requested sub-action.
25
 */
26
function ManageNews()
27
{
28
	global $context, $txt;
29
30
	// First, let's do a quick permissions check for the best error message possible.
31
	isAllowedTo(array('edit_news', 'send_mail', 'admin_forum'));
32
33
	loadTemplate('ManageNews');
34
35
	// Format: 'sub-action' => array('function', 'permission')
36
	$subActions = array(
37
		'editnews' => array('EditNews', 'edit_news'),
38
		'mailingmembers' => array('SelectMailingMembers', 'send_mail'),
39
		'mailingcompose' => array('ComposeMailing', 'send_mail'),
40
		'mailingsend' => array('SendMailing', 'send_mail'),
41
		'settings' => array('ModifyNewsSettings', 'admin_forum'),
42
	);
43
44
	call_integration_hook('integrate_manage_news', array(&$subActions));
45
46
	// Default to sub action 'main' or 'settings' depending on permissions.
47
	$_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : (allowedTo('edit_news') ? 'editnews' : (allowedTo('send_mail') ? 'mailingmembers' : 'settings'));
48
49
	// Have you got the proper permissions?
50
	isAllowedTo($subActions[$_REQUEST['sa']][1]);
51
52
	// Create the tabs for the template.
53
	$context[$context['admin_menu_name']]['tab_data'] = array(
54
		'title' => $txt['news_title'],
55
		'help' => 'edit_news',
56
		'description' => $txt['admin_news_desc'],
57
		'tabs' => array(
58
			'editnews' => array(
59
			),
60
			'mailingmembers' => array(
61
				'description' => $txt['news_mailing_desc'],
62
			),
63
			'settings' => array(
64
				'description' => $txt['news_settings_desc'],
65
			),
66
		),
67
	);
68
69
	// Force the right area...
70
	if (substr($_REQUEST['sa'], 0, 7) == 'mailing')
71
		$context[$context['admin_menu_name']]['current_subsection'] = 'mailingmembers';
72
73
	call_helper($subActions[$_REQUEST['sa']][0]);
74
}
75
76
/**
77
 * Let the administrator(s) edit the news items for the forum.
78
 * It writes an entry into the moderation log.
79
 * This function uses the edit_news administration area.
80
 * Called by ?action=admin;area=news.
81
 * Requires the edit_news permission.
82
 * Can be accessed with ?action=admin;sa=editnews.
83
 *
84
 * @uses ManageNews template, edit_news sub template.
85
 */
86
function EditNews()
87
{
88
	global $txt, $modSettings, $context, $sourcedir, $scripturl;
89
	global $smcFunc;
90
91
	require_once($sourcedir . '/Subs-Post.php');
92
93
	// The 'remove selected' button was pressed.
94
	if (!empty($_POST['delete_selection']) && !empty($_POST['remove']))
95
	{
96
		checkSession();
97
98
		// Store the news temporarily in this array.
99
		$temp_news = explode("\n", $modSettings['news']);
100
101
		// Remove the items that were selected.
102
		foreach ($temp_news as $i => $news)
103
			if (in_array($i, $_POST['remove']))
104
				unset($temp_news[$i]);
105
106
		// Update the database.
107
		updateSettings(array('news' => implode("\n", $temp_news)));
108
109
		$context['saved_successful'] = true;
110
111
		logAction('news');
112
	}
113
	// The 'Save' button was pressed.
114
	elseif (!empty($_POST['save_items']))
115
	{
116
		checkSession();
117
118
		foreach ($_POST['news'] as $i => $news)
119
		{
120
			if (trim($news) == '')
121
				unset($_POST['news'][$i]);
122
			else
123
			{
124
				$_POST['news'][$i] = $smcFunc['htmlspecialchars']($_POST['news'][$i], ENT_QUOTES);
125
				preparsecode($_POST['news'][$i]);
126
			}
127
		}
128
129
		// Send the new news to the database.
130
		updateSettings(array('news' => implode("\n", $_POST['news'])));
131
132
		$context['saved_successful'] = true;
133
134
		// Log this into the moderation log.
135
		logAction('news');
136
	}
137
138
	// We're going to want this for making our list.
139
	require_once($sourcedir . '/Subs-List.php');
140
141
	$context['page_title'] = $txt['admin_edit_news'];
142
143
	// Use the standard templates for showing this.
144
	$listOptions = array(
145
		'id' => 'news_lists',
146
		'get_items' => array(
147
			'function' => 'list_getNews',
148
		),
149
		'columns' => array(
150
			'news' => array(
151
				'header' => array(
152
					'value' => $txt['admin_edit_news'],
153
					'class' => 'half_table',
154
				),
155
				'data' => array(
156
					'function' => function($news)
157
					{
158
						if (is_numeric($news['id']))
159
							return '
160
								<textarea id="data_' . $news['id'] . '" rows="3" cols="50" name="news[]" class="padding block">' . $news['unparsed'] . '</textarea>
161
								<div class="floatleft" id="preview_' . $news['id'] . '"></div>';
162
						else
163
							return $news['unparsed'];
164
					},
165
					'class' => 'half_table',
166
				),
167
			),
168
			'preview' => array(
169
				'header' => array(
170
					'value' => $txt['preview'],
171
					'class' => 'half_table',
172
				),
173
				'data' => array(
174
					'function' => function($news)
175
					{
176
						return '<div id="box_preview_' . $news['id'] . '" style="overflow: auto; width: 100%; height: 10ex;">' . $news['parsed'] . '</div>';
177
					},
178
					'class' => 'half_table',
179
				),
180
			),
181
			'check' => array(
182
				'header' => array(
183
					'value' => '<input type="checkbox" onclick="invertAll(this, this.form);">',
184
					'class' => 'centercol icon',
185
				),
186
				'data' => array(
187
					'function' => function($news)
188
					{
189
						if (is_numeric($news['id']))
190
							return '<input type="checkbox" name="remove[]" value="' . $news['id'] . '">';
191
						else
192
							return '';
193
					},
194
					'class' => 'centercol icon',
195
				),
196
			),
197
		),
198
		'form' => array(
199
			'href' => $scripturl . '?action=admin;area=news;sa=editnews',
200
			'hidden_fields' => array(
201
				$context['session_var'] => $context['session_id'],
202
			),
203
		),
204
		'additional_rows' => array(
205
			array(
206
				'position' => 'bottom_of_list',
207
				'value' => '
208
				<span id="moreNewsItems_link" class="floatleft" style="display: none;">
209
					<a class="button" href="javascript:void(0);" onclick="addNewsItem(); return false;">' . $txt['editnews_clickadd'] . '</a>
210
				</span>
211
				<input type="submit" name="save_items" value="' . $txt['save'] . '" class="button">
212
				<input type="submit" name="delete_selection" value="' . $txt['editnews_remove_selected'] . '" data-confirm="' . $txt['editnews_remove_confirm'] . '" class="button you_sure">',
213
			),
214
		),
215
		'javascript' => '
216
					document.getElementById(\'list_news_lists_last\').style.display = "none";
217
					document.getElementById("moreNewsItems_link").style.display = "";
218
					var last_preview = 0;
219
220
					$(document).ready(function () {
221
						$("div[id ^= \'preview_\']").each(function () {
222
							var preview_id = $(this).attr(\'id\').split(\'_\')[1];
223
							if (last_preview < preview_id)
224
								last_preview = preview_id;
225
							make_preview_btn(preview_id);
226
						});
227
					});
228
229
					function make_preview_btn (preview_id)
230
					{
231
						$("#preview_" + preview_id).addClass("button");
232
						$("#preview_" + preview_id).text(\'' . $txt['preview'] . '\').click(function () {
233
							$.ajax({
234
								type: "POST",
235
								url: "' . $scripturl . '?action=xmlhttp;sa=previews;xml",
236
								data: {item: "newspreview", news: $("#data_" + preview_id).val()},
237
								context: document.body,
238
								success: function(request){
239
									if ($(request).find("error").text() == \'\')
240
										$(document).find("#box_preview_" + preview_id).html($(request).text());
241
									else
242
										$(document).find("#box_preview_" + preview_id).text(\'' . $txt['news_error_no_news'] . '\');
243
								},
244
							});
245
						});
246
					}
247
248
					function addNewsItem ()
249
					{
250
						last_preview++;
251
						$("#list_news_lists_last").before(' . javaScriptEscape('
252
						<tr class="windowbg') . ' + (last_preview % 2 == 0 ? \'\' : \'2\') + ' . javaScriptEscape('">
253
							<td style="width: 50%;">
254
									<textarea id="data_') . ' + last_preview + ' . javaScriptEscape('" rows="3" cols="65" name="news[]" style="width: 95%;"></textarea>
255
									<br>
256
									<div class="floatleft" id="preview_') . ' + last_preview + ' . javaScriptEscape('"></div>
257
							</td>
258
							<td style="width: 45%;">
259
								<div id="box_preview_') . ' + last_preview + ' . javaScriptEscape('" style="overflow: auto; width: 100%; height: 10ex;"></div>
260
							</td>
261
							<td></td>
262
						</tr>') . ');
263
						make_preview_btn(last_preview);
264
					}',
265
	);
266
267
	// Create the request list.
268
	createList($listOptions);
269
270
	// And go!
271
	loadTemplate('ManageNews');
272
	$context['sub_template'] = 'news_lists';
273
}
274
275
/**
276
 * Prepares an array of the forum news items for display in the template
277
 *
278
 * @return array An array of information about the news items
279
 */
280
function list_getNews()
281
{
282
	global $modSettings;
283
284
	$admin_current_news = array();
285
	// Ready the current news.
286
	foreach (explode("\n", $modSettings['news']) as $id => $line)
287
		$admin_current_news[$id] = array(
288
			'id' => $id,
289
			'unparsed' => un_preparsecode($line),
290
			'parsed' => preg_replace('~<([/]?)form[^>]*?[>]*>~i', '<em class="smalltext">&lt;$1form&gt;</em>', parse_bbc($line)),
291
		);
292
293
	$admin_current_news['last'] = array(
294
		'id' => 'last',
295
		'unparsed' => '<div id="moreNewsItems"></div>
296
		<noscript><textarea rows="3" cols="65" name="news[]" style="width: 85%;"></textarea></noscript>',
297
		'parsed' => '<div id="moreNewsItems_preview"></div>',
298
	);
299
300
	return $admin_current_news;
301
}
302
303
/**
304
 * This function allows a user to select the membergroups to send their
305
 * mailing to.
306
 * Called by ?action=admin;area=news;sa=mailingmembers.
307
 * Requires the send_mail permission.
308
 * Form is submitted to ?action=admin;area=news;mailingcompose.
309
 *
310
 * @uses the ManageNews template and email_members sub template.
311
 */
312
function SelectMailingMembers()
313
{
314
	global $txt, $context, $modSettings, $smcFunc;
315
316
	// Is there any confirm message?
317
	$context['newsletter_sent'] = isset($_SESSION['newsletter_sent']) ? $_SESSION['newsletter_sent'] : '';
318
319
	$context['page_title'] = $txt['admin_newsletters'];
320
321
	$context['sub_template'] = 'email_members';
322
323
	$context['groups'] = array();
324
	$postGroups = array();
325
	$normalGroups = array();
326
327
	// If we have post groups disabled then we need to give a "ungrouped members" option.
328
	if (empty($modSettings['permission_enable_postgroups']))
329
	{
330
		$context['groups'][0] = array(
331
			'id' => 0,
332
			'name' => $txt['membergroups_members'],
333
			'member_count' => 0,
334
		);
335
		$normalGroups[0] = 0;
336
	}
337
338
	// Get all the extra groups as well as Administrator and Global Moderator.
339
	$request = $smcFunc['db_query']('', '
340
		SELECT mg.id_group, mg.group_name, mg.min_posts
341
		FROM {db_prefix}membergroups AS mg' . (empty($modSettings['permission_enable_postgroups']) ? '
342
		WHERE mg.min_posts = {int:min_posts}' : '') . '
343
		GROUP BY mg.id_group, mg.min_posts, mg.group_name
344
		ORDER BY mg.min_posts, CASE WHEN mg.id_group < {int:newbie_group} THEN mg.id_group ELSE 4 END, mg.group_name',
345
		array(
346
			'min_posts' => -1,
347
			'newbie_group' => 4,
348
		)
349
	);
350
	while ($row = $smcFunc['db_fetch_assoc']($request))
351
	{
352
		$context['groups'][$row['id_group']] = array(
353
			'id' => $row['id_group'],
354
			'name' => $row['group_name'],
355
			'member_count' => 0,
356
		);
357
358
		if ($row['min_posts'] == -1)
359
			$normalGroups[$row['id_group']] = $row['id_group'];
360
		else
361
			$postGroups[$row['id_group']] = $row['id_group'];
362
	}
363
	$smcFunc['db_free_result']($request);
364
365
	// If we have post groups, let's count the number of members...
366
	if (!empty($postGroups))
367
	{
368
		$query = $smcFunc['db_query']('', '
369
			SELECT mem.id_post_group AS id_group, COUNT(*) AS member_count
370
			FROM {db_prefix}members AS mem
371
			WHERE mem.id_post_group IN ({array_int:post_group_list})
372
			GROUP BY mem.id_post_group',
373
			array(
374
				'post_group_list' => $postGroups,
375
			)
376
		);
377
		while ($row = $smcFunc['db_fetch_assoc']($query))
378
			$context['groups'][$row['id_group']]['member_count'] += $row['member_count'];
379
		$smcFunc['db_free_result']($query);
380
	}
381
382
	if (!empty($normalGroups))
383
	{
384
		// Find people who are members of this group...
385
		$query = $smcFunc['db_query']('', '
386
			SELECT id_group, COUNT(*) AS member_count
387
			FROM {db_prefix}members
388
			WHERE id_group IN ({array_int:normal_group_list})
389
			GROUP BY id_group',
390
			array(
391
				'normal_group_list' => $normalGroups,
392
			)
393
		);
394
		while ($row = $smcFunc['db_fetch_assoc']($query))
395
			$context['groups'][$row['id_group']]['member_count'] += $row['member_count'];
396
		$smcFunc['db_free_result']($query);
397
398
		// Also do those who have it as an additional membergroup - this ones more yucky...
399
		$query = $smcFunc['db_query']('', '
400
			SELECT mg.id_group, COUNT(*) AS member_count
401
			FROM {db_prefix}membergroups AS mg
402
				INNER JOIN {db_prefix}members AS mem ON (mem.additional_groups != {string:blank_string}
403
					AND mem.id_group != mg.id_group
404
					AND FIND_IN_SET(mg.id_group, mem.additional_groups) != 0)
405
			WHERE mg.id_group IN ({array_int:normal_group_list})
406
			GROUP BY mg.id_group',
407
			array(
408
				'normal_group_list' => $normalGroups,
409
				'blank_string' => '',
410
			)
411
		);
412
		while ($row = $smcFunc['db_fetch_assoc']($query))
413
			$context['groups'][$row['id_group']]['member_count'] += $row['member_count'];
414
		$smcFunc['db_free_result']($query);
415
	}
416
417
	// Any moderators?
418
	$request = $smcFunc['db_query']('', '
419
		SELECT COUNT(DISTINCT id_member) AS num_distinct_mods
420
		FROM {db_prefix}moderators
421
		LIMIT 1',
422
		array(
423
		)
424
	);
425
	list ($context['groups'][3]['member_count']) = $smcFunc['db_fetch_row']($request);
426
	$smcFunc['db_free_result']($request);
427
428
	$context['can_send_pm'] = allowedTo('pm_send');
429
430
	loadJavaScriptFile('suggest.js', array('defer' => false, 'minimize' => true), 'smf_suggest');
431
}
432
433
/**
434
 * Prepare subject and message of an email for the preview box
435
 * Used in ComposeMailing and RetrievePreview (Xml.php)
436
 */
437
function prepareMailingForPreview()
438
{
439
	global $context, $modSettings, $scripturl, $user_info, $txt;
440
	loadLanguage('Errors');
441
442
	$processing = array('preview_subject' => 'subject', 'preview_message' => 'message');
443
444
	// Use the default time format.
445
	$user_info['time_format'] = $modSettings['time_format'];
446
447
	$variables = array(
448
		'{$board_url}',
449
		'{$current_time}',
450
		'{$latest_member.link}',
451
		'{$latest_member.id}',
452
		'{$latest_member.name}'
453
	);
454
455
	$html = $context['send_html'];
456
457
	// We might need this in a bit
458
	$cleanLatestMember = empty($context['send_html']) || $context['send_pm'] ? un_htmlspecialchars($modSettings['latestRealName']) : $modSettings['latestRealName'];
459
460
	foreach ($processing as $key => $post)
461
	{
462
		$context[$key] = !empty($_REQUEST[$post]) ? $_REQUEST[$post] : '';
463
464
		if (empty($context[$key]) && empty($_REQUEST['xml']))
465
			$context['post_error']['messages'][] = $txt['error_no_' . $post];
466
		elseif (!empty($_REQUEST['xml']))
467
			continue;
468
469
		preparsecode($context[$key]);
470
		if ($html)
471
		{
472
			$enablePostHTML = $modSettings['enablePostHTML'];
473
			$modSettings['enablePostHTML'] = $context['send_html'];
474
			$context[$key] = parse_bbc($context[$key]);
475
			$modSettings['enablePostHTML'] = $enablePostHTML;
476
		}
477
478
		// Replace in all the standard things.
479
		$context[$key] = str_replace($variables,
480
			array(
481
				!empty($context['send_html']) ? '<a href="' . $scripturl . '">' . $scripturl . '</a>' : $scripturl,
482
				timeformat(forum_time(), false),
483
				!empty($context['send_html']) ? '<a href="' . $scripturl . '?action=profile;u=' . $modSettings['latestMember'] . '">' . $cleanLatestMember . '</a>' : ($context['send_pm'] ? '[url=' . $scripturl . '?action=profile;u=' . $modSettings['latestMember'] . ']' . $cleanLatestMember . '[/url]' : $cleanLatestMember),
484
				$modSettings['latestMember'],
485
				$cleanLatestMember
486
			), $context[$key]);
487
	}
488
}
489
490
/**
491
 * Shows a form to edit a forum mailing and its recipients.
492
 * Called by ?action=admin;area=news;sa=mailingcompose.
493
 * Requires the send_mail permission.
494
 * Form is submitted to ?action=admin;area=news;sa=mailingsend.
495
 *
496
 * @uses ManageNews template, email_members_compose sub-template.
497
 */
498
function ComposeMailing()
499
{
500
	global $txt, $sourcedir, $context, $smcFunc;
501
502
	// Setup the template!
503
	$context['page_title'] = $txt['admin_newsletters'];
504
	$context['sub_template'] = 'email_members_compose';
505
506
	$context['subject'] = !empty($_POST['subject']) ? $_POST['subject'] : $smcFunc['htmlspecialchars']($context['forum_name'] . ': ' . $txt['subject']);
507
	$context['message'] = !empty($_POST['message']) ? $_POST['message'] : $smcFunc['htmlspecialchars']($txt['message'] . "\n\n" . $txt['regards_team'] . "\n\n" . '{$board_url}');
508
509
	// Needed for the WYSIWYG editor.
510
	require_once($sourcedir . '/Subs-Editor.php');
511
512
	// Now create the editor.
513
	$editorOptions = array(
514
		'id' => 'message',
515
		'value' => $context['message'],
516
		'height' => '250px',
517
		'width' => '100%',
518
		'labels' => array(
519
			'post_button' => $txt['sendtopic_send'],
520
		),
521
		'preview_type' => 2,
522
		'required' => true,
523
	);
524
	create_control_richedit($editorOptions);
525
	// Store the ID for old compatibility.
526
	$context['post_box_name'] = $editorOptions['id'];
527
528
	if (isset($context['preview']))
529
	{
530
		require_once($sourcedir . '/Subs-Post.php');
531
		$context['recipients']['members'] = !empty($_POST['members']) ? explode(',', $_POST['members']) : array();
532
		$context['recipients']['exclude_members'] = !empty($_POST['exclude_members']) ? explode(',', $_POST['exclude_members']) : array();
533
		$context['recipients']['groups'] = !empty($_POST['groups']) ? explode(',', $_POST['groups']) : array();
534
		$context['recipients']['exclude_groups'] = !empty($_POST['exclude_groups']) ? explode(',', $_POST['exclude_groups']) : array();
535
		$context['recipients']['emails'] = !empty($_POST['emails']) ? explode(';', $_POST['emails']) : array();
536
		$context['email_force'] = !empty($_POST['email_force']) ? 1 : 0;
537
		$context['total_emails'] = !empty($_POST['total_emails']) ? (int) $_POST['total_emails'] : 0;
538
		$context['send_pm'] = !empty($_POST['send_pm']) ? 1 : 0;
539
		$context['send_html'] = !empty($_POST['send_html']) ? '1' : '0';
540
541
		return prepareMailingForPreview();
0 ignored issues
show
Bug introduced by
Are you sure the usage of prepareMailingForPreview() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
542
	}
543
544
	// Start by finding any members!
545
	$toClean = array();
546
	if (!empty($_POST['members']))
547
		$toClean[] = 'members';
548
	if (!empty($_POST['exclude_members']))
549
		$toClean[] = 'exclude_members';
550
	if (!empty($toClean))
551
	{
552
		require_once($sourcedir . '/Subs-Auth.php');
553
		foreach ($toClean as $type)
554
		{
555
			// Remove the quotes.
556
			$_POST[$type] = strtr($_POST[$type], array('\\"' => '"'));
557
558
			preg_match_all('~"([^"]+)"~', $_POST[$type], $matches);
559
			$_POST[$type] = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $_POST[$type]))));
560
561
			foreach ($_POST[$type] as $index => $member)
562
				if (strlen(trim($member)) > 0)
563
					$_POST[$type][$index] = $smcFunc['htmlspecialchars']($smcFunc['strtolower'](trim($member)));
564
				else
565
					unset($_POST[$type][$index]);
566
567
			// Find the members
568
			$_POST[$type] = implode(',', array_keys(findMembers($_POST[$type])));
569
		}
570
	}
571
572
	if (isset($_POST['member_list']) && is_array($_POST['member_list']))
573
	{
574
		$members = array();
575
		foreach ($_POST['member_list'] as $member_id)
576
			$members[] = (int) $member_id;
577
		$_POST['members'] = implode(',', $members);
578
	}
579
580
	if (isset($_POST['exclude_member_list']) && is_array($_POST['exclude_member_list']))
581
	{
582
		$members = array();
583
		foreach ($_POST['exclude_member_list'] as $member_id)
584
			$members[] = (int) $member_id;
585
		$_POST['exclude_members'] = implode(',', $members);
586
	}
587
588
	// Clean the other vars.
589
	SendMailing(true);
590
591
	// We need a couple strings from the email template file
592
	loadLanguage('EmailTemplates');
593
594
	// Get a list of all full banned users.  Use their Username and email to find them.  Only get the ones that can't login to turn off notification.
595
	$request = $smcFunc['db_query']('', '
596
		SELECT DISTINCT mem.id_member
597
		FROM {db_prefix}ban_groups AS bg
598
			INNER JOIN {db_prefix}ban_items AS bi ON (bg.id_ban_group = bi.id_ban_group)
599
			INNER JOIN {db_prefix}members AS mem ON (bi.id_member = mem.id_member)
600
		WHERE (bg.cannot_access = {int:cannot_access} OR bg.cannot_login = {int:cannot_login})
601
			AND (bg.expire_time IS NULL OR bg.expire_time > {int:current_time})',
602
		array(
603
			'cannot_access' => 1,
604
			'cannot_login' => 1,
605
			'current_time' => time(),
606
		)
607
	);
608
	while ($row = $smcFunc['db_fetch_assoc']($request))
609
		$context['recipients']['exclude_members'][] = $row['id_member'];
610
	$smcFunc['db_free_result']($request);
611
612
	$request = $smcFunc['db_query']('', '
613
		SELECT DISTINCT bi.email_address
614
		FROM {db_prefix}ban_items AS bi
615
			INNER JOIN {db_prefix}ban_groups AS bg ON (bg.id_ban_group = bi.id_ban_group)
616
		WHERE (bg.cannot_access = {int:cannot_access} OR bg.cannot_login = {int:cannot_login})
617
			AND (bg.expire_time IS NULL OR bg.expire_time > {int:current_time})
618
			AND bi.email_address != {string:blank_string}',
619
		array(
620
			'cannot_access' => 1,
621
			'cannot_login' => 1,
622
			'current_time' => time(),
623
			'blank_string' => '',
624
		)
625
	);
626
	$condition_array = array();
627
	$condition_array_params = array();
628
	$count = 0;
629
	while ($row = $smcFunc['db_fetch_assoc']($request))
630
	{
631
		$condition_array[] = '{string:email_' . $count . '}';
632
		$condition_array_params['email_' . $count++] = $row['email_address'];
633
	}
634
	$smcFunc['db_free_result']($request);
635
636
	if (!empty($condition_array))
637
	{
638
		$request = $smcFunc['db_query']('', '
639
			SELECT id_member
640
			FROM {db_prefix}members
641
			WHERE email_address IN(' . implode(', ', $condition_array) . ')',
642
			$condition_array_params
643
		);
644
		while ($row = $smcFunc['db_fetch_assoc']($request))
645
			$context['recipients']['exclude_members'][] = $row['id_member'];
646
		$smcFunc['db_free_result']($request);
647
	}
648
649
	// Did they select moderators - if so add them as specific members...
650
	if ((!empty($context['recipients']['groups']) && in_array(3, $context['recipients']['groups'])) || (!empty($context['recipients']['exclude_groups']) && in_array(3, $context['recipients']['exclude_groups'])))
651
	{
652
		$request = $smcFunc['db_query']('', '
653
			SELECT DISTINCT mem.id_member AS identifier
654
			FROM {db_prefix}members AS mem
655
				INNER JOIN {db_prefix}moderators AS mods ON (mods.id_member = mem.id_member)
656
			WHERE mem.is_activated = {int:is_activated}',
657
			array(
658
				'is_activated' => 1,
659
			)
660
		);
661
		while ($row = $smcFunc['db_fetch_assoc']($request))
662
		{
663
			if (in_array(3, $context['recipients']))
664
				$context['recipients']['exclude_members'][] = $row['identifier'];
665
			else
666
				$context['recipients']['members'][] = $row['identifier'];
667
		}
668
		$smcFunc['db_free_result']($request);
669
	}
670
671
	// For progress bar!
672
	$context['total_emails'] = count($context['recipients']['emails']);
673
	$request = $smcFunc['db_query']('', '
674
		SELECT COUNT(*)
675
		FROM {db_prefix}members',
676
		array(
677
		)
678
	);
679
	list ($context['total_members']) = $smcFunc['db_fetch_row']($request);
680
	$smcFunc['db_free_result']($request);
681
682
	// Clean up the arrays.
683
	$context['recipients']['members'] = array_unique($context['recipients']['members']);
684
	$context['recipients']['exclude_members'] = array_unique($context['recipients']['exclude_members']);
685
}
686
687
/**
688
 * Handles the sending of the forum mailing in batches.
689
 * Called by ?action=admin;area=news;sa=mailingsend
690
 * Requires the send_mail permission.
691
 * Redirects to itself when more batches need to be sent.
692
 * Redirects to ?action=admin;area=news;sa=mailingmembers after everything has been sent.
693
 *
694
 * @param bool $clean_only If set, it will only clean the variables, put them in context, then return.
695
 * @uses the ManageNews template and email_members_send sub template.
696
 */
697
function SendMailing($clean_only = false)
698
{
699
	global $txt, $sourcedir, $context, $smcFunc;
700
	global $scripturl, $modSettings, $user_info;
701
702
	if (isset($_POST['preview']))
703
	{
704
		$context['preview'] = true;
705
		return ComposeMailing();
0 ignored issues
show
Bug introduced by
Are you sure the usage of ComposeMailing() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
706
	}
707
708
	// How many to send at once? Quantity depends on whether we are queueing or not.
709
	// @todo Might need an interface? (used in Post.php too with different limits)
710
	$num_at_once = 1000;
711
712
	// If by PM's I suggest we half the above number.
713
	if (!empty($_POST['send_pm']))
714
		$num_at_once /= 2;
715
716
	checkSession();
717
718
	// Where are we actually to?
719
	$context['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0;
720
	$context['email_force'] = !empty($_POST['email_force']) ? 1 : 0;
721
	$context['send_pm'] = !empty($_POST['send_pm']) ? 1 : 0;
722
	$context['total_emails'] = !empty($_POST['total_emails']) ? (int) $_POST['total_emails'] : 0;
723
	$context['send_html'] = !empty($_POST['send_html']) ? '1' : '0';
724
	$context['parse_html'] = !empty($_POST['parse_html']) ? '1' : '0';
725
726
	//One can't simply nullify things around
727
	if (empty($_REQUEST['total_members']))
728
	{
729
		$request = $smcFunc['db_query']('', '
730
			SELECT COUNT(*)
731
			FROM {db_prefix}members',
732
			array(
733
			)
734
		);
735
		list ($context['total_members']) = $smcFunc['db_fetch_row']($request);
736
		$smcFunc['db_free_result']($request);
737
	}
738
	else
739
	{
740
		$context['total_members'] = (int) $_REQUEST['total_members'];
741
	}
742
743
	// Create our main context.
744
	$context['recipients'] = array(
745
		'groups' => array(),
746
		'exclude_groups' => array(),
747
		'members' => array(),
748
		'exclude_members' => array(),
749
		'emails' => array(),
750
	);
751
752
	// Have we any excluded members?
753
	if (!empty($_POST['exclude_members']))
754
	{
755
		$members = explode(',', $_POST['exclude_members']);
756
		foreach ($members as $member)
757
			if ($member >= $context['start'])
758
				$context['recipients']['exclude_members'][] = (int) $member;
759
	}
760
761
	// What about members we *must* do?
762
	if (!empty($_POST['members']))
763
	{
764
		$members = explode(',', $_POST['members']);
765
		foreach ($members as $member)
766
			if ($member >= $context['start'])
767
				$context['recipients']['members'][] = (int) $member;
768
	}
769
	// Cleaning groups is simple - although deal with both checkbox and commas.
770
	if (isset($_POST['groups']))
771
	{
772
		if (is_array($_POST['groups']))
773
		{
774
			foreach ($_POST['groups'] as $group => $dummy)
775
				$context['recipients']['groups'][] = (int) $group;
776
		}
777
		else
778
		{
779
			$groups = explode(',', $_POST['groups']);
780
			foreach ($groups as $group)
781
				$context['recipients']['groups'][] = (int) $group;
782
		}
783
	}
784
	// Same for excluded groups
785
	if (isset($_POST['exclude_groups']))
786
	{
787
		if (is_array($_POST['exclude_groups']))
788
		{
789
			foreach ($_POST['exclude_groups'] as $group => $dummy)
790
				$context['recipients']['exclude_groups'][] = (int) $group;
791
		}
792
		// Ignore an empty string - we don't want to exclude "Regular Members" unless it's specifically selected
793
		elseif ($_POST['exclude_groups'] != '')
794
		{
795
			$groups = explode(',', $_POST['exclude_groups']);
796
			foreach ($groups as $group)
797
				$context['recipients']['exclude_groups'][] = (int) $group;
798
		}
799
	}
800
	// Finally - emails!
801
	if (!empty($_POST['emails']))
802
	{
803
		$addressed = array_unique(explode(';', strtr($_POST['emails'], array("\n" => ';', "\r" => ';', ',' => ';'))));
804
		foreach ($addressed as $curmem)
805
		{
806
			$curmem = trim($curmem);
807
			if ($curmem != '' && filter_var($curmem, FILTER_VALIDATE_EMAIL))
808
				$context['recipients']['emails'][$curmem] = $curmem;
809
		}
810
	}
811
812
	// If we're only cleaning drop out here.
813
	if ($clean_only)
814
		return;
815
816
	require_once($sourcedir . '/Subs-Post.php');
817
818
	// We are relying too much on writing to superglobals...
819
	$_POST['subject'] = !empty($_POST['subject']) ? $_POST['subject'] : '';
820
	$_POST['message'] = !empty($_POST['message']) ? $_POST['message'] : '';
821
822
	// Save the message and its subject in $context
823
	$context['subject'] = $smcFunc['htmlspecialchars']($_POST['subject'], ENT_QUOTES);
824
	$context['message'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES);
825
826
	// Prepare the message for sending it as HTML
827
	if (!$context['send_pm'] && !empty($_POST['send_html']))
828
	{
829
		// Prepare the message for HTML.
830
		if (!empty($_POST['parse_html']))
831
			$_POST['message'] = str_replace(array("\n", '  '), array('<br>' . "\n", '&nbsp; '), $_POST['message']);
832
833
		// This is here to prevent spam filters from tagging this as spam.
834
		if (preg_match('~\<html~i', $_POST['message']) == 0)
835
		{
836
			if (preg_match('~\<body~i', $_POST['message']) == 0)
837
				$_POST['message'] = '<html><head><title>' . $_POST['subject'] . '</title></head>' . "\n" . '<body>' . $_POST['message'] . '</body></html>';
838
			else
839
				$_POST['message'] = '<html>' . $_POST['message'] . '</html>';
840
		}
841
	}
842
843
	if (empty($_POST['message']) || empty($_POST['subject']))
844
	{
845
		$context['preview'] = true;
846
		return ComposeMailing();
847
	}
848
849
	// Use the default time format.
850
	$user_info['time_format'] = $modSettings['time_format'];
851
852
	$variables = array(
853
		'{$board_url}',
854
		'{$current_time}',
855
		'{$latest_member.link}',
856
		'{$latest_member.id}',
857
		'{$latest_member.name}'
858
	);
859
860
	// We might need this in a bit
861
	$cleanLatestMember = empty($_POST['send_html']) || $context['send_pm'] ? un_htmlspecialchars($modSettings['latestRealName']) : $modSettings['latestRealName'];
862
863
	// Replace in all the standard things.
864
	$_POST['message'] = str_replace($variables,
865
		array(
866
			!empty($_POST['send_html']) ? '<a href="' . $scripturl . '">' . $scripturl . '</a>' : $scripturl,
867
			timeformat(forum_time(), false),
868
			!empty($_POST['send_html']) ? '<a href="' . $scripturl . '?action=profile;u=' . $modSettings['latestMember'] . '">' . $cleanLatestMember . '</a>' : ($context['send_pm'] ? '[url=' . $scripturl . '?action=profile;u=' . $modSettings['latestMember'] . ']' . $cleanLatestMember . '[/url]' : $scripturl . '?action=profile;u=' . $modSettings['latestMember']),
869
			$modSettings['latestMember'],
870
			$cleanLatestMember
871
		), $_POST['message']);
872
	$_POST['subject'] = str_replace($variables,
873
		array(
874
			$scripturl,
875
			timeformat(forum_time(), false),
876
			$modSettings['latestRealName'],
877
			$modSettings['latestMember'],
878
			$modSettings['latestRealName']
879
		), $_POST['subject']);
880
881
	$from_member = array(
882
		'{$member.email}',
883
		'{$member.link}',
884
		'{$member.id}',
885
		'{$member.name}'
886
	);
887
888
	// If we still have emails, do them first!
889
	$i = 0;
890
	foreach ($context['recipients']['emails'] as $k => $email)
891
	{
892
		// Done as many as we can?
893
		if ($i >= $num_at_once)
894
			break;
895
896
		// Don't sent it twice!
897
		unset($context['recipients']['emails'][$k]);
898
899
		// Dammit - can't PM emails!
900
		if ($context['send_pm'])
901
			continue;
902
903
		$to_member = array(
904
			$email,
905
			!empty($_POST['send_html']) ? '<a href="mailto:' . $email . '">' . $email . '</a>' : $email,
906
			'??',
907
			$email
908
		);
909
910
		sendmail($email, str_replace($from_member, $to_member, $_POST['subject']), str_replace($from_member, $to_member, $_POST['message']), null, 'news', !empty($_POST['send_html']), 5);
911
912
		// Done another...
913
		$i++;
914
	}
915
916
	if ($i < $num_at_once)
917
	{
918
		// Need to build quite a query!
919
		$sendQuery = '(';
920
		$sendParams = array();
921
		if (!empty($context['recipients']['groups']))
922
		{
923
			// Take the long route...
924
			$queryBuild = array();
925
			foreach ($context['recipients']['groups'] as $group)
926
			{
927
				$sendParams['group_' . $group] = $group;
928
				$queryBuild[] = 'mem.id_group = {int:group_' . $group . '}';
929
				if (!empty($group))
930
				{
931
					$queryBuild[] = 'FIND_IN_SET({int:group_' . $group . '}, mem.additional_groups) != 0';
932
					$queryBuild[] = 'mem.id_post_group = {int:group_' . $group . '}';
933
				}
934
			}
935
			if (!empty($queryBuild))
936
			$sendQuery .= implode(' OR ', $queryBuild);
937
		}
938
		if (!empty($context['recipients']['members']))
939
		{
940
			$sendQuery .= ($sendQuery == '(' ? '' : ' OR ') . 'mem.id_member IN ({array_int:members})';
941
			$sendParams['members'] = $context['recipients']['members'];
942
		}
943
944
		$sendQuery .= ')';
945
946
		// If we've not got a query then we must be done!
947
		if ($sendQuery == '()')
948
		{
949
			// Set a confirmation message.
950
			$_SESSION['newsletter_sent'] = 'queue_done';
951
			redirectexit('action=admin;area=news;sa=mailingmembers');
952
		}
953
954
		// Anything to exclude?
955
		if (!empty($context['recipients']['exclude_groups']) && in_array(0, $context['recipients']['exclude_groups']))
956
			$sendQuery .= ' AND mem.id_group != {int:regular_group}';
957
		if (!empty($context['recipients']['exclude_members']))
958
		{
959
			$sendQuery .= ' AND mem.id_member NOT IN ({array_int:exclude_members})';
960
			$sendParams['exclude_members'] = $context['recipients']['exclude_members'];
961
		}
962
963
		// Get the smelly people - note we respect the id_member range as it gives us a quicker query.
964
		$result = $smcFunc['db_query']('', '
965
			SELECT mem.id_member, mem.email_address, mem.real_name, mem.id_group, mem.additional_groups, mem.id_post_group
966
			FROM {db_prefix}members AS mem
967
			WHERE ' . $sendQuery . '
968
				AND mem.is_activated = {int:is_activated}
969
			ORDER BY mem.id_member ASC
970
			LIMIT {int:start}, {int:atonce}',
971
			array_merge($sendParams, array(
972
				'start' => $context['start'],
973
				'atonce' => $num_at_once,
974
				'regular_group' => 0,
975
				'is_activated' => 1,
976
			))
977
		);
978
		$rows = array();
979
		while ($row = $smcFunc['db_fetch_assoc']($result))
980
		{
981
			$rows[$row['id_member']] = $row;
982
		}
983
		$smcFunc['db_free_result']($result);
984
985
		// Load their alert preferences
986
		require_once($sourcedir . '/Subs-Notify.php');
987
		$prefs = getNotifyPrefs(array_keys($rows), 'announcements', true);
988
989
		foreach ($rows as $row)
990
		{
991
			// Force them to have it?
992
			if (empty($context['email_force']) && empty($prefs[$row['id_member']]['announcements']))
993
				continue;
994
995
			// What groups are we looking at here?
996
			if (empty($row['additional_groups']))
997
				$groups = array($row['id_group'], $row['id_post_group']);
998
			else
999
				$groups = array_merge(
1000
					array($row['id_group'], $row['id_post_group']),
1001
					explode(',', $row['additional_groups'])
1002
				);
1003
1004
			// Excluded groups?
1005
			if (array_intersect($groups, $context['recipients']['exclude_groups']))
1006
				continue;
1007
1008
			// We might need this
1009
			$cleanMemberName = empty($_POST['send_html']) || $context['send_pm'] ? un_htmlspecialchars($row['real_name']) : $row['real_name'];
1010
1011
			// Replace the member-dependant variables
1012
			$message = str_replace($from_member,
1013
				array(
1014
					$row['email_address'],
1015
					!empty($_POST['send_html']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $cleanMemberName . '</a>' : ($context['send_pm'] ? '[url=' . $scripturl . '?action=profile;u=' . $row['id_member'] . ']' . $cleanMemberName . '[/url]' : $scripturl . '?action=profile;u=' . $row['id_member']),
1016
					$row['id_member'],
1017
					$cleanMemberName,
1018
				), $_POST['message']);
1019
1020
			$subject = str_replace($from_member,
1021
				array(
1022
					$row['email_address'],
1023
					$row['real_name'],
1024
					$row['id_member'],
1025
					$row['real_name'],
1026
				), $_POST['subject']);
1027
1028
			// Send the actual email - or a PM!
1029
			if (!$context['send_pm'])
1030
				sendmail($row['email_address'], $subject, $message, null, 'news', !empty($_POST['send_html']), 5);
1031
			else
1032
				sendpm(array('to' => array($row['id_member']), 'bcc' => array()), $subject, $message);
1033
		}
1034
	}
1035
1036
1037
	$context['start'] = $context['start'] + $num_at_once;
1038
	if (empty($context['recipients']['emails']) && ($context['start'] >= $context['total_members']))
1039
	{
1040
		// Log this into the admin log.
1041
		logAction('newsletter', array(), 'admin');
1042
		$_SESSION['newsletter_sent'] = 'queue_done';
1043
		redirectexit('action=admin;area=news;sa=mailingmembers');
1044
	}
1045
1046
	// Working out progress is a black art of sorts.
1047
	$percentEmails = $context['total_emails'] == 0 ? 0 : ((count($context['recipients']['emails']) / $context['total_emails']) * ($context['total_emails'] / ($context['total_emails'] + $context['total_members'])));
1048
	$percentMembers = ($context['start'] / $context['total_members']) * ($context['total_members'] / ($context['total_emails'] + $context['total_members']));
1049
	$context['percentage_done'] = round(($percentEmails + $percentMembers) * 100, 2);
1050
1051
	$context['page_title'] = $txt['admin_newsletters'];
1052
	$context['sub_template'] = 'email_members_send';
1053
}
1054
1055
/**
1056
 * Set general news and newsletter settings and permissions.
1057
 * Called by ?action=admin;area=news;sa=settings.
1058
 * Requires the forum_admin permission.
1059
 *
1060
 * @uses ManageNews template, news_settings sub-template.
1061
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
1062
 * @return void|array Returns nothing or returns the config_vars array if $return_config is true
1063
 */
1064
function ModifyNewsSettings($return_config = false)
1065
{
1066
	global $context, $sourcedir, $txt, $scripturl;
1067
1068
	$config_vars = array(
1069
		array('title', 'settings'),
1070
			// Inline permissions.
1071
			array('permissions', 'edit_news', 'help' => ''),
1072
			array('permissions', 'send_mail'),
1073
		'',
1074
			// Just the remaining settings.
1075
			array('check', 'xmlnews_enable', 'onclick' => 'document.getElementById(\'xmlnews_maxlen\').disabled = !this.checked;'),
1076
			array('int', 'xmlnews_maxlen', 'subtext' => $txt['xmlnews_maxlen_note'], 10),
1077
			array('check', 'xmlnews_attachments', 'subtext' => $txt['xmlnews_attachments_note']),
1078
	);
1079
1080
	call_integration_hook('integrate_modify_news_settings', array(&$config_vars));
1081
1082
	if ($return_config)
1083
		return $config_vars;
1084
1085
	$context['page_title'] = $txt['admin_edit_news'] . ' - ' . $txt['settings'];
1086
	$context['sub_template'] = 'show_settings';
1087
1088
	// Needed for the settings template.
1089
	require_once($sourcedir . '/ManageServer.php');
1090
1091
	// Wrap it all up nice and warm...
1092
	$context['post_url'] = $scripturl . '?action=admin;area=news;save;sa=settings';
1093
1094
	// Add some javascript at the bottom...
1095
	addInlineJavaScript('
1096
	document.getElementById("xmlnews_maxlen").disabled = !document.getElementById("xmlnews_enable").checked;', true);
1097
1098
	// Saving the settings?
1099
	if (isset($_GET['save']))
1100
	{
1101
		checkSession();
1102
1103
		call_integration_hook('integrate_save_news_settings');
1104
1105
		saveDBSettings($config_vars);
1106
		$_SESSION['adm-save'] = true;
1107
		redirectexit('action=admin;area=news;sa=settings');
1108
	}
1109
1110
	// We need this for the in-line permissions
1111
	createToken('admin-mp');
1112
1113
	prepareDBSettingContext($config_vars);
1114
}
1115
1116
?>