Completed
Pull Request — master (#32260)
by Sujith
09:42
created

Manager::removeUserNotifications()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Joas Schilling <[email protected]>
4
 * @author Morris Jobke <[email protected]>
5
 *
6
 * @copyright Copyright (c) 2018, ownCloud GmbH
7
 * @license AGPL-3.0
8
 *
9
 * This code is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License, version 3,
11
 * as published by the Free Software Foundation.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License, version 3,
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
20
 *
21
 */
22
23
namespace OC\Notification;
24
25
use OCP\IUser;
26
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
27
use OCP\Notification\IApp;
28
use OCP\Notification\IManager;
29
use OCP\Notification\INotification;
30
use OCP\Notification\INotifier;
31
use OCP\Notification\Exceptions\NotifierIdInUseException;
32
use OCP\Notification\Events\RegisterConsumerEvent;
33
use OCP\Notification\Events\RegisterNotifierEvent;
34
use OC\Notification\Events\RegisterConsumerEventImpl;
35
use OC\Notification\Events\RegisterNotifierEventImpl;
36
use Symfony\Component\EventDispatcher\GenericEvent;
37
38
class Manager implements IManager {
39
	/** @var EventDispatcherInterface */
40
	protected $dispatcher;
41
42
	/** @var IApp[] */
43
	protected $apps;
44
45
	/** @var INotifier */
46
	protected $notifiers;
47
48
	/** @var array[] */
49
	protected $notifiersInfo;
50
51
	/** @var \Closure[] */
52
	protected $appsClosures;
53
54
	/** @var \Closure[] */
55
	protected $notifiersClosures;
56
57
	/** @var \Closure[] */
58
	protected $notifiersInfoClosures;
59
60
	/** @var IApp[] */
61
	protected $builtAppsHolder;
62
63
	/** @var INotifier[] */
64
	protected $builtNotifiersHolder;
65
66
	public function __construct(EventDispatcherInterface $dispatcher) {
67
		$this->dispatcher = $dispatcher;
68
69
		$this->apps = [];
70
		$this->notifiers = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type object<OCP\Notification\INotifier> of property $notifiers.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
71
		$this->notifiersInfo = [];
72
		$this->appsClosures = [];
73
		$this->notifiersClosures = [];
74
		$this->notifiersInfoClosures = [];
75
76
		$this->builtAppsHolder = [];
77
78
		$this->dispatcher->addListener('user.afterdelete', function (GenericEvent $event) {
79
			$this->removeUserNotifications($event->getArgument('uid'));
80
		});
81
	}
82
83
	/**
84
	 * @param \Closure $service The service must implement IApp, otherwise a
85
	 *                          \InvalidArgumentException is thrown later
86
	 * @return null
87
	 * @since 8.2.0
88
	 */
89
	public function registerApp(\Closure $service) {
90
		$this->appsClosures[] = $service;
91
		$this->apps = [];
92
	}
93
94
	/**
95
	 * @param \Closure $service The service must implement INotifier, otherwise a
96
	 *                          \InvalidArgumentException is thrown later
97
	 * @param \Closure $info    An array with the keys 'id' and 'name' containing
98
	 *                          the app id and the app name
99
	 * @return null
100
	 * @since 8.2.0 - Parameter $info was added in 9.0.0
101
	 */
102
	public function registerNotifier(\Closure $service, \Closure $info) {
103
		$this->notifiersClosures[] = $service;
104
		$this->notifiersInfoClosures[] = $info;
105
		$this->notifiers = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type object<OCP\Notification\INotifier> of property $notifiers.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
106
		$this->notifiersInfo = [];
107
	}
108
109
	/**
110
	 * INTERNAL USE ONLY!! This method isn't part of the IManager interface
111
	 * @internal This should only be used by the RegisterConsumerEventImpl (the real implementation).
112
	 * Do NOT use this method outside as it might not work as expected.
113
	 */
114
	public function registerBuiltApp(IApp $app) {
115
		$this->builtAppsHolder[] = $app;
116
	}
117
118
	/**
119
	 * INTERNAL USE ONLY!! This method isn't part of the IManager interface
120
	 * @internal This should only be used by the RegisterNotifierEventImpl (the real implementation).
121
	 * Do NOT use this method outside as it might not work as expected.
122
	 */
123
	public function registerBuiltNotifier(INotifier $notifier, $id, $name) {
124
		if (!isset($this->builtNotifiersHolder[$id]) && !isset($this->notifiersInfo[$id])) {
125
			// we have to check also in the notifiersInfo
126
			$this->builtNotifiersHolder[$id] = [];
127
			$this->builtNotifiersHolder[$id]['notifier'] = $notifier;
128
			$this->builtNotifiersHolder[$id]['name'] = $name;
129
		} else {
130
			throw new NotifierIdInUseException("The given notifier ID $id is already in use");
131
		}
132
	}
133
134
	/**
135
	 * @return IApp[]
136
	 */
137
	protected function getApps() {
138
		if (!empty($this->apps)) {
139
			return $this->apps;
140
		}
141
142
		$this->apps = [];
143
		foreach ($this->appsClosures as $closure) {
144
			$app = $closure();
145
			if (!($app instanceof IApp)) {
146
				throw new \InvalidArgumentException('The given notification app does not implement the IApp interface');
147
			}
148
			$this->apps[] = $app;
149
		}
150
151
		$this->builtAppsHolder = [];
152
		$registerAppEvent = new RegisterConsumerEventImpl($this);
153
		$this->dispatcher->dispatch(RegisterConsumerEvent::NAME, $registerAppEvent);
154
		$this->apps = \array_merge($this->apps, $this->builtAppsHolder);
0 ignored issues
show
Documentation Bug introduced by
It seems like \array_merge($this->apps, $this->builtAppsHolder) of type array is incompatible with the declared type array<integer,object<OCP\Notification\IApp>> of property $apps.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
155
156
		return $this->apps;
157
	}
158
159
	/**
160
	 * @return INotifier[]
161
	 */
162
	protected function getNotifiers() {
163
		if (!empty($this->notifiers)) {
164
			return $this->notifiers;
165
		}
166
167
		$this->notifiers = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type object<OCP\Notification\INotifier> of property $notifiers.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
168
		foreach ($this->notifiersClosures as $closure) {
169
			$notifier = $closure();
170
			if (!($notifier instanceof INotifier)) {
171
				throw new \InvalidArgumentException('The given notifier does not implement the INotifier interface');
172
			}
173
			$this->notifiers[] = $notifier;
174
		}
175
176
		$this->builtNotifiersHolder = [];
177
		$registerNotifierEvent = new RegisterNotifierEventImpl($this);
178
		$this->dispatcher->dispatch(RegisterNotifierEvent::NAME, $registerNotifierEvent);
179
		foreach ($this->builtNotifiersHolder as $notifierData) {
180
			$this->notifiers[] = $notifierData['notifier'];
181
		}
182
183
		return $this->notifiers;
184
	}
185
186
	/**
187
	 * @return array[]
188
	 */
189
	public function listNotifiers() {
190
		if (!empty($this->notifiersInfo)) {
191
			return $this->notifiersInfo;
192
		}
193
194
		$this->notifiersInfo = [];
195
		foreach ($this->notifiersInfoClosures as $closure) {
196
			$notifier = $closure();
197
			if (!\is_array($notifier) || \sizeof($notifier) !== 2 || !isset($notifier['id']) || !isset($notifier['name'])) {
198
				throw new \InvalidArgumentException('The given notifier information is invalid');
199
			}
200
			if (isset($this->notifiersInfo[$notifier['id']])) {
201
				throw new \InvalidArgumentException('The given notifier ID ' . $notifier['id'] . ' is already in use');
202
			}
203
			$this->notifiersInfo[$notifier['id']] = $notifier['name'];
204
		}
205
206
		$this->builtNotifiersHolder = [];
207
		$registerNotifierEvent = new RegisterNotifierEventImpl($this);
208
		$this->dispatcher->dispatch(RegisterNotifierEvent::NAME, $registerNotifierEvent);
209
		foreach ($this->builtNotifiersHolder as $id => $notifierData) {
210
			$this->notifiersInfo[$id] = $notifierData['name'];
211
		}
212
213
		return $this->notifiersInfo;
214
	}
215
216
	/**
217
	 * @return INotification
218
	 * @since 8.2.0
219
	 */
220
	public function createNotification() {
221
		return new Notification();
222
	}
223
224
	/**
225
	 * @return bool
226
	 * @since 8.2.0
227
	 */
228
	public function hasNotifiers() {
229
		return !empty($this->notifiersClosures);
230
	}
231
232
	/**
233
	 * @param INotification $notification
234
	 * @return null
235
	 * @throws \InvalidArgumentException When the notification is not valid
236
	 * @since 8.2.0
237
	 */
238
	public function notify(INotification $notification) {
239
		if (!$notification->isValid()) {
240
			throw new \InvalidArgumentException('The given notification is invalid');
241
		}
242
243
		$apps = $this->getApps();
244
245
		foreach ($apps as $app) {
246
			try {
247
				$app->notify($notification);
248
			} catch (\InvalidArgumentException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
249
			}
250
		}
251
	}
252
253
	/**
254
	 * @param INotification $notification
255
	 * @param string $languageCode The code of the language that should be used to prepare the notification
256
	 * @return INotification
257
	 * @throws \InvalidArgumentException When the notification was not prepared by a notifier
258
	 * @since 8.2.0
259
	 */
260
	public function prepare(INotification $notification, $languageCode) {
261
		$notifiers = $this->getNotifiers();
262
263
		foreach ($notifiers as $notifier) {
0 ignored issues
show
Bug introduced by
The expression $notifiers of type object<OCP\Notification\INotifier>|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
264
			try {
265
				$notification = $notifier->prepare($notification, $languageCode);
266
			} catch (\InvalidArgumentException $e) {
267
				continue;
268
			}
269
270
			if (!($notification instanceof INotification) || !$notification->isValidParsed()) {
271
				throw new \InvalidArgumentException('The given notification has not been handled');
272
			}
273
		}
274
275
		if (!($notification instanceof INotification) || !$notification->isValidParsed()) {
276
			throw new \InvalidArgumentException('The given notification has not been handled');
277
		}
278
279
		return $notification;
280
	}
281
282
	/**
283
	 * @param INotification $notification
284
	 * @return null
285
	 */
286
	public function markProcessed(INotification $notification) {
287
		$apps = $this->getApps();
288
289
		foreach ($apps as $app) {
290
			$app->markProcessed($notification);
291
		}
292
	}
293
294
	/**
295
	 * @param INotification $notification
296
	 * @return int
297
	 */
298
	public function getCount(INotification $notification) {
299
		$apps = $this->getApps();
300
301
		$count = 0;
302
		foreach ($apps as $app) {
303
			$count += $app->getCount($notification);
304
		}
305
306
		return $count;
307
	}
308
309
	public function removeUserNotifications($uid) {
310
		$apps = $this->getApps();
311
312
		foreach ($apps as $app) {
313
			$app->removeUserNotifications($uid);
314
		}
315
	}
316
}
317