|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
declare(strict_types=1); |
|
4
|
|
|
|
|
5
|
|
|
/** |
|
6
|
|
|
* balloon |
|
7
|
|
|
* |
|
8
|
|
|
* @copyright Copryright (c) 2012-2018 gyselroth GmbH (https://gyselroth.com) |
|
9
|
|
|
* @license GPL-3.0 https://opensource.org/licenses/GPL-3.0 |
|
10
|
|
|
*/ |
|
11
|
|
|
|
|
12
|
|
|
namespace Balloon\App\Notification\Hook; |
|
13
|
|
|
|
|
14
|
|
|
use Balloon\App\Notification\Notifier; |
|
15
|
|
|
use Balloon\Filesystem\Node\Collection; |
|
16
|
|
|
use Balloon\Filesystem\Node\File; |
|
17
|
|
|
use Balloon\Filesystem\Node\NodeInterface; |
|
18
|
|
|
use Balloon\Hook\AbstractHook; |
|
19
|
|
|
use Balloon\Server; |
|
20
|
|
|
use Balloon\Server\User; |
|
21
|
|
|
use InvalidArgumentException; |
|
22
|
|
|
use Psr\Log\LoggerInterface; |
|
23
|
|
|
|
|
24
|
|
|
class Subscription extends AbstractHook |
|
25
|
|
|
{ |
|
26
|
|
|
/** |
|
27
|
|
|
* Notification throttle. |
|
28
|
|
|
* |
|
29
|
|
|
* @var int |
|
30
|
|
|
*/ |
|
31
|
|
|
protected $notification_throttle = 120; |
|
32
|
|
|
|
|
33
|
|
|
/** |
|
34
|
|
|
* Notifier. |
|
35
|
|
|
* |
|
36
|
|
|
* @var Notifier |
|
37
|
|
|
*/ |
|
38
|
|
|
protected $notifier; |
|
39
|
|
|
|
|
40
|
|
|
/** |
|
41
|
|
|
* Server. |
|
42
|
|
|
* |
|
43
|
|
|
* @var Server |
|
44
|
|
|
*/ |
|
45
|
|
|
protected $server; |
|
46
|
|
|
|
|
47
|
|
|
/** |
|
48
|
|
|
* Logger. |
|
49
|
|
|
* |
|
50
|
|
|
* @var LoggerInterface |
|
51
|
|
|
*/ |
|
52
|
|
|
protected $logger; |
|
53
|
|
|
|
|
54
|
|
|
/** |
|
55
|
|
|
* Constructor. |
|
56
|
|
|
* |
|
57
|
|
|
* @param Notification $notifier |
|
58
|
|
|
*/ |
|
59
|
|
|
public function __construct(Notifier $notifier, Server $server, LoggerInterface $logger, ?Iterable $config = null) |
|
60
|
|
|
{ |
|
61
|
|
|
$this->notifier = $notifier; |
|
62
|
|
|
$this->server = $server; |
|
63
|
|
|
$this->setOptions($config); |
|
64
|
|
|
$this->logger = $logger; |
|
65
|
|
|
} |
|
66
|
|
|
|
|
67
|
|
|
/** |
|
68
|
|
|
* Set config. |
|
69
|
|
|
* |
|
70
|
|
|
* @param iterable $config |
|
71
|
|
|
* |
|
72
|
|
|
* @return Subscription |
|
73
|
|
|
*/ |
|
74
|
|
|
public function setOptions(?Iterable $config = null): self |
|
75
|
|
|
{ |
|
76
|
|
|
if (null === $config) { |
|
77
|
|
|
return $this; |
|
78
|
|
|
} |
|
79
|
|
|
|
|
80
|
|
|
foreach ($config as $option => $value) { |
|
81
|
|
|
switch ($option) { |
|
82
|
|
|
case 'notification_throttle': |
|
|
|
|
|
|
83
|
|
|
$this->{$option} = (int) $value; |
|
84
|
|
|
|
|
85
|
|
|
break; |
|
86
|
|
|
default: |
|
87
|
|
|
throw new InvalidArgumentException('invalid option '.$option.' given'); |
|
88
|
|
|
} |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
return $this; |
|
92
|
|
|
} |
|
93
|
|
|
|
|
94
|
|
|
/** |
|
95
|
|
|
* {@inheritdoc} |
|
96
|
|
|
*/ |
|
97
|
|
|
public function postCreateCollection(Collection $parent, Collection $node, bool $clone): void |
|
98
|
|
|
{ |
|
99
|
|
|
$this->notify($node); |
|
100
|
|
|
$subscription = $this->notifier->getSubscription($parent, $this->server->getIdentity()); |
|
101
|
|
|
|
|
102
|
|
|
if ($subscription !== null && $subscription['recursive'] === true) { |
|
103
|
|
|
$this->notifier->subscribeNode($node, true, $subscription['exclude_me'], $subscription['recursive']); |
|
104
|
|
|
} |
|
105
|
|
|
} |
|
106
|
|
|
|
|
107
|
|
|
/** |
|
108
|
|
|
* {@inheritdoc} |
|
109
|
|
|
*/ |
|
110
|
|
|
public function postCreateFile(Collection $parent, File $node, bool $clone): void |
|
111
|
|
|
{ |
|
112
|
|
|
$this->notify($node); |
|
113
|
|
|
} |
|
114
|
|
|
|
|
115
|
|
|
/** |
|
116
|
|
|
* {@inheritdoc} |
|
117
|
|
|
*/ |
|
118
|
|
|
public function postDeleteCollection(Collection $node, bool $force, ?string $recursion, bool $recursion_first): void |
|
119
|
|
|
{ |
|
120
|
|
|
$this->notify($node); |
|
121
|
|
|
} |
|
122
|
|
|
|
|
123
|
|
|
/** |
|
124
|
|
|
* {@inheritdoc} |
|
125
|
|
|
*/ |
|
126
|
|
|
public function postRestoreFile(File $node, int $version): void |
|
127
|
|
|
{ |
|
128
|
|
|
$this->notify($node); |
|
129
|
|
|
} |
|
130
|
|
|
|
|
131
|
|
|
/** |
|
132
|
|
|
* {@inheritdoc} |
|
133
|
|
|
*/ |
|
134
|
|
|
public function postDeleteFile(File $node, bool $force, ?string $recursion, bool $recursion_first): void |
|
135
|
|
|
{ |
|
136
|
|
|
$this->notify($node); |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
/** |
|
140
|
|
|
* {@inheritdoc} |
|
141
|
|
|
*/ |
|
142
|
|
|
public function postPutFile(File $node, $content, bool $force, array $attributes): void |
|
143
|
|
|
{ |
|
144
|
|
|
$this->notify($node); |
|
145
|
|
|
} |
|
146
|
|
|
|
|
147
|
|
|
/** |
|
148
|
|
|
* {@inheritdoc} |
|
149
|
|
|
*/ |
|
150
|
|
|
public function postSaveNodeAttributes(NodeInterface $node, array $attributes, array $remove, ?string $recursion, bool $recursion_first): void |
|
151
|
|
|
{ |
|
152
|
|
|
$raw = $node->getRawAttributes(); |
|
153
|
|
|
if (in_array('parent', $attributes, true) && $raw['parent'] !== $node->getAttributes()['parent']) { |
|
154
|
|
|
$this->notify($node); |
|
155
|
|
|
} |
|
156
|
|
|
} |
|
157
|
|
|
|
|
158
|
|
|
/** |
|
159
|
|
|
* Check if we need to notify. |
|
160
|
|
|
*/ |
|
161
|
|
|
protected function notify(NodeInterface $node): bool |
|
162
|
|
|
{ |
|
163
|
|
|
$receiver = $this->getReceiver($node); |
|
164
|
|
|
$parent = $node->getParent(); |
|
165
|
|
|
if ($parent !== null) { |
|
166
|
|
|
$parents = $this->getReceiver($parent); |
|
167
|
|
|
$receiver = array_merge($parents, $receiver); |
|
168
|
|
|
} |
|
169
|
|
|
|
|
170
|
|
|
if (empty($receiver)) { |
|
171
|
|
|
$this->logger->debug('skip subscription notification for node ['.$node->getId().'] due empty receiver list', [ |
|
172
|
|
|
'category' => get_class($this), |
|
173
|
|
|
]); |
|
174
|
|
|
|
|
175
|
|
|
return false; |
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
$this->notifier->throttleSubscriptions($node, $receiver); |
|
179
|
|
|
$receiver = $this->server->getUsers(['_id' => ['$in' => $receiver]]); |
|
180
|
|
|
$message = $this->notifier->nodeMessage('subscription', $node); |
|
181
|
|
|
|
|
182
|
|
|
return $this->notifier->notify($receiver, $this->server->getIdentity(), $message); |
|
183
|
|
|
} |
|
184
|
|
|
|
|
185
|
|
|
/** |
|
186
|
|
|
* Get receiver list. |
|
187
|
|
|
*/ |
|
188
|
|
|
protected function getReceiver(NodeInterface $node): array |
|
189
|
|
|
{ |
|
190
|
|
|
$subs = $this->notifier->getSubscriptions($node); |
|
191
|
|
|
|
|
192
|
|
|
$receiver = []; |
|
193
|
|
|
$user_id = null; |
|
194
|
|
|
if ($this->server->getIdentity() instanceof User) { |
|
195
|
|
|
$user_id = $this->server->getIdentity()->getId(); |
|
196
|
|
|
} |
|
197
|
|
|
|
|
198
|
|
|
foreach ($subs as $key => $subscription) { |
|
199
|
|
|
if (isset($subscription['last_notification']) && ($subscription['last_notification']->toDateTime()->format('U') + $this->notification_throttle) > time()) { |
|
200
|
|
|
$this->logger->debug('skip message for user ['.$subscription['user'].'], message within throttle time range of ['.$this->notification_throttle.'s]', [ |
|
201
|
|
|
'category' => get_class($this), |
|
202
|
|
|
]); |
|
203
|
|
|
} elseif ($subscription['user'] == $user_id && $subscription['exclude_me'] === true) { |
|
204
|
|
|
$this->logger->debug('skip message for user ['.$user_id.'], user excludes own actions in node ['.$node->getId().']', [ |
|
205
|
|
|
'category' => get_class($this), |
|
206
|
|
|
]); |
|
207
|
|
|
} else { |
|
208
|
|
|
$receiver[] = $subscription['user']; |
|
209
|
|
|
} |
|
210
|
|
|
} |
|
211
|
|
|
|
|
212
|
|
|
$this->notifier->throttleSubscriptions($node, $receiver); |
|
213
|
|
|
|
|
214
|
|
|
return $receiver; |
|
215
|
|
|
} |
|
216
|
|
|
} |
|
217
|
|
|
|
As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next
break.There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.