Completed
Pull Request — patch_1-1-4 (#3202)
by Spuds
15:49
created

Daily_Digest::run()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Send out emails of all subscribed topics, to members.
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
15
 *
16
 */
17
18
namespace ElkArte\sources\subs\ScheduledTask;
19
20
/**
21
 * Class Daily_Digest - Send out a daily email of all subscribed topics, to members.
22
 *
23
 * - It sends notifications about replies or new topics, and moderation actions.
24
 *
25
 * @package ScheduledTasks
26
 */
27
class Daily_Digest implements Scheduled_Task_Interface
28
{
29
	/**
30
	 * Sends out the daily digest for all the DD subscribers
31
	 *
32
	 * @return bool
33
	 */
34 1
	public function run()
35
	{
36 1
		return $this->runDigest();
37
	}
38
39
	/**
40
	 * Send out a email of all subscribed topics, to members.
41
	 *
42
	 * - Builds email body's of topics and messages per user as defined by their
43
	 * notification settings
44
	 * - If weekly builds the weekly abridged digest
45
	 *
46
	 * @param bool $is_weekly
47
	 *
48
	 * @return bool
49
	 */
50 2
	public function runDigest($is_weekly = false)
51
	{
52 2
		global $txt, $mbname, $scripturl, $modSettings, $boardurl;
53
54 2
		$db = database();
55
56
		// We'll want this...
57 2
		require_once(SUBSDIR . '/Mail.subs.php');
58 2
		loadEssentialThemeData();
59
60
		// If the maillist function is on then so is the enhanced digest
61 2
		$maillist = !empty($modSettings['maillist_enabled']) && !empty($modSettings['pbe_digest_enabled']);
62
		if ($maillist)
63 2
			require_once(SUBSDIR . '/Emailpost.subs.php');
64
65 2
		$is_weekly = !empty($is_weekly) ? 1 : 0;
66
67
		// Right - get all the notification data FIRST.
68 2
		$request = $db->query('', '
69
			SELECT 
70
				ln.id_topic, COALESCE(t.id_board, ln.id_board) AS id_board, 
71
				mem.email_address, mem.member_name, mem.real_name, mem.notify_types, mem.lngfile, mem.id_member
72
			FROM {db_prefix}log_notify AS ln
73
				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
74
				LEFT JOIN {db_prefix}topics AS t ON (ln.id_topic != {int:empty_topic} AND t.id_topic = ln.id_topic)
75
			WHERE mem.notify_regularity = {int:notify_regularity}
76 2
				AND mem.is_activated = {int:is_activated}',
77
			array(
78 2
				'empty_topic' => 0,
79 2
				'notify_regularity' => $is_weekly ? '3' : '2',
80 2
				'is_activated' => 1,
81
			)
82 2
		);
83 2
		$members = array();
84 2
		$langs = array();
85 2
		$notify = array();
86 2
		$boards = array();
87 2
		while ($row = $db->fetch_assoc($request))
88
		{
89
			if (!isset($members[$row['id_member']]))
90
			{
91
				$members[$row['id_member']] = array(
92
					'email' => $row['email_address'],
93
					'name' => ($row['real_name'] == '') ? $row['member_name'] : un_htmlspecialchars($row['real_name']),
94
					'id' => $row['id_member'],
95
					'notifyMod' => $row['notify_types'] < 3 ? true : false,
96
					'lang' => $row['lngfile'],
97
				);
98
				$langs[$row['lngfile']] = $row['lngfile'];
99
			}
100
101
			// Store this useful data!
102
			$boards[$row['id_board']] = $row['id_board'];
103
			if ($row['id_topic'])
104
				$notify['topics'][$row['id_topic']][] = $row['id_member'];
105
			else
106
				$notify['boards'][$row['id_board']][] = $row['id_member'];
107
		}
108 2
		$db->free_result($request);
109
110 2
		if (empty($boards))
111 2
			return true;
112
113
		// Just get the board names.
114
		require_once(SUBSDIR . '/Boards.subs.php');
115
		$boards = fetchBoardsInfo(array('boards' => $boards), array('override_permissions' => true));
116
117
		if (empty($boards))
118
			return true;
119
120
		// Get the actual topics...
121
		$request = $db->query('', '
122
			SELECT 
123
				ld.note_type, ld.id_msg AS last_reply,
124
				t.id_topic, t.id_board, t.id_member_started, 
125
				m.id_msg, m.subject, m.body, 
126
				b.name AS board_name, 
127
				ml.body as last_body
128
			FROM {db_prefix}log_digest AS ld
129
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ld.id_topic
130
					AND t.id_board IN ({array_int:board_list}))
131
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
132
				INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = ld.id_msg)
133
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
134
			WHERE ' . ($is_weekly ? 'ld.daily != {int:daily_value}' : 'ld.daily IN (0, 2)'),
135
			array(
136
				'board_list' => array_keys($boards),
137
				'daily_value' => 2,
138
			)
139
		);
140
		$types = array();
141
		while ($row = $db->fetch_assoc($request))
142
		{
143
			if (!isset($types[$row['note_type']][$row['id_board']]))
144
				$types[$row['note_type']][$row['id_board']] = array(
145
					'lines' => array(),
146
					'name' => un_htmlspecialchars($row['board_name']),
147
					'id' => $row['id_board'],
148
				);
149
150
			// A reply has been made
151
			if ($row['note_type'] === 'reply')
152
			{
153
				// More than one reply to this topic?
154
				if (isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]))
155
				{
156
					$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['count']++;
157
158
					// keep track of the highest numbered reply and body text for this topic ...
159
					if ($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['body_id'] < $row['last_reply'])
160
					{
161
						$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['body_id'] = $row['last_reply'];
162
						$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['body_text'] = $row['last_body'];
163
					}
164
				}
165
				else
166
				{
167
					// First time we have seen a reply to this topic, so load our array
168
					$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array(
169
						'id' => $row['id_topic'],
170
						'subject' => un_htmlspecialchars($row['subject']),
171
						'link' => $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new',
172
						'count' => 1,
173
						'body_id' => $row['last_reply'],
174
						'body_text' => $row['last_body'],
175
					);
176
				}
177
			}
