DailyDigest::runDigest()   F
last analyzed

Complexity

Conditions 62
Paths > 20000

Size

Total Lines 427
Code Lines 195

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 3906

Importance

Changes 0
Metric Value
cc 62
eloc 195
nc 9983848
nop 1
dl 0
loc 427
rs 0
c 0
b 0
f 0
ccs 0
cts 182
cp 0
crap 3906

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
 * Send out emails of all subscribed topics, to members.
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\ScheduledTasks\Tasks;
18
19
use ElkArte\Helper\Util;
20
use ElkArte\Languages\Loader;
21
use ElkArte\Themes\ThemeLoader;
22
23
/**
24
 * Class DailyDigest - Send out a daily email of all subscribed topics, to members.
25
 *
26
 * - It sends notifications about replies or new topics, and moderation actions.
27
 *
28
 * @package ScheduledTasks
29
 */
30
class DailyDigest implements ScheduledTaskInterface
31
{
32
	/**
33
	 * Sends out the daily digest for all the DD subscribers
34
	 *
35
	 * @return bool
36
	 */
37
	public function run()
38
	{
39
		return $this->runDigest();
40
	}
41
42
	/**
43
	 * Send out a email of all subscribed topics, to members.
44
	 *
45
	 * What it does:
46
	 *
47
	 * - Builds email body's of topics and messages per user as defined by their notification settings
48
	 * - If weekly builds the weekly abridged digest
49
	 *
50
	 * @param bool $is_weekly
51
	 *
52
	 * @return bool
53
	 */
54
	public function runDigest($is_weekly = false)
55
	{
56
		global $mbname, $modSettings, $boardurl;
57
58
		$db = database();
59
60
		// We'll want this...
61
		require_once(SUBSDIR . '/Mail.subs.php');
62
		ThemeLoader::loadEssentialThemeData();
63
64
		// If the maillist function is on then so is the enhanced digest
65
		$maillist = !empty($modSettings['maillist_enabled']) && !empty($modSettings['maillist_digest_enabled']);
66
		if ($maillist)
67
		{
68
			require_once(SUBSDIR . '/Maillist.subs.php');
69
		}
70
71
		$is_weekly = empty($is_weekly) ? 0 : 1;
72
73
		// Right - get all the notification data FIRST.
74
		$request = $db->fetchQuery('
75
			SELECT 
76
				ln.id_topic, COALESCE(t.id_board, ln.id_board) AS id_board, 
77
				mem.email_address, mem.member_name, mem.real_name, mem.notify_types, mem.lngfile, mem.id_member
78
			FROM {db_prefix}log_notify AS ln
79
				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
80
				LEFT JOIN {db_prefix}topics AS t ON (ln.id_topic != {int:empty_topic} AND t.id_topic = ln.id_topic)
81
			WHERE mem.notify_regularity = {int:notify_regularity}
82
				AND mem.is_activated = {int:is_activated}',
83
			array(
84
				'empty_topic' => 0,
85
				'notify_regularity' => $is_weekly !== 0 ? '3' : '2',
86
				'is_activated' => 1,
87
			)
88
		);
89
		$members = [];
90
		$langs = [];
91
		$notify = [];
92
		$boards = [];
93
		while (($row = $request->fetch_assoc()))
94
		{
95
			if (!isset($members[$row['id_member']]))
96
			{
97
				$members[$row['id_member']] = array(
98
					'email' => $row['email_address'],
99
					'name' => ($row['real_name'] === '') ? $row['member_name'] : un_htmlspecialchars($row['real_name']),
100
					'id' => $row['id_member'],
101
					'notifyMod' => $row['notify_types'] < 3,
102
					'lang' => $row['lngfile'],
103
				);
104
				$langs[$row['lngfile']] = $row['lngfile'];
105
			}
106
107
			// Store this useful data!
108
			$boards[$row['id_board']] = $row['id_board'];
109
			if ($row['id_topic'])
110
			{
111
				$notify['topics'][$row['id_topic']][] = $row['id_member'];
112
			}
113
			else
114
			{
115
				$notify['boards'][$row['id_board']][] = $row['id_member'];
116
			}
117
		}
118
119
		$request->free_result();
120
121
		if (empty($boards))
122
		{
123
			return true;
124
		}
125
126
		// Just get the board names.
127
		require_once(SUBSDIR . '/Boards.subs.php');
128
		$boards = fetchBoardsInfo(array('boards' => $boards), array('override_permissions' => true));
129
130
		if (empty($boards))
131
		{
132
			return true;
133
		}
134
135
		// Get the actual topics...
136
		$request = $db->fetchQuery('
137
			SELECT 
138
				ld.note_type, ld.id_msg AS last_reply,
139
				t.id_topic, t.id_board, t.id_member_started, 
140
				m.id_msg, m.subject, m.body, 
141
				b.name AS board_name, 
142
				ml.body as last_body
143
			FROM {db_prefix}log_digest AS ld
144
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ld.id_topic
145
					AND t.id_board IN ({array_int:board_list}))
146
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
147
				INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = ld.id_msg)
148
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
149
			WHERE ' . ($is_weekly !== 0 ? 'ld.daily != {int:daily_value}' : 'ld.daily IN (0, 2)'),
150
			array(
151
				'board_list' => array_keys($boards),
152
				'daily_value' => 2,
153
			)
154
		);
