Completed
Branch develop-3.0 (b19f08)
by Mohamed
03:01
created

Updater::deleteIssue()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
cc 2
eloc 16
nc 2
nop 0
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\Repository\Project\Issue;
13
14
use Illuminate\Support\Collection;
15
use Tinyissue\Model\Activity;
16
use Tinyissue\Model\Message\Queue;
17
use Tinyissue\Model\Project;
18
use Tinyissue\Model\Tag;
19
use Tinyissue\Model\User;
20
use Tinyissue\Repository\RepositoryUpdater;
21
22
class Updater extends RepositoryUpdater
23
{
24
    /**
25
     * @var Project\Issue
26
     */
27
    protected $model;
28
29
    public function __construct(Project\Issue $model)
30
    {
31
        $this->model = $model;
32
    }
33
34
    /**
35
     * Set the issue is updated by a user.
36
     *
37
     * @return Project\Issue
38
     */
39
    public function changeUpdatedBy()
40
    {
41
        $this->model->updated_by = $this->user->id;
42
        $this->model->touch();
43
44
        return $this->save();
45
    }
46
47
    /**
48
     * Reassign the issue to a new user.
49
     *
50
     * @param int|User $assignTo
51
     * @param User     $user
52
     *
53
     * @return Project\Issue
54
     */
55
    public function reassign($assignTo, User $user)
56
    {
57
        $this->setUser($user);
58
        $assignToId = !$assignTo instanceof User ? $assignTo : $assignTo->id;
59
        $this->model->assigned_to = $assignToId;
60
61
        // Add event on successful save
62
        Project\Issue::saved(function (Project\Issue $issue) {
63
            $this->queueAssign($issue, $this->user);
64
        });
65
66
        $this->save();
67
68
        $this->saveToActivities([
69
            'type_id'   => Activity::TYPE_REASSIGN_ISSUE,
70
            'parent_id' => $this->model->project->id,
71
            'user_id'   => $user->id,
72
            'action_id' => $this->model->assigned_to,
73
        ]);
74
75
        return $this->model;
76
    }
77
78
    protected function filterUpdateAttributes(array $input)
79
    {
80
        $fill = array_only($input, ['title', 'body', 'assigned_to']);
81
        $fill['updated_by'] = $this->model->updatedBy->id;
82
        $fill['lock_quote'] = (bool)isset($input['time_quote']['lock']);
83
84
        // Only save quote if not locked or locked & user allowed to modify it
85
        if (array_key_exists('time_quote', $input) &&
86
            (!$this->model->isQuoteLocked() || $this->getLoggedUser()->can('lockQuote', $this->model))
87
        ) {
88
            $fill['time_quote'] = $input['time_quote'];
89
        }
90
91
        return $fill;
92
    }
93
94
    /**
95
     * Update the given issue.
96
     *
97
     * @param array $input
98
     *
99
     * @return Project\Issue
100
     */
101
    public function update(array $input = [])
102
    {
103
        $this->model->fill($this->filterUpdateAttributes($input);
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected ';', expecting ',' or ')'
Loading history...
104
105
        /* Add to activity log for assignment if changed */
106
        if($this->model->isDirty('assigned_to') && $this->model->assigned_to > 0) {
107
            $this->saveToActivities([
108
                'type_id'   => Activity::TYPE_REASSIGN_ISSUE,
109
                'parent_id' => $this->model->project->id,
110
                'user_id'   => $this->model->updatedBy->id,
111
                'action_id' => $this->model->assigned_to,
112
            ]);
113
        }
114
115
        $this->syncTags($input, $this->model->tags()->with('parent')->get());
116
117
        // Add event on successful save
118
        Project\Issue::saved(function (Project\Issue $issue) {
119
            $this->queueUpdate($issue, $this->user);
120
        });
121
122
        return $this->save();
123
    }
124
125
    /**
126
     * Create a new issue.
127
     *
128
     * @param array $input
129
     *
130
     * @return Project\Issue
131
     */
132
    public function create(array $input)
133
    {
134
        if (array_key_exists('project_id', $input)) {
135
            $this->model->setRelation('project', Project::find((int)$input['project_id']));
136
        }
137
138
        $fill = [
139
            'created_by'  => $this->model->user->id,
140
            'project_id'  => $this->model->project->id,
141
            'title'       => $input['title'],
142
            'body'        => $input['body'],
143
            'assigned_to' => (int)$this->model->project->default_assignee,
144
        ];
145
146
        if ($this->model->user->isDeveloperOrMore()) {
147
            $fill['assigned_to'] = array_get($input, 'assigned_to', $fill['assigned_to']);
148
            $fill['time_quote'] = array_get($input, 'time_quote');
149
        }
150
151
        $this->model->fill($fill)->save();
152
153
        // Add issue to messages queue
154
        $this->queueAdd($this->model, $this->user);
155
156
        /* Add to user's activity log */
157
        $this->saveToActivities([
158
            'type_id'   => Activity::TYPE_CREATE_ISSUE,
159
            'parent_id' => $this->model->project->id,
160
            'user_id'   => $this->model->user->id,
161
        ]);
162
163
        /* Add attachments to issue */
164
        Project\Issue\Attachment::instance()->updater()->updateIssueToken($input['upload_token'], $this->model->user->id, $this->model->id);
165
166
        // Add default tag to newly created issue
167
        $defaultTag = app('tinyissue.settings')->getFirstStatusTagId();
168
        if ($defaultTag > 0 && empty($input['tag_status'])) {
169
            $input['tag_status'] = $defaultTag;
170
        }
171
172
        $this->syncTags($input);
173
174
        return $this->model;
175
    }
176
177
    /**
178
     * Move the issue (comments & activities) to another project.
179
     *
180
     * @param int $projectId
181
     *
182
     * @return Project\Issue
183
     */
184
    public function changeProject($projectId)
185
    {
186
        $this->model->project_id = $projectId;
187
        $this->save();
188
        $comments = $this->model->comments()->get();
189
        foreach ($comments as $comment) {
190
            $comment->project_id = $projectId;
191
            $comment->save();
192
        }
193
194
        $activities = $this->model->activities()->get();
195
        foreach ($activities as $activity) {
196
            $activity->parent_id = $projectId;
197
            $activity->save();
198
        }
199
200
        return $this->model;
201
    }
202
203
    /**
204
     * Delete an issue.
205
     *
206
     * @return bool
207
     *
208
     * @throws \Exception
209
     */
210
    public function delete()
211
    {
212
        return $this->transaction('deleteIssue');
213
    }
214
215
    protected function deleteIssue()
216
    {
217
        $id = $this->model->id;
218
        $projectId = $this->model->project_id;
219
        $comments = $this->model->comments;
220
        $attachments = $this->model->attachments;
221
222
        $status = $this->model->delete();
223
224
        if ($status) {
225
            $attachments->each(function (Project\Issue\Attachment $attachment) use ($projectId) {
226
                $path = $this->getUploadStorage($projectId, $attachment->upload_token);
227
                $attachment->updater()->deleteFile($path, $attachment->filename);
228
                $attachment->updater()->delete();
229
            });
230
            $comments->each(function (Project\Issue\Comment $comment) {
231
                $comment->updater($this->getLoggedUser())->delete();
232
            });
233
            User\Activity::where('parent_id', '=', $projectId)->where('item_id', '=', $id)->delete();
234
            \DB::table('projects_issues_tags')->where('issue_id', '=', $id)->delete();
235
        }
236
237
        return $status;
238
    }
239
240
    /**
241
     * Change the status of an issue.
242
     *
243
     * @param int  $status
244
     * @param User $user
245
     *
246
     * @return Project\Issue
247
     */
248
    public function changeStatus($status, User $user)
249
    {
250
        if ($status == 0) {
251
            $this->model->closed_by = $user->id;
252
            $this->model->closed_at = (new \DateTime())->format('Y-m-d H:i:s');
253
            $activityType = Activity::TYPE_CLOSE_ISSUE;
254
        } else {
255
            $this->model->closed_by = 0;
256
            $this->model->closed_at = null;
257
            $activityType = Activity::TYPE_REOPEN_ISSUE;
258
        }
259
260
        /* Add to activity log */
261
        $this->saveToActivities([
262
            'type_id'   => $activityType,
263
            'parent_id' => $this->model->project->id,
264
            'user_id'   => $user->id,
265
        ]);
266
267
        $this->model->status = $status;
268
269
        // Add event on successful save
270
        Project\Issue::saved(function (Project\Issue $issue) {
271
            $this->queueUpdate($issue, $this->user);
272
        });
273
274
        return $this->save();
275
    }
276
277
    /**
278
     * Sync the issue tags.
279
     *
280
     * @param array      $input
281
     * @param Collection $currentTags
282
     *
283
     * @return bool
284
     */
285
    public function syncTags(array $input, Collection $currentTags = null)
286
    {
287
        $tagIds = array_only($input, [
288
            'tag_type', 'tag_status', 'tag_resolution',
289
        ]);
290
        $currentTags = is_null($currentTags) ? Collection::make([]) : $currentTags;
291
292
        // User can edit their own role and can only change issue type
293
        if ($this->model->updatedBy instanceof User && $this->model->updatedBy->isUser()) {
294
            $currentTagIds = $currentTags->pluck('id', 'parent.name')->toArray();
295
            $tagIds['tag_status'] = array_key_exists('status', $currentTagIds) ? $currentTagIds['status'] : 0;
296
            $tagIds['tag_resolution'] = array_key_exists('resolution', $currentTagIds) ? $currentTagIds['resolution'] : 0;
297
        }
298
299
        $tags = (new Tag())->whereIn('id', $tagIds)->get();
300
301
        $removedTags = [];
302
        if (null === $currentTags) {
303
            // Add the following tags except for open status
304
            $addedTags = $tags
305
                ->map(function (Tag $tag) {
306
                    return $tag->toShortArray();
307
                })
308
                ->toArray();
309
        } else {
310
            // Tags remove from the issue
311
            $removedTags = $currentTags
312
                ->diff($tags)
313
                ->map(function (Tag $tag) {
314
                    return $tag->toShortArray();
315
                })
316
                ->toArray();
317
318
            // Check if we are adding new tags
319
            $addedTags = $tags
320
                ->filter(function (Tag $tag) use ($currentTags) {
321
                    return $currentTags->where('id', $tag->id)->count() === 0;
322
                })
323
                ->map(function (Tag $tag) {
324
                    return $tag->toShortArray();
325
                })
326
                ->toArray();
327
328
            // No new tags to add or remove
329
            if (empty($removedTags) && empty($addedTags)) {
330
                return true;
331
            }
332
        }
333
334
        // Save relation
335
        $this->model->tags()->sync($tags->pluck('id')->all());
336
337
        // Activity is added when new issue create with tags or updated with tags excluding the open status tag
338
        if (!empty($removedTags) || !empty($addedTags)) {
339
            // Add this change to messages queue
340
            $this->queueChangeTags($this->model, $addedTags, $removedTags, $this->user);
341
342
            // Add to activity log for tags if changed
343
            $this->saveToActivities([
344
                'type_id'   => Activity::TYPE_ISSUE_TAG,
345
                'parent_id' => $this->model->project->id,
346
                'user_id'   => $this->model->user->id,
347
                'data'      => ['added_tags' => $addedTags, 'removed_tags' => $removedTags],
348
            ]);
349
        }
350
351
        return true;
352
    }
353
354
    /**
355
     * Add tag to the issue & close issue if added tag is Closed.
356
     *
357
     * @param Tag $newTag
358
     * @param Tag $oldTag
359
     *
360
     * @return Project\Issue
361
     */
362
    public function changeKanbanTag(Tag $newTag, Tag $oldTag)
363
    {
364
        //  skip if there is no change in status tags
365
        if ($oldTag->name === $newTag->name) {
366
            return $this->model;
367
        }
368
369
        // Open issue
370
        $data = ['added_tags' => [], 'removed_tags' => []];
371
372
        // Remove previous status tag
373
        $this->model->tags()->detach($oldTag);
374
        $data['removed_tags'][] = $oldTag->toShortArray();
375
376
        // Add new tag
377
        if (!$this->model->tags->contains($newTag)) {
378
            $this->model->tags()->attach($newTag);
379
380
            $data['added_tags'][] = $newTag->toShortArray();
381
        }
382
383
        if (!empty($data)) {
384
            // Add this change to messages queue
385
            $this->queueChangeTags($this->model, $data['added_tags'], $data['removed_tags'], $this->user);
386
387
            // Add to activity log for tags if changed
388
            $this->saveToActivities([
389
                'type_id'   => Activity::TYPE_ISSUE_TAG,
390
                'parent_id' => $this->model->project->id,
391
                'user_id'   => $this->model->user->id,
392
                'data'      => $data,
393
            ]);
394
        }
395
396
        return $this->model;
397
    }
398
399
    /**
400
     * Insert update issue to message queue.
401
     *
402
     * @param Project\Issue $issue
403
     * @param User          $changeBy
404
     *
405
     * @return void
406
     */
407
    public function queueUpdate(Project\Issue $issue, User $changeBy)
408
    {
409
        // is Closed?
410
        $this->queueClosedIssue($issue, $changeBy);
411
412
        // is Reopened?
413
        $this->queueReopenedIssue($issue, $changeBy);
414
415
        // If the assignee has changed and it is not the logged in user who made the action
416
        $noMessageForMe = $this->queueAssign($issue, $changeBy);
417
418
        // If the update was just for assigning user, then skip update issue
419
        $this->queueUpdateIssue($issue, $changeBy, $noMessageForMe);
420
    }
421
422
    /**
423
     * @param Project\Issue $issue
424
     * @param User          $changeBy
425
     * @param bool|int      $noMessageForMe
426
     */
427
    protected function queueUpdateIssue(Project\Issue $issue, User $changeBy, $noMessageForMe = false)
428
    {
429
        // Number of changed attributes
430
        $countChanges = count($issue->getDirty());
431
432
        if (!($countChanges === 1 && $noMessageForMe !== false)) {
433
            return (new Queue())->updater($changeBy)->queue(Queue::UPDATE_ISSUE, $issue, $changeBy);
434
        }
435
    }
436
437
    /**
438
     * @param Project\Issue $issue
439
     * @param User          $changeBy
440
     */
441
    protected function queueClosedIssue(Project\Issue $issue, User $changeBy)
442
    {
443
        if (!$issue->isOpen()) {
444
            (new Queue())->updater($changeBy)->queue(Queue::CLOSE_ISSUE, $issue, $changeBy);
445
        }
446
    }
447
448
    /**
449
     * @param Project\Issue $issue
450
     * @param User          $changeBy
451
     */
452
    protected function queueReopenedIssue(Project\Issue $issue, User $changeBy)
453
    {
454
        if ((int)$issue->getOriginal('status') === Project\Issue::STATUS_CLOSED) {
455
            (new Queue())->updater($changeBy)->queue(Queue::REOPEN_ISSUE, $issue, $changeBy);
456
        }
457
    }
458
459
    /**
460
     * Insert add issue to message queue.
461
     *
462
     * @param Project\Issue $issue
463
     * @param User          $changeBy
464
     *
465
     * @return void
466
     */
467
    public function queueAdd(Project\Issue $issue, User $changeBy)
468
    {
469
        return (new Queue())->updater($changeBy)->queue(Queue::ADD_ISSUE, $issue, $changeBy);
470
    }
471
472
    /**
473
     * Insert assign issue to message queue.
474
     *
475
     * @param Project\Issue $issue
476
     * @param User          $changeBy
477
     *
478
     * @return bool|int
479
     */
480
    public function queueAssign(Project\Issue $issue, User $changeBy)
481
    {
482
        // Whether or not the assignee has changed
483
        $return = false;
484
485
        // If the assignee has changed and it is not the logged in user who made the action
486
        if ($issue->assigned_to > 0 && $changeBy->id !== $issue->assigned_to && $issue->assigned_to !== $issue->getOriginal('assigned_to', $issue->assigned_to)) {
487
            (new Queue())->updater($changeBy)->queue(Queue::ASSIGN_ISSUE, $issue, $changeBy);
488
489
            $return = $issue->assigned_to;
490
        }
491
492
        return $return;
493
    }
494
495
    /**
496
     * Insert issue tag changes to message queue.
497
     *
498
     * @param Project\Issue $issue
499
     * @param array         $addedTags
500
     * @param array         $removedTags
501
     * @param User          $changeBy
502
     *
503
     * @return mixed
504
     */
505
    public function queueChangeTags(Project\Issue $issue, array $addedTags, array $removedTags, User $changeBy)
506
    {
507
        return (new Queue())->updater($changeBy)->queueIssueTagChanges($issue, $addedTags, $removedTags, $changeBy);
508
    }
509
}
510