178
			// New topics are good too
179
			elseif ($row['note_type'] === 'topic')
180
			{
181
				if ($maillist)
182
				{
183
					// Convert to markdown markup e.g. text ;)
184
					pbe_prepare_text($row['body']);
185
					$row['body'] = \Util::shorten_text($row['body'], !empty($modSettings['digest_preview_length']) ? $modSettings['digest_preview_length'] : 375, true);
186
					$row['body'] = preg_replace("~\n~s", "\n  ", $row['body']);
187
				}
188
189
				// Topics are simple since we are only concerned with the first post
190
				if (!isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]))
191
					$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array(
192
						'id' => $row['id_topic'],
193
						'link' => $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new',
194
						'subject' => un_htmlspecialchars($row['subject']),
195
						'body' => $row['body'],
196
					);
197
			}
198
			elseif ($maillist && empty($modSettings['pbe_no_mod_notices']))
199
			{
200
				if (!isset($types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]))
201
				{
202
					$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']] = array(
203
						'id' => $row['id_topic'],
204
						'subject' => un_htmlspecialchars($row['subject']),
205
						'starter' => $row['id_member_started'],
206
					);
207
				}
208
			}
209
210
			$types[$row['note_type']][$row['id_board']]['lines'][$row['id_topic']]['members'] = array();
211
212 View Code Duplication
			if (!empty($notify['topics'][$row['id_topic']]))
213
				$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']]);
214
215 View Code Duplication
			if (!empty($notify['boards'][$row['id_board']]))
216
				$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']]);
217
		}
218
		$db->free_result($request);
219
220
		if (empty($types))
221
			return true;
222
223
		// Fix the last reply message so its suitable for previewing
224
		if ($maillist)
225
		{
226
			foreach ($types['reply'] as $id => $board)
227
			{
228
				foreach ($board['lines'] as $topic)
229
				{
230
					// Replace the body array with the appropriate preview message
231
					$body = $types['reply'][$id]['lines'][$topic['id']]['body_text'];
232
					pbe_prepare_text($body);
233
					$body = \Util::shorten_text($body, !empty($modSettings['digest_preview_length']) ? $modSettings['digest_preview_length'] : 375, true);
234
					$body = preg_replace("~\n~s", "\n  ", $body);
235
					$types['reply'][$id]['lines'][$topic['id']]['body'] = $body;
236
237
					unset($types['reply'][$id]['lines'][$topic['id']]['body_text'], $body);
238
				}
239
			}
240
		}