155
		$types = array();
156
		while (($row = $request->fetch_assoc()))
157
		{
158
			if (!isset($types[$row['note_type']][$row['id_board']]))
159
			{
160
				$types[$row['note_type']][$row['id_board']] = array(
161
					'lines' => array(),
162
					'name' => un_htmlspecialchars($row['board_name']),
163
					'id' => $row['id_board'],
164
				);
165
			}
166
167
			// A reply has been made
168
			if ($row['note_type'] === 'reply')
169
			{
170
				// More than one reply to this topic?
171
				if (isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]))
172
				{
173
					$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['count']++;
174
175
					// keep track of the highest numbered reply and body text for this topic ...
176
					if ($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['body_id'] < $row['last_reply'])
177
					{
178
						$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['body_id'] = $row['last_reply'];
179
						$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['body_text'] = $row['last_body'];
180
					}
181
				}
182
				else
183
				{
184
					// First time we have seen a reply to this topic, so load our array
185
					$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array(
186
						'id' => $row['id_topic'],
187
						'subject' => un_htmlspecialchars($row['subject']),
188
						'link' => getUrl('action', ['topic' => $row['id_topic'] . '.new', 'topicseen', 'hash' => '#new']),
189
						'count' => 1,
190
						'body_id' => $row['last_reply'],
191
						'body_text' => $row['last_body'],
192
					);
193
				}
194
			}
195
			// New topics are good too
196
			elseif ($row['note_type'] === 'topic')
197
			{
198
				if ($maillist)
199
				{
200
					// Convert to markdown markup e.g. text ;)
201
					pbe_prepare_text($row['body']);
202
					$row['body'] = Util::shorten_text($row['body'], empty($modSettings['digest_preview_length']) ? 375 : $modSettings['digest_preview_length'], true);
203
					$row['body'] = preg_replace("~\n~", "\n  ", $row['body']);
204
				}
205
206
				// Topics are simple since we are only concerned with the first post
207
				if (!isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]))
208
				{
209
					$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array(
210
						'id' => $row['id_topic'],
211
						'link' => getUrl('action', ['topic' => $row['id_topic'] . '.new', 'topicseen', 'hash' => '#new']),
212
						'subject' => un_htmlspecialchars($row['subject']),
213
						'body' => $row['body'],
214
					);
215
				}
216
			}
217
			elseif ($maillist && empty($modSettings['pbe_no_mod_notices']))
218
			{
219
				if (!isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]))
220
				{
221
					$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array(
222
						'id' => $row['id_topic'],
223
						'subject' => un_htmlspecialchars($row['subject']),
224
						'starter' => $row['id_member_started'],
225
					);
226
				}
227
			}
228
229
			$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array();
230
231
			if (!empty($notify['topics'][$row['id_topic']]))
