Completed
Push — development ( 84535f...6fa0bd )
by Stephen
16s
created

Notifications::_send_task()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 31
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 4.002

Importance

Changes 0
Metric Value
cc 4
eloc 15
nc 4
nop 1
dl 0
loc 31
ccs 19
cts 20
cp 0.95
crap 4.002
rs 8.5806
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Class that centralize the "notification" process.
5
 * ... or at least tries to.
6
 *
7
 * @name      ElkArte Forum
8
 * @copyright ElkArte Forum contributors
9
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
10
 *
11
 * @version 1.1 Release Candidate 2
12
 *
13
 */
14
15
/**
16
 * Class Notifications
17
 *
18
 * Core area for notifications, defines the abstract model
19
 */
20
class Notifications extends AbstractModel
21
{
22
	/**
23
	 * Instance manager
24
	 *
25
	 * @var Notifications
26
	 */
27
	protected static $_instance;
28
29
	/**
30
	 * List of notifications to send
31
	 *
32
	 * @var \Notifications_Task[]
33
	 */
34
	protected $_to_send;
35
36
	/**
37
	 * Available notification frequencies
38
	 *
39
	 * @var string[]
40
	 */
41
	protected $_notification_frequencies;
42
43
	/**
44
	 * Available notification frequencies
45
	 *
46
	 * @var array
47
	 */
48
	protected $_notifiers;
49
	/**
50
	 * Notifications constructor.
51
	 *
52
	 * Registers the known notifications to the system, allows for integration to add more
53
	 *
54
	 * @param \Database $db
55
	 * @throws Elk_Exception
56
	 */
57
	public function __construct($db)
58
	{
59
		parent::__construct($db);
60
61
		// Let's register all the notifications we know by default
62
		$this->register(1, 'notification', array($this, '_send_notification'));
63
		$this->register(2, 'email', array($this, '_send_email'), array('subject' => 'subject', 'body' => 'body', 'suffix' => true));
0 ignored issues
show
Documentation introduced by
array('subject' => 'subj...ody', 'suffix' => true) is of type array<string,string|bool...g","suffix":"boolean"}>, but the function expects a null|array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
64
		$this->register(3, 'email_daily', array($this, '_send_daily_email'), array('subject' => 'subject', 'body' => 'snippet', 'suffix' => true));
0 ignored issues
show
Documentation introduced by
array('subject' => 'subj...pet', 'suffix' => true) is of type array<string,string|bool...g","suffix":"boolean"}>, but the function expects a null|array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
65
		$this->register(4, 'email_weekly', array($this, '_send_weekly_email'), array('subject' => 'subject', 'body' => 'snippet', 'suffix' => true));
0 ignored issues
show
Documentation introduced by
array('subject' => 'subj...pet', 'suffix' => true) is of type array<string,string|bool...g","suffix":"boolean"}>, but the function expects a null|array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
66
67
		call_integration_hook('integrate_notifications_methods', array($this));
68
	}
69
70
	/**
71
	 * We hax a new notification to send out!
72
	 *
73
	 * @param \Notifications_Task $task
74
	 */
75 2
	public function add(\Notifications_Task $task)
76
	{
77 2
		$this->_to_send[] = $task;
78 2
	}
79
80
	/**
81
	 * Time to notify our beloved members! YAY!
82
	 */
83 2
	public function send()
84
	{
85 2
		Elk_Autoloader::instance()->register(SUBSDIR . '/MentionType', '\\ElkArte\\sources\\subs\\MentionType');
86
87 2
		$this->_notification_frequencies = array(
88
			// 0 is for no notifications, so we start from 1 the counting, that saves a +1 later
89 2
			1 => 'notification',
90 2
			'email',
91 2
			'email_daily',
92 2
			'email_weekly',
93
		);
94
95 2
		if (!empty($this->_to_send))
96 2
		{
97 2
			foreach ($this->_to_send as $task)
98
			{
99 2
				$this->_send_task($task);
100 2
			}
101 2
		}
102
103 2
		$this->_to_send = array();
104 2
	}
105
106
	/**
107
	 * Function to register any new notification method.
108
	 *
109
	 * @param int $id This shall be a unique integer representing the notification method.
110
	 *
111
	 * <b>WARNING for addons developers</b>: note that this has to be **unique** across
112
	 * addons, so if you develop an addon that extends notifications, please verify this id
113
	 * has not been "taken" by someone else! 1-4 are reserved system notifications.
114
	 * @param int $key The string name identifying the notification method
115
	 *
116
	 *  - _send_email
117
	 *  - _send_daily_email
118
	 *  - _send_weekly_email
119
	 * @param mixed|mixed[] $callback A callable function/array/whatever that will take care
120
	 * of sending the notification
121
	 * @param null|string[] $lang_data For the moment an array containing at least:
122
	 *
123
	 *  - 'subject' => 'something'
124
	 *  - 'body' => 'something_else'
125
	 *  - 'suffix' => true/false
126
	 *
127
	 * Used to identify the strings for the subject and body respectively of the notification.
128
	 * @throws Elk_Exception
129
	 */
130
	public function register($id, $key, $callback, $lang_data = null)
131
	{
132
		if (isset($this->_notifiers[$key]))
133
		{
134
			throw new Elk_Exception('error_invalid_notification_id');
135
		}
136
137
		$this->_notifiers[$key] = array(
138
			'id' => $id,
139
			'key' => $key,
140
			'callback' => $callback,
141
			'lang_data' => $lang_data,
142
		);
143
144
		$this->_notification_frequencies[$id] = $key;
145
	}
146
147
	/**
148
	 * Returns the notifications in the system, daily, weekly, etc
149
	 *
150
	 * @return string[]
151
	 */
152 1
	public function getNotifiers()
153
	{
154 1
		return $this->_notification_frequencies;
155
	}
156
157
	/**
158
	 * Process a certain task in order to send out the notifications.
159
	 *
160
	 * @param \Notifications_Task $task
161
	 */
162 2
	protected function _send_task(\Notifications_Task $task)
163
	{
164 2
		$class = $task->getClass();
165 2
		$obj = new $class($this->_db);
166 2
		$obj->setTask($task);
167
168 2
		require_once(SUBSDIR . '/Notification.subs.php');
169 2
		require_once(SUBSDIR . '/Mail.subs.php');
170 2
		$notification_frequencies = filterNotificationMethods($this->_notification_frequencies, $class::getType());
171
172
		// Cleanup the list of members to notify,
173
		// in certain cases it may differ from the list passed (if any)
174 2
		$task->setMembers($obj->getUsersToNotify());
175 2
		$notif_prefs = $this->_getNotificationPreferences($notification_frequencies, $task->notification_type, $task->getMembers());
0 ignored issues
show
Documentation introduced by
The property notification_type does not exist on object<Notifications_Task>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
176
177 2
		foreach ($notification_frequencies as $key)
178
		{
179 2
			if (!empty($notif_prefs[$key]))
180 2
			{
181 2
				$bodies = $obj->getNotificationBody($this->_notifiers[$key]['lang_data'], $notif_prefs[$key]);
182
183
				// Just in case...
184 2
				if (empty($bodies))
185 2
				{
186
					continue;
187
				}
188
189 2
				call_user_func_array($this->_notifiers[$key]['callback'], array($obj, $task, $bodies, $this->_db));
190 2
			}
191 2
		}
192 2
	}
193
194
	/**
195
	 * Inserts a new mention in the database (those that appear in the mentions area).
196
	 *
197
	 * @param \ElkArte\sources\subs\MentionType\Mention_Type_Interface $obj
198
	 * @param \Notifications_Task $task
199
	 * @param mixed[] $bodies
200
	 */
201 2
	protected function _send_notification(\ElkArte\sources\subs\MentionType\Mention_Type_Interface $obj, \Notifications_Task $task, $bodies)
202
	{
203 2
		$mentioning = new Mentioning($this->_db, new Data_Validator(), $this->_modSettings->enabled_mentions);
204 2
		foreach ($bodies as $body)
205
		{
206 2
			$mentioning->create($obj, array(
207 2
				'id_member_from' => $task['id_member_from'],
208 2
				'id_member' => $body['id_member_to'],
209 2
				'id_msg' => $task['id_target'],
210 2
				'type' => $task['notification_type'],
211 2
				'log_time' => $task['log_time'],
212 2
				'status' => $task['source_data']['status'],
213 2
			));
214 2
		}
215 2
	}
216
217
	/**
218
	 * Sends an immediate email notification.
219
	 *
220
	 * @param \ElkArte\sources\subs\MentionType\Mention_Type_Interface $obj
221
	 * @param \Notifications_Task $task
222
	 * @param mixed[] $bodies
223
	 */
224
	protected function _send_email(\ElkArte\sources\subs\MentionType\Mention_Type_Interface $obj, \Notifications_Task $task, $bodies)
225
	{
226
		$last_id = $obj->getLastId();
227
		foreach ($bodies as $body)
228
		{
229
			sendmail($body['email_address'], $body['subject'], $body['body'], null, $last_id);
230
		}
231
	}
232
233
	/**
234
	 * Stores data in the database to send a daily digest.
235
	 *
236
	 * @param \ElkArte\sources\subs\MentionType\Mention_Type_Interface $obj
237
	 * @param \Notifications_Task $task
238
	 * @param mixed[] $bodies
239
	 */
240 View Code Duplication
	protected function _send_daily_email(\ElkArte\sources\subs\MentionType\Mention_Type_Interface $obj, \Notifications_Task $task, $bodies)
241
	{
242
		foreach ($bodies as $body)
243
		{
244
			$this->_insert_delayed(array(
245
				$task['notification_type'],
246
				$body['id_member_to'],
247
				$task['log_time'],
248
				'd',
249
				$body['body']
250
			));
251
		}
252
	}
253
254
	/**
255
	 * Stores data in the database to send a weekly digest.
256
	 *
257
	 * @param \ElkArte\sources\subs\MentionType\Mention_Type_Interface $obj
258
	 * @param \Notifications_Task $task
259
	 * @param mixed[] $bodies
260
	 */
261 View Code Duplication
	protected function _send_weekly_email(\ElkArte\sources\subs\MentionType\Mention_Type_Interface $obj, \Notifications_Task $task, $bodies)
262
	{
263
		foreach ($bodies as $body)
264
		{
265
			$this->_insert_delayed(array(
266
				$task['notification_type'],
267
				$body['id_member_to'],
268
				$task['log_time'],
269
				'w',
270
				$body['body']
271
			));
272
		}
273
	}
274
275
	/**
276
	 * Do the insert into the database for daily and weekly digests.
277
	 *
278
	 * @param mixed[] $insert_array
279
	 */
280
	protected function _insert_delayed($insert_array)
281
	{
282
		$this->_db->insert('ignore',
283
			'{db_prefix}pending_notifications',
284
			array(
285
				'notification_type' => 'string-10',
286
				'id_member' => 'int',
287
				'log_time' => 'int',
288
				'frequency' => 'string-1',
289
				'snippet' => 'string',
290
			),
291
			$insert_array,
292
			array()
293
		);
294
	}
295
296
	/**
297
	 * Loads from the database the notification preferences for a certain type
298
	 * of mention for a bunch of members.
299
	 *
300
	 * @param string[] $notification_frequencies
301
	 * @param string $notification_type
302
	 * @param int[] $members
303
	 */
304 2
	protected function _getNotificationPreferences($notification_frequencies, $notification_type, $members)
305
	{
306 2
		$query_members = $members;
307
		// The member 0 is the "default" setting
308 2
		$query_members[] = 0;
309
310 2
		require_once(SUBSDIR . '/Notification.subs.php');
311 2
		$preferences = getUsersNotificationsPreferences($notification_type, $query_members);
312
313 2
		$notification_types = array();
314 2
		foreach ($notification_frequencies as $freq)
315
		{
316 2
			$notification_types[$freq] = array();
317 2
		}
318
319
		// notification_level can be:
320
		//    - 0 => no notification
321
		//    - 1 => only mention
322
		//    - 2 => mention + immediate email
323
		//    - 3 => mention + daily email
324
		//    - 4 => mention + weekly email
325
		//    - 5+ => usable by addons
326 2
		foreach ($members as $member)
327
		{
328 2
			$this_pref = $preferences[$member][$notification_type];
329 2
			if ($this_pref === 0)
330 2
			{
331
				continue;
332
			}
333
334
			// In the following two checks the use of the $this->_notification_frequencies
335
			// is intended, because the numeric id is important and is not preserved
336
			// in the local $notification_frequencies
337 2
			if (isset($this->_notification_frequencies[1]))
338 2
			{
339 2
				$notification_types[$this->_notification_frequencies[1]][] = $member;
340 2
			}
341
342 2
			if ($this_pref > 1)
343 2
			{
344
				if (isset($this->_notification_frequencies[$this_pref]) && isset($notification_types[$this->_notification_frequencies[$this_pref]]))
345
				{
346
					$notification_types[$this->_notification_frequencies[$this_pref]][] = $member;
347
				}
348
			}
349 2
		}
350
351 2
		return $notification_types;
352
	}
353
354
	/**
355
	 * Singleton... until we have something better.
356
	 *
357
	 * @return Notifications
358
	 */
359 3
	public static function instance()
360
	{
361 3
		if (self::$_instance === null)
362 3
		{
363
			self::$_instance = new Notifications(database());
364
		}
365
366 3
		return self::$_instance;
367
	}
368
}
369