Passed
Push — master ( a9f547...49cca7 )
by Raffael
04:18
created

Subscription::postSaveNodeAttributes()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 0
cts 7
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 4
nc 2
nop 5
crap 12
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':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

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.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
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