241
242
		// Let's load all the languages into a cache thingy.
243
		$langtxt = array();
244
		foreach ($langs as $lang)
245
		{
246
			loadLanguage('Post', $lang);
247
			loadLanguage('index', $lang);
248
			loadLanguage('Maillist', $lang);
249
			loadLanguage('EmailTemplates', $lang);
250
251
			$langtxt[$lang] = array(
252
				'subject' => $txt['digest_subject_' . ($is_weekly ? 'weekly' : 'daily')],
253
				'char_set' => 'UTF-8',
254
				'intro' => sprintf($txt['digest_intro_' . ($is_weekly ? 'weekly' : 'daily')], $mbname),
255
				'new_topics' => $txt['digest_new_topics'],
256
				'topic_lines' => $txt['digest_new_topics_line'],
257
				'new_replies' => $txt['digest_new_replies'],
258
				'mod_actions' => $txt['digest_mod_actions'],
259
				'replies_one' => $txt['digest_new_replies_one'],
260
				'replies_many' => $txt['digest_new_replies_many'],
261
				'sticky' => $txt['digest_mod_act_sticky'],
262
				'lock' => $txt['digest_mod_act_lock'],
263
				'unlock' => $txt['digest_mod_act_unlock'],
264
				'remove' => $txt['digest_mod_act_remove'],
265
				'move' => $txt['digest_mod_act_move'],
266
				'merge' => $txt['digest_mod_act_merge'],
267
				'split' => $txt['digest_mod_act_split'],
268
				'bye' => (!empty($modSettings['maillist_sitename_regards']) ? $modSettings['maillist_sitename_regards'] : '') . "\n" . $boardurl,
269
				'preview' => $txt['digest_preview'],
270
				'see_full' => $txt['digest_see_full'],
271
				'reply_preview' => $txt['digest_reply_preview'],
272
				'unread_reply_link' => $txt['digest_unread_reply_link'],
273
			);
274
		}
275
276
		// Right - send out the silly things - this will take quite some space!
277
		foreach ($members as $mid => $member)
