Completed
Push — develop ( d8ba64...7d836d )
by Mohamed
08:56
created

SendMessages::getMessageDataForAssignIssue()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 13
ccs 8
cts 8
cp 1
rs 9.4285
cc 2
eloc 8
nc 2
nop 2
crap 2
1
<?php
2
3
/*
4
 * This file is part of the Tinyissue package.
5
 *
6
 * (c) Mohamed Alsharaf <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Tinyissue\Model\Project\Issue;
13
14
use Illuminate\Database\Eloquent\Collection;
15
use Tinyissue\Model\Message;
16
use Tinyissue\Model\Message\Queue;
17
use Tinyissue\Model\Project;
18
use Tinyissue\Model\Project\Issue;
19
use Tinyissue\Model\Tag;
20
use Tinyissue\Model\User;
21
use Tinyissue\Services\SendMessagesAbstract;
22
23
/**
24
 * SendMessages is a class to manage & process of sending messages about issue changes.
25
 *
26
 * @author Mohamed Alsharaf <[email protected]>
27
 */
28
class SendMessages extends SendMessagesAbstract
29
{
30
    protected $template = 'update_issue';
31
32
    /**
33
     * Collection of tags.
34
     *
35
     * @var Collection
36
     */
37
    protected $tags;
38
39
    /**
40
     * Returns an instance of Issue.
41
     *
42
     * @return Issue
43
     */
44 4
    protected function getIssue()
45
    {
46 4
        if (null === $this->issue) {
47 4
            $this->issue = $this->getModel();
48
        }
49
50 4
        return $this->issue;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->issue; (Tinyissue\Model\Project\...Tinyissue\Model\Project) is incompatible with the return type declared by the abstract method Tinyissue\Services\SendMessagesAbstract::getIssue of type Tinyissue\Model\Project\Issue.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
51
    }
52
53
    /**
54
     * Returns an instance of Project.
55
     *
56
     * @return Project
57
     */
58 4
    protected function getProject()
59
    {
60 4
        return $this->getIssue()->project;
61
    }
62
63
    /**
64
     * Returns the project id.
65
     *
66
     * @return int
67
     */
68 4
    protected function getProjectId()
69
    {
70 4
        return $this->getIssue()->project_id;
71
    }
72
73
    /**
74
     * Returns message data belongs to adding an issue.
75
     *
76
     * @param Queue $queue
77
     *
78
     * @return array
79
     */
80 4
    protected function getMessageDataForAddIssue(Queue $queue)
81
    {
82 4
        $messageData                    = ['changes' => []];
83 4
        $messageData['changeByHeading'] = $queue->changeBy->fullname . ' created a new issue';
0 ignored issues
show
Documentation introduced by
The property fullname does not exist on object<Tinyissue\Model\Project\User>. 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...
84 4
        if ($this->issue->assigned) {
85 2
            $messageData['changes']['assignee'] = $this->issue->assigned->fullname;
86
        }
87
88 4
        $tags = $this->issue->tags()->with('parent')->get();
89 4
        foreach ($tags as $tag) {
90 2
            $tagArray                                   = $tag->toShortArray();
91 2
            $tagArray['now']                            = $tagArray['name'];
92 2
            $messageData['changes'][$tag->parent->name] = $tagArray;
93
        }
94
95 4
        if ($this->issue->time_quote && !$this->issue->isQuoteLocked()) {
96
            $messageData['changes']['time_quote'] = \Html::duration($this->issue->time_quote);
97
        }
98
99 4
        return $messageData;
100
    }
101
102
    /**
103
     * Returns message data belongs to updating an issue.
104
     *
105
     * @param Queue $queue
106
     *
107
     * @return array
108
     */
109 1
    protected function getMessageDataForUpdateIssue(Queue $queue)
110
    {
111 1
        $messageData                    = [];
112 1
        $messageData['changeByHeading'] = $queue->changeBy->fullname . ' updated an issue';
0 ignored issues
show
Documentation introduced by
The property fullname does not exist on object<Tinyissue\Model\Project\User>. 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...
113 1
        $whiteListFields                = ['change_by', 'title', 'body', 'assignee', 'status', 'type', 'resolution', 'time_quote'];
114
115 1
        foreach ($queue->payload['dirty'] as $field => $value) {
116
            // Skip fields that not part of the white list or quote is locked
117 1
            if (!in_array($field, $whiteListFields) || ($field == 'time_quote' && $this->issue->isQuoteLocked())) {
118 1
                continue;
119
            }
120
121
            // Format quote to readable time
122 1
            $value                          = $field === 'time_quote' ? \Html::duration($value) : $value;
123 1
            $value                          = $field === 'body' ? \Html::format($value) : $value;
124 1
            $messageData['changes'][$field] = [
125 1
                'now' => $value,
126 1
                'was' => $queue->getDataFromPayload('origin.' . $field),
127
            ];
128
        }
129
130 1
        return $messageData;
131
    }
132
133
    /**
134
     * Returns message data belongs to reopening an issue.
135
     *
136
     * @param Queue $queue
137
     *
138
     * @return array
139
     */
140 1
    protected function getMessageDataForReopenIssue(Queue $queue)
141
    {
142 1
        $messageData                    = [];
143 1
        $messageData['changeByHeading'] = $queue->changeBy->fullname . ' reopened an issue';
0 ignored issues
show
Documentation introduced by
The property fullname does not exist on object<Tinyissue\Model\Project\User>. 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...
144 1
        $statusNow                      = $this->issue
145 1
            ->tags()->with('parent')->get()->where('parent.name', Tag::GROUP_STATUS)->last();
146 1
        $messageData['changes']['status'] = [
147 1
            'was' => trans('tinyissue.closed'),
148 1
            'now' => ($statusNow ? $statusNow->fullname : ''),
149 1
            'id'  => ($statusNow ? $statusNow->id : ''),
150
        ];
151
152 1
        return $messageData;
153
    }
154
155
    /**
156
     * Returns message data belongs to closing an issue.
157
     *
158
     * @param Queue $queue
159
     *
160
     * @return array
161
     */
162 2
    protected function getMessageDataForCloseIssue(Queue $queue)
163
    {
164 2
        $messageData                    = [];
165 2
        $messageData['changeByHeading'] = $queue->changeBy->fullname . ' closed an issue';
0 ignored issues
show
Documentation introduced by
The property fullname does not exist on object<Tinyissue\Model\Project\User>. 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...
166 2
        $statusWas                      = $this->issue
167 2
            ->tags()->with('parent')->get()->where('parent.name', Tag::GROUP_STATUS)->last();
168 2
        $messageData['changes']['status'] = [
169 2
            'was' => ($statusWas ? $statusWas->fullname : ''),
170 2
            'now' => trans('tinyissue.closed'),
171
        ];
172
173 2
        return $messageData;
174
    }
175
176
    /**
177
     * Returns message data belongs to assigning an issue to a user.
178
     *
179
     * @param Queue $queue
180
     * @param array $extraData
181
     *
182
     * @return array
183
     */
184 1
    protected function getMessageDataForAssignIssue(Queue $queue, array $extraData)
185
    {
186 1
        $messageData = [];
187 1
        if (!array_key_exists('now', $extraData)) {
188 1
            $assignTo  = $this->getUserById($queue->getDataFromPayload('dirty.assigned_to'));
189 1
            $extraData = ['now' => $assignTo->fullname];
190
        }
191
192 1
        $messageData['changes']['assignee'] = $extraData;
193 1
        $messageData['changeByHeading']     = $queue->changeBy->fullname . ' assigned an issue to ' . $extraData['now'];
0 ignored issues
show
Documentation introduced by
The property fullname does not exist on object<Tinyissue\Model\Project\User>. 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...
194
195 1
        return $messageData;
196
    }
197
198
    /**
199
     * Returns message data belongs to changing an issue tags.
200
     *
201
     * @param Queue $queue
202
     *
203
     * @return array
204
     */
205 3
    protected function getMessageDataForChangeTagIssue(Queue $queue)
206
    {
207 3
        $messageData                    = [];
208 3
        $messageData['changeByHeading'] = $queue->changeBy->fullname . ' changed an issue tag';
0 ignored issues
show
Documentation introduced by
The property fullname does not exist on object<Tinyissue\Model\Project\User>. 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...
209
210 3
        foreach ($queue->payload['added'] as $tag) {
211 3
            $group                          = strtolower($tag['group']);
212 3
            $messageData['changes'][$group] = [
213 3
                'now'           => $tag['name'],
214 3
                'id'            => $tag['id'],
215 3
                'message_limit' => $tag['message_limit'],
216
            ];
217
        }
218
219 3
        foreach ($queue->payload['removed'] as $tag) {
220 2
            $group                                 = strtolower($tag['group']);
221 2
            $messageData['changes'][$group]['was'] = $tag['name'];
222
        }
223
224 3
        return $messageData;
225
    }
226
227
    /**
228
     * Check that the issue is load.
229
     *
230
     * @return bool
231
     */
232 4
    protected function validateData()
233
    {
234
        // if issue closed and last issue not closed, then something is wrong skip
235 4
        if (!$this->issue->isOpen() && $this->latestMessage->event !== Queue::CLOSE_ISSUE) {
236
            return false;
237
        }
238
239 4
        return $this->issue;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->issue; (Tinyissue\Model\Project\Issue) is incompatible with the return type declared by the abstract method Tinyissue\Services\SendM...sAbstract::validateData of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
240
    }
241
242
    /**
243
     * Populate assigned relation in the current issue.
244
     *
245
     * @return void
246
     */
247 4 View Code Duplication
    protected function populateData()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
248
    {
249 4
        $this->issue->setRelation('assigned', $this->getUserById($this->issue->assigned_to));
250 4
        $creator = $this->getUserById($this->issue->created_by);
251 4
        if ($creator) {
252 4
            $this->issue->setRelation('user', $creator);
253
        }
254 4
        $this->loadIssueCreatorToProjectUsers();
255 4
    }
256
257
    /**
258
     * Check if the latest message is about closing or reopening an issue.
259
     *
260
     * @return bool
261
     */
262 4
    public function isStatusMessage()
263
    {
264 4
        return ((!$this->issue->isOpen() && $this->latestMessage->event === Queue::CLOSE_ISSUE)
265 4
            || ($this->issue->isOpen() && $this->latestMessage->event === Queue::REOPEN_ISSUE));
266
    }
267
268
    /**
269
     * Process assign to user message. Send direct message to new and previous users and full subscribers.
270
     *
271
     * @return void
272
     */
273 4
    protected function processDirectMessages()
274
    {
275
        // Stop if issue is closed
276 4
        if (!$this->getIssue()->isOpen()) {
277 2
            return;
278
        }
279
280
        // Fetch all of the assign issue changes
281 4
        $assignMessages = $this->allMessages->where('event', Queue::ASSIGN_ISSUE);
282
283
        // Skip if no changes
284 4
        if ($assignMessages->isEmpty()) {
285 4
            return;
286
        }
287
288
        // Fetch the latest assignee
289
        /** @var Queue $assignMessage */
290 1
        $assignMessage = $assignMessages->first();
291
292
        // Fetch the user details of the new assignee & previous assignee if this isn't new issue
293 1
        $assigns        = [];
294 1
        $assigns['new'] = (int) $assignMessage->getDataFromPayload('dirty.assigned_to');
295 1
        if (!$this->addMessage) {
296 1
            $previousAssignMessage = $assignMessages->last();
297 1
            $assigns['old']        = (int) $previousAssignMessage->getDataFromPayload('origin.assigned_to');
298
        }
299
300
        // Fetch users objects for old and new assignee
301
        /** @var \Illuminate\Database\Eloquent\Collection $assignObjects */
302 1
        $assignObjects = (new User())->whereIn('id', $assigns)->get();
0 ignored issues
show
Documentation Bug introduced by
The method whereIn does not exist on object<Tinyissue\Model\User>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
303
304
        // If for what ever reason the user does not exists, skip this change
305
        // or if there is only one user and is not matching the new assignee.
306
        // then skip this message
307 1
        if ($assignObjects->count() === 0
308 1
            || ($assignObjects->count() === 1 && (int) $assignObjects->first()->id !== $assigns['new'])
309
        ) {
310
            return;
311
        }
312
313
        // Get the object of the new assignee
314 1
        $assignTo = $assignObjects->where('id', $assigns['new'], false)->first();
315 1
        $users    = collect([$this->createProjectUserObject($assignTo)]);
316
317
        // Exclude the user from any other message for this issue
318 1
        $this->addToExcludeUsers($assignTo);
319
320
        // Data about new and previous assignee
321
        $extraMessageData = [
322 1
            'now' => $assignTo->fullname,
323
        ];
324
325
        // Make sure that the previous assignee was not the same as the new user
326 1
        if (array_key_exists('old', $assigns) && $assigns['old'] > 0 && $assigns['new'] !== $assigns['old']) {
327
            $previousAssign = $assignObjects->where('id', $assigns['old'], false)->first();
328
            if ($previousAssign) {
329
                $extraMessageData['was'] = $previousAssign->fullname;
330
                $users->push($this->createProjectUserObject($previousAssign));
331
            }
332
        }
333
334
        // Get message data needed for the message & send
335 1
        $messageData = $this->getMessageData($assignMessage, $extraMessageData);
336 1
        $this->sendMessages($users, $messageData);
337 1
    }
338
339
    /**
340
     * Create user project object for a user.
341
     *
342
     * @param User $user
343
     *
344
     * @return Project\User
345
     */
346 1
    protected function createProjectUserObject(User $user)
347
    {
348 1
        $userProject = new Project\User([
349 1
            'user_id'    => $user->id,
350 1
            'project_id' => $this->getProjectId(),
351
        ]);
352 1
        $userProject->setRelation('user', $user);
353 1
        $userProject->setRelation('project', $this->getProject());
354
355 1
        return $userProject;
356
    }
357
358
    /**
359
     * Get collection of tags or one by ID.
360
     *
361
     * @param int $tagId
362
     *
363
     * @return Tag
364
     */
365 3
    protected function getTag($tagId)
366
    {
367 3
        if (null === $this->tags) {
368 3
            $this->tags = collect([]);
369
        }
370
371
        // Load & extract tag by ID
372 3
        $tag = $this->tags->where('id', $tagId, false)->first();
373 3
        if (!$tag) {
374 3
            $tag = (new Tag())->find($tagId);
0 ignored issues
show
Documentation Bug introduced by
The method find does not exist on object<Tinyissue\Model\Tag>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
375 3
            $this->tags->push($tag);
376
        }
377
378 3
        return $tag;
379
    }
380
381
    /**
382
     * Whether or not the user wants to receive the message.
383
     *
384
     * @param Project\User $user
385
     * @param array        $data
386
     *
387
     * @return bool
388
     */
389 4
    protected function wantToReceiveMessage(Project\User $user, array $data)
390
    {
391 4
        $status = parent::wantToReceiveMessage($user, $data);
392
393 4
        if (!$status) {
394 4
            return false;
395
        }
396
397
        // Search for tag changes and verify if the user can receive messages
398 4
        foreach ($data['changes'] as $label => $change) {
399
            // Skip other changes that are not related to tag
400 4
            if (!in_array($label, Tag::getCoreGroups())) {
401 4
                continue;
402
            }
403
404
            // If the change was only remove a tag
405 3
            if (!array_key_exists('now', $change) && array_key_exists('was', $change)) {
406
                return true;
407
            }
408
409
            // If tag id not found
410 3
            if (!array_key_exists('id', $change)) {
411 2
                return true;
412
            }
413
414
            // Fetch tag details
415 3
            $tag = $this->getTag($change['id']);
416
417
            // Check if user allowed to receive the message
418 3
            if (!$tag->allowMessagesToUser($user->user)) {
419 3
                return false;
420
            }
421
        }
422
423 4
        return true;
424
    }
425
}
426