232
			{
233
				$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array_merge($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'], $notify['topics'][$row['id_topic']]);
234
			}
235
236
			if (!empty($notify['boards'][$row['id_board']]))
237
			{
238
				$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array_merge($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'], $notify['boards'][$row['id_board']]);
239
			}
240
		}
241
242
		$request->free_result();
243
244
		if (empty($types))
245
		{
246
			return true;
247
		}
248
249
		// Fix the last reply message so its suitable for previewing
250
		if ($maillist && !empty($types['reply']))
251
		{
252
			foreach ($types['reply'] as $id => $board)
253
			{
254
				foreach ($board['lines'] as $topic)
255
				{
256
					// Replace the body array with the appropriate preview message
257
					$body = $types['reply'][$id]['lines'][$topic['id']]['body_text'];
258
					pbe_prepare_text($body);
259
					$body = Util::shorten_text($body, empty($modSettings['digest_preview_length']) ? 375 : $modSettings['digest_preview_length'], true);
260
					$body = preg_replace("~\n~", "\n  ", $body);
261
					$types['reply'][$id]['lines'][$topic['id']]['body'] = $body;
262
263
					unset($types['reply'][$id]['lines'][$topic['id']]['body_text'], $body);
264
				}
265
			}
266
		}
267
268
		// Let's load all the languages into a cache thingy.
269
		$langtxt = array();
270
		foreach ($langs as $lang)
271
		{
272
			$mtxt = [];
273
			$lang_loader = new Loader($lang, $mtxt, database());
274
			$lang_loader->load('index+Post+Maillist+EmailTemplates');
275
276
			$langtxt[$lang] = array(
277
				'subject' => $mtxt['digest_subject_' . ($is_weekly !== 0 ? 'weekly' : 'daily')],
278
				'char_set' => 'UTF-8',
279
				'intro' => sprintf($mtxt['digest_intro_' . ($is_weekly !== 0 ? 'weekly' : 'daily')], $mbname),
280
				'new_topics' => $mtxt['digest_new_topics'],
281
				'topic_lines' => $mtxt['digest_new_topics_line'],
282
				'new_replies' => $mtxt['digest_new_replies'],
283
				'mod_actions' => $mtxt['digest_mod_actions'],
284
				'replies_one' => $mtxt['digest_new_replies_one'],
285
				'replies_many' => $mtxt['digest_new_replies_many'],
286
				'sticky' => $mtxt['digest_mod_act_sticky'],
287
				'lock' => $mtxt['digest_mod_act_lock'],
288
				'unlock' => $mtxt['digest_mod_act_unlock'],
289
				'remove' => $mtxt['digest_mod_act_remove'],
290
				'move' => $mtxt['digest_mod_act_move'],
291
				'merge' => $mtxt['digest_mod_act_merge'],
292
				'split' => $mtxt['digest_mod_act_split'],
293
				'bye' => (empty($modSettings['maillist_sitename_regards']) ? '' : $modSettings['maillist_sitename_regards']) . "\n" . $boardurl,
294
				'preview' => $mtxt['digest_preview'],
295
				'see_full' => $mtxt['digest_see_full'],
296
				'reply_preview' => $mtxt['digest_reply_preview'],
297
				'unread_reply_link' => $mtxt['digest_unread_reply_link'],
298
			);
299
		}
300
301
		// Right - send out the silly things - this will take quite some space!
302
		foreach ($members as $mid => $member)
303
		{
304
			// Do the start stuff!
305
			$email = array(
306
				'subject' => $mbname . ' - ' . $langtxt[$lang]['subject'],
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $lang seems to be defined by a foreach iteration on line 270. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
307
				'body' => $member['name'] . ',' . "\n\n" . $langtxt[$lang]['intro'] . "\n" . getUrl('profile', ['action' => 'profile', 'area' => 'notification', 'u' => $member['id'], 'name' => $member['name']]) . "\n",
308
				'email' => $member['email'],
309
			);
310
311
			// All the new topics
312
			if (isset($types['topic']))
313
			{
314
				$titled = false;
315
316
				// Each type contains a board ID and then a topic number
317
				foreach ($types['topic'] as $board)
318
				{
319
					foreach ($board['lines'] as $topic)
320
					{
321
						// They have requested notification for new topics in this board
322
						if (in_array($mid, $topic['members']))
323
						{
324
							// Start of the new topics with a heading bar
325
							if (!$titled)
326
							{
327
								$email['body'] .= "\n" . $langtxt[$lang]['new_topics'] . ':' . "\n" . str_repeat('-', 78);
328
								$titled = true;
329
							}
330
331
							$email['body'] .= "\n" . sprintf($langtxt[$lang]['topic_lines'], $topic['subject'], $board['name']);
332
							if ($maillist)
333
							{
334
								$email['body'] .= $langtxt[$lang]['preview'] . $topic['body'] . $langtxt[$lang]['see_full'] . $topic['link'] . "\n";
335
							}
336
						}
337
					}
338
				}
339
340
				if ($titled)
341
				{
342
					$email['body'] .= "\n";
343
				}
344
			}
345
346
			// What about replies?
347
			if (isset($types['reply']))
348
			{
349
				$titled = false;
350
351
				// Each reply will have a board id and then a topic ID
352
				foreach ($types['reply'] as $board)
353
				{
354
					foreach ($board['lines'] as $topic)
355
					{
356
						// This member wants notices on reply's to this topic
357
						if (in_array($mid, $topic['members']))
358
						{
359
							// First one in the section gets a nice heading
360
							if (!$titled)
361
							{
362
								$email['body'] .= "\n" . $langtxt[$lang]['new_replies'] . ':' . "\n" . str_repeat('-', 78);
363
								$titled = true;
364
							}
365
366
							$email['body'] .= "\n" . ($topic['count'] === 1 ? sprintf($langtxt[$lang]['replies_one'], $topic['subject']) : sprintf($langtxt[$lang]['replies_many'], $topic['count'], $topic['subject']));
367
							if ($maillist)
368
							{
369
								$email['body'] .= $langtxt[$lang]['reply_preview'] . $topic['body'] . $langtxt[$lang]['unread_reply_link'] . $topic['link'] . "\n";
370
							}
371
						}
372
					}
373
				}
374
375
				if ($titled)
376
				{
377
					$email['body'] .= "\n";
378
				}
379
			}
380
381
			// Finally, moderation actions!
382
			$titled = false;
383
			foreach ($types as $note_type => $type)
384
			{
385
				if ($note_type === 'topic' || $note_type === 'reply')
386
				{
387
					continue;
388
				}
389
390
				foreach ($type as $board)
391
				{
392
					foreach ($board['lines'] as $topic)
393
					{
394
						if (in_array($mid, $topic['members']))
395
						{
396
							if (!$titled)
397
							{
398
								$email['body'] .= "\n" . $langtxt[$lang]['mod_actions'] . ':' . "\n" . str_repeat('-', 47);
399
								$titled = true;
400
							}
401
402
							$email['body'] .= "\n" . sprintf($langtxt[$lang][$note_type], $topic['subject']);
403
						}
404
					}
405
				}
406
			}
407
408
			if ($titled)
409
			{
410
				$email['body'] .= "\n";
411
			}
412
413
			// Then just say our goodbyes!
414
			$email['body'] .= "\n\n" . $langtxt[$lang]['bye'];
415
416
			// Send it - low priority!
417
			sendmail($email['email'], $email['subject'], $email['body'], null, null, false, 4);
418
		}
419
420
		// Using the queue, do a final flush before we say that's all folks
421
		if (!empty($modSettings['mail_queue']))
422
		{
423
			AddMailQueue(true);
424
		}
425
426
		// Clean up...
427
		if ($is_weekly !== 0)
428
		{
429
			$db->query('', '
430
				DELETE FROM {db_prefix}log_digest
431
				WHERE daily != {int:not_daily}',
432
				array(
433
					'not_daily' => 0,
434
				)
435
			);
436
			$db->query('', '
437
				UPDATE {db_prefix}log_digest
438
				SET daily = {int:daily_value}
439
				WHERE daily = {int:not_daily}',
440
				array(
441
					'daily_value' => 2,
442
					'not_daily' => 0,
443
				)
444
			);
445
		}
446
		else
447
		{
448
			// Clear any only weekly ones, and stop us from sending daily again.
449
			$db->query('', '
450
				DELETE FROM {db_prefix}log_digest
451
				WHERE daily = {int:daily_value}',
452
				array(
453
					'daily_value' => 2,
454
				)
455
			);
456
			$db->query('', '
457
				UPDATE {db_prefix}log_digest
458
				SET daily = {int:both_value}
459
				WHERE daily = {int:no_value}',
460
				array(
461
					'both_value' => 1,
462
					'no_value' => 0,
463
				)
464
			);
465
		}
466
467
		// Just in case the member changes their settings mark this as sent.
468
		$members = array_keys($members);
469
		$db->query('', '
470
			UPDATE {db_prefix}log_notify
471
			SET sent = {int:is_sent}
472
			WHERE id_member IN ({array_int:member_list})',
473
			array(
474
				'member_list' => $members,
475
				'is_sent' => 1,
476
			)
477
		);
478
479
		// Log we've done it...
480
		return true;
481
	}
482
}
483