278
		{
279
			// Do the start stuff!
280
			$email = array(
281
				'subject' => $mbname . ' - ' . $langtxt[$lang]['subject'],
0 ignored issues
show
Bug introduced by
The variable $lang seems to be defined by a foreach iteration on line 244. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
282
				'body' => $member['name'] . ',' . "\n\n" . $langtxt[$lang]['intro'] . "\n" . $scripturl . '?action=profile;area=notification;u=' . $member['id'] . "\n",
283
				'email' => $member['email'],
284
			);
285
286
			// All the new topics
287
			if (isset($types['topic']))
288
			{
289
				$titled = false;
290
291
				// Each type contains a board ID and then a topic number
292
				foreach ($types['topic'] as $id => $board)
293
				{
294
					foreach ($board['lines'] as $topic)
295
					{
296
						// They have requested notification for new topics in this board
297
						if (in_array($mid, $topic['members']))
298
						{
299
							// Start of the new topics with a heading bar
300 View Code Duplication
							if (!$titled)
301
							{
302
								$email['body'] .= "\n" . $langtxt[$lang]['new_topics'] . ':' . "\n" . str_repeat('-', 78);
303
								$titled = true;
304
							}
305
306
							$email['body'] .= "\n" . sprintf($langtxt[$lang]['topic_lines'], $topic['subject'], $board['name']);
307 View Code Duplication
							if ($maillist)
308
								$email['body'] .= $langtxt[$lang]['preview'] . $topic['body'] . $langtxt[$lang]['see_full'] . $topic['link'] . "\n";
309
						}
310
					}
311
				}
312
313
				if ($titled)
314
					$email['body'] .= "\n";
315
			}
316
317
			// What about replies?
318
			if (isset($types['reply']))
319
			{
320
				$titled = false;
321
322
				// Each reply will have a board id and then a topic ID
323
				foreach ($types['reply'] as $id => $board)
324
				{
325
					foreach ($board['lines'] as $topic)
326
					{
327
						// This member wants notices on reply's to this topic
328
						if (in_array($mid, $topic['members']))
329
						{
330
							// First one in the section gets a nice heading
331 View Code Duplication
							if (!$titled)
332
							{
333
								$email['body'] .= "\n" . $langtxt[$lang]['new_replies'] . ':' . "\n" . str_repeat('-', 78);
334
								$titled = true;
335
							}
336
337
							$email['body'] .= "\n" . ($topic['count'] === 1 ? sprintf($langtxt[$lang]['replies_one'], $topic['subject']) : sprintf($langtxt[$lang]['replies_many'], $topic['count'], $topic['subject']));
338 View Code Duplication
							if ($maillist)
339
								$email['body'] .= $langtxt[$lang]['reply_preview'] . $topic['body'] . $langtxt[$lang]['unread_reply_link'] . $topic['link'] . "\n";
340
						}
341
					}
342
				}
343
344
				if ($titled)
345
					$email['body'] .= "\n";
346
			}
347
348
			// Finally, moderation actions!
349
			$titled = false;
350
			foreach ($types as $note_type => $type)
351
			{
352
				if ($note_type === 'topic' || $note_type === 'reply')
353
					continue;
354
355
				foreach ($type as $id => $board)
356
				{
357
					foreach ($board['lines'] as $topic)
358
					{
359
						if (in_array($mid, $topic['members']))
360
						{
361 View Code Duplication
							if (!$titled)
362
							{
363
								$email['body'] .= "\n" . $langtxt[$lang]['mod_actions'] . ':' . "\n" . str_repeat('-', 47);
364
								$titled = true;
365
							}
366
367
							$email['body'] .= "\n" . sprintf($langtxt[$lang][$note_type], $topic['subject']);
368
						}
369
					}
370
				}
371
			}
372
373
			if ($titled)
374
				$email['body'] .= "\n";
375
376
			// Then just say our goodbyes!
377
			$email['body'] .= "\n\n" . $langtxt[$lang]['bye'];
378
379
			// Send it - low priority!
380
			sendmail($email['email'], $email['subject'], $email['body'], null, null, false, 4);
381
		}
382
383
		// Using the queue, do a final flush before we say that's all folks
384
		if (!empty($modSettings['mail_queue']))
385
			AddMailQueue(true);
386
387
		// Clean up...
388
		if ($is_weekly)
389
		{
390
			$db->query('', '
391
				DELETE FROM {db_prefix}log_digest
392
				WHERE daily != {int:not_daily}',
393
				array(
394
					'not_daily' => 0,
395
				)
396
			);
397
			$db->query('', '
398
				UPDATE {db_prefix}log_digest
399
				SET daily = {int:daily_value}
400
				WHERE daily = {int:not_daily}',
401
				array(
402
					'daily_value' => 2,
403
					'not_daily' => 0,
404
				)
405
			);
406
		}
407
		else
408
		{
409
			// Clear any only weekly ones, and stop us from sending daily again.
410
			$db->query('', '
411
				DELETE FROM {db_prefix}log_digest
412
				WHERE daily = {int:daily_value}',
413
				array(
414
					'daily_value' => 2,
415
				)
416
			);
417
			$db->query('', '
418
				UPDATE {db_prefix}log_digest
419
				SET daily = {int:both_value}
420
				WHERE daily = {int:no_value}',
421
				array(
422
					'both_value' => 1,
423
					'no_value' => 0,
424
				)
425
			);
426
		}
427
428
		// Just in case the member changes their settings mark this as sent.
429
		$members = array_keys($members);
430
		$db->query('', '
431
			UPDATE {db_prefix}log_notify
432
			SET sent = {int:is_sent}
433
			WHERE id_member IN ({array_int:member_list})',
434
			array(
435
				'member_list' => $members,
436
				'is_sent' => 1,
437
			)
438
		);
439
440
		// Log we've done it...
441
		return true;
442
	}
443
}