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

Project::canView()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 5
cts 5
cp 1
rs 9.2
cc 4
eloc 6
nc 2
nop 1
crap 4
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;
13
14
use Illuminate\Database\Eloquent\Model;
15
use Tinyissue\Model\Project\Note;
16
use Tinyissue\Model\Project\User as ProjectUser;
17
use Tinyissue\Model\User\Activity;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Tinyissue\Model\Activity.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
18
use URL;
19
20
/**
21
 * Project is model class for projects.
22
 *
23
 * @author Mohamed Alsharaf <[email protected]>
24
 *
25
 * @property int $id
26
 * @property string $name
27
 * @property int $status
28
 * @property int $default_assignee
29
 * @property int $private
30
 * @property Project\Issue[] $issues
31
 * @property int $openIssuesCount
32
 * @property int $closedIssuesCount
33
 * @property int $closedIssuesCount
34
 * @property Project\Issue[] $issuesByUser
35
 * @property \Tinyissue\Model\User[] $users
36
 * @property ProjectUser[] $projectUsers
37
 * @property Activity[] $activities
38
 * @property Note[] $notes
39
 * @property Tag[] $kanbanTags
40
 */
41
class Project extends Model
42
{
43
    use Traits\CountAttributeTrait,
44
        Traits\Project\CountTrait,
45
        Traits\Project\FilterTrait,
46
        Traits\Project\SortTrait,
47
        Traits\Project\RelationTrait,
48
        Traits\Project\CrudTrait,
49
        Traits\Project\QueryTrait;
50
51
    /**
52
     * Project private & user role can see their own issues only.
53
     *
54
     * @var int
55
     */
56
    const INTERNAL_YES = 2;
57
58
    /**
59
     * Project not public to view and create issue.
60
     *
61
     * @var int
62
     */
63
    const PRIVATE_YES = 1;
64
65
    /**
66
     * Project public to view and create issue.
67
     *
68
     * @var int
69
     */
70
    const PRIVATE_NO = 0;
71
72
    /**
73
     * All projects.
74
     *
75
     * @var int
76
     */
77
    const PRIVATE_ALL = -1;
78
79
    /**
80
     * Project status Open.
81
     *
82
     * @var int
83
     */
84
    const STATUS_OPEN = 1;
85
86
    /**
87
     * Project status Archived.
88
     *
89
     * @var int
90
     */
91
    const STATUS_ARCHIVED = 0;
92
93
    /**
94
     * Timestamp enabled.
95
     *
96
     * @var bool
97
     */
98
    public $timestamps = true;
99
100
    /**
101
     * Name of database table.
102
     *
103
     * @var string
104
     */
105
    protected $table = 'projects';
106
107
    /**
108
     * List of allowed columns to be used in $this->fill().
109
     *
110
     * @var array
111
     */
112
    protected $fillable = ['name', 'default_assignee', 'status', 'private'];
113
114
    /**
115
     * List of HTML classes for each status.
116
     *
117
     * @var array
118
     */
119
    protected $attrClassNames = [
120
        self::PRIVATE_NO   => 'note',
121
        self::PRIVATE_YES  => 'info',
122
        self::INTERNAL_YES => 'primary',
123
    ];
124
125
    /**
126
     * List of statuses names.
127
     *
128
     * @var array
129
     */
130
    protected $statusesNames = [
131
        self::PRIVATE_NO   => 'public',
132
        self::PRIVATE_YES  => 'private',
133
        self::INTERNAL_YES => 'internal',
134
    ];
135
136
    /**
137
     * Generate a URL for the active project.
138
     *
139
     * @param string $url
140
     *
141
     * @return string
142
     */
143 45
    public function to($url = '')
144
    {
145 45
        return URL::to('project/' . $this->id . (($url) ? '/' . $url : ''));
146
    }
147
148
    /**
149
     * Returns the aggregate value of number of open issues in the project.
150
     *
151
     * @return int
152
     */
153 5
    public function getOpenIssuesCountAttribute()
154
    {
155 5
        return $this->getCountAttribute('openIssuesCount');
156
    }
157
158
    /**
159
     * Returns the aggregate value of number of closed issues in the project.
160
     *
161
     * @return int
162
     */
163 1
    public function getClosedIssuesCountAttribute()
164
    {
165 1
        return $this->getCountAttribute('closedIssuesCount');
166
    }
167
168
    /**
169
     * Set default assignee attribute.
170
     *
171
     * @param int $value
172
     *
173
     * @return $this
174
     */
175 54
    public function setDefaultAssigneeAttribute($value)
176
    {
177 54
        if (!empty($value)) {
178 35
            $this->attributes['default_assignee'] = (int) $value;
179
        }
180
181 54
        return $this;
182
    }
183
184
    /**
185
     * Returns the aggregate value of number of issues in the project.
186
     *
187
     * @return int
188
     */
189
    public function getIssuesCountAttribute()
190
    {
191
        return $this->getCountAttribute('issuesCount');
192
    }
193
194
    /**
195
     * Get total issues total quote time.
196
     *
197
     * @return int
198
     */
199 3
    public function getTotalQuote()
200
    {
201 3
        $total = 0;
202 3
        foreach ($this->issues as $issue) {
203 3
            $total += $issue->time_quote;
204
        }
205
206 3
        return $total;
207
    }
208
209
    /**
210
     * Calculate the progress (open & closed issues).
211
     *
212
     * @return float|int
213
     */
214 1
    public function getProgress()
215
    {
216 1
        $total    = $this->openIssuesCount + $this->closedIssuesCount;
217 1
        $progress = 100;
218 1
        if ($total > 0) {
219 1
            $progress = (float) ($this->closedIssuesCount / $total) * 100;
220
        }
221 1
        $progressInt = (int) $progress;
222 1
        if ($progressInt > 0) {
223 1
            $progress = number_format($progress, 2);
224 1
            $fraction = $progress - $progressInt;
225 1
            if ($fraction === 0.0) {
226 1
                $progress = $progressInt;
227
            }
228
        }
229
230 1
        return $progress;
231
    }
232
233
    /**
234
     * Whether or not a user is member of the project.
235
     *
236
     * @param int $userId
237
     *
238
     * @return bool
239
     */
240 10
    public function isMember($userId)
241
    {
242 10
        return $this->user($userId)->count() > 0;
243
    }
244
245
    /**
246
     * Whether or not the project is private.
247
     *
248
     * @return bool
249
     */
250 2
    public function isPrivate()
251
    {
252 2
        return (int) $this->private === self::PRIVATE_YES;
253
    }
254
255
    /**
256
     * Whether or not the project is public.
257
     *
258
     * @return bool
259
     */
260 34
    public function isPublic()
261
    {
262 34
        return (int) $this->private === self::PRIVATE_NO;
263
    }
264
265
    /**
266
     * Whether or not the project is private internal.
267
     *
268
     * @return bool
269
     */
270 29
    public function isPrivateInternal()
271
    {
272 29
        return (int) $this->private === self::INTERNAL_YES;
273
    }
274
275
    /**
276
     * Returns project status as string name.
277
     *
278
     * @return string
279
     */
280 2
    public function getStatusAsName()
281
    {
282 2
        if (array_key_exists((int) $this->private, $this->statusesNames)) {
283 2
            return $this->statusesNames[(int) $this->private];
284
        }
285
286
        return '';
287
    }
288
289
    /**
290
     * Returns the class name to be used for project status.
291
     *
292
     * @return string
293
     */
294 2
    public function getStatusClass()
295
    {
296 2
        if (array_key_exists((int) $this->private, $this->attrClassNames)) {
297 2
            return $this->attrClassNames[(int) $this->private];
298
        }
299
300
        return '';
301
    }
302
303
    /**
304
     * Whether or not a user can access the project.
305
     *
306
     * @param User $user
307
     *
308
     * @return bool
309
     */
310 3
    public function canView(User $user)
311
    {
312
        // Is member of the project
313
        if (
314 3
            ($this->isPublic() && app('tinyissue.settings')->isPublicProjectsEnabled()) ||
315 3
            $this->isMember($user->id)
316
        ) {
317 1
            return true;
318
        }
319
320 3
        return false;
321
    }
322
323
    /**
324
     * Whether a user can edit the project.
325
     *
326
     * @param User $user
327
     *
328
     * @return bool
329
     */
330
    public function canEdit(User $user)
331
    {
332
        return $user->permission(Permission::PERM_PROJECT_MODIFY) || $user->permission(Permission::PERM_PROJECT_ALL);
333
    }
334
}
335