Passed
Push — master ( c92e71...89beee )
by Jonathan
25:53
created

User::canSeeDescendantsRecords()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 10
c 0
b 0
f 0
nc 4
nop 1
dl 0
loc 17
rs 9.9332
1
<?php
2
3
namespace Uccello\Core\Models;
4
5
use Illuminate\Notifications\Notifiable;
0 ignored issues
show
Bug introduced by
The type Illuminate\Notifications\Notifiable was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use Illuminate\Foundation\Auth\User as Authenticatable;
0 ignored issues
show
Bug introduced by
The type Illuminate\Foundation\Auth\User was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7
use Illuminate\Database\Eloquent\SoftDeletes;
0 ignored issues
show
Bug introduced by
The type Illuminate\Database\Eloquent\SoftDeletes was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Illuminate\Support\Collection;
0 ignored issues
show
Bug introduced by
The type Illuminate\Support\Collection was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use Illuminate\Support\Facades\Cache;
0 ignored issues
show
Bug introduced by
The type Illuminate\Support\Facades\Cache was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Spatie\Searchable\Searchable;
0 ignored issues
show
Bug introduced by
The type Spatie\Searchable\Searchable was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use Spatie\Searchable\SearchResult;
0 ignored issues
show
Bug introduced by
The type Spatie\Searchable\SearchResult was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use Uccello\Core\Support\Traits\RelatedlistTrait;
13
14
class User extends Authenticatable implements Searchable
15
{
16
    use SoftDeletes;
17
    use Notifiable;
18
    use RelatedlistTrait;
19
20
    /**
21
     * The table associated with the model.
22
     *
23
     * @var string
24
     */
25
    protected $table = 'users';
26
27
    /**
28
     * The attributes that should be mutated to dates.
29
     *
30
     * @var array
31
     */
32
    protected $dates = [ 'deleted_at' ];
33
34
    /**
35
     * The attributes that should be casted to native types.
36
     *
37
     * @var array
38
     */
39
    protected $casts = [
40
        'avatar' => 'object',
41
    ];
42
43
    /**
44
     * The attributes that are mass assignable.
45
     *
46
     * @var array
47
     */
48
    protected $fillable = [
49
        'username',
50
        'name',
51
        'email',
52
        'password',
53
        'is_admin',
54
        'domain_id'
55
    ];
56
57
    /**
58
     * The attributes that should be hidden for arrays.
59
     *
60
     * @var array
61
     */
62
    protected $hidden = [
63
        'password', 'remember_token',
64
    ];
65
66
    /**
67
     * The accessors to append to the model's array form.
68
     *
69
     * @var array
70
     */
71
    protected $appends = [
72
        'recordLabel'
73
    ];
74
75
    public $searchableType = 'user';
76
77
    public $searchableColumns = [
78
        'name'
79
    ];
80
81
    public function getSearchResult(): SearchResult
82
    {
83
        return new SearchResult(
84
            $this,
85
            $this->recordLabel
86
        );
87
    }
88
89
    public function domain()
90
    {
91
        return $this->belongsTo(Domain::class);
92
    }
93
94
    public function lastDomain()
95
    {
96
        return $this->belongsTo(Domain::class);
97
    }
98
99
    public function privileges()
100
    {
101
        return $this->hasMany(Privilege::class);
102
    }
103
104
    public function menus()
105
    {
106
        return $this->hasMany(Menu::class);
107
    }
108
109
    /**
110
     * Returns record label
111
     *
112
     * @return string
113
     */
114
    public function getRecordLabelAttribute() : string
115
    {
116
        return trim($this->name) ?? $this->username;
117
    }
118
119
    /**
120
     * Get avatar type
121
     *
122
     * @return string
123
     */
124
    public function getAvatarTypeAttribute() : string
125
    {
126
        return $this->avatar->type ?? 'initials';
127
    }
128
129
    /**
130
     * Returns initals generated from the user name
131
     *
132
     * @return string
133
     */
134
    public function getInitialsAttribute() : string
135
    {
136
        $initials = "";
137
138
        $words = explode(" ", strtoupper($this->name));
139
140
        $i = 0;
141
        foreach ($words as $w) {
142
            $initials .= $w[0];
143
            $i++;
144
145
            if ($i === 3) { // Maximum: 3 letters
146
                break;
147
            }
148
        }
149
150
        return $initials;
151
    }
152
153
    /**
154
     * Returns the image to use as the user avatar
155
     *
156
     * @return string
157
     */
158
    public function getImageAttribute() : string
159
    {
160
        $image = 'vendor/uccello/uccello/images/user-no-image.png';
161
162
        if ($this->avatarType === 'gravatar') {
163
            $image = 'https://www.gravatar.com/avatar/' . md5($this->email) . '?d=mm';
164
165
        } elseif ($this->avatarType === 'image' && !empty($this->avatar->path)) {
166
            $image = $this->avatar->path;
167
        }
168
169
        return $image;
170
    }
171
172
    /**
173
     * Returns user's roles on a domain
174
     *
175
     * @param \Uccello\Core\Models\Domain $domain
176
     * @return \Illuminate\Support\Collection
177
     */
178
    public function rolesOnDomain($domain) : Collection
179
    {
180
        return Cache::remember('domain_'.$domain->slug.'_roles', 600, function () use($domain) {
181
            $roles = collect();
0 ignored issues
show
Bug introduced by
The function collect was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

181
            $roles = /** @scrutinizer ignore-call */ collect();
Loading history...
182
183
            if (config('uccello.roles.display_ancestors_roles')) {
0 ignored issues
show
Bug introduced by
The function config was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

183
            if (/** @scrutinizer ignore-call */ config('uccello.roles.display_ancestors_roles')) {
Loading history...
184
                $treeDomainsIds = $domain->findAncestors()->pluck('id');
185
            } else {
186
                $treeDomainsIds = collect([ $domain->id ]);
187
            }
188
189
            foreach ($treeDomainsIds as $treeDomainId) {
190
                $_domain = Domain::find($treeDomainId);
191
                foreach ($this->privileges->where('domain_id', $_domain->id) as $privilege) {
192
                    $roles[ ] = $privilege->role;
193
                }
194
            }
195
196
            return $roles;
197
        });
198
199
    }
200
201
    /**
202
     * Check if the user has at least a role on a domain
203
     *
204
     * @param \Uccello\Core\Models\Domain $domain
205
     * @return boolean
206
     */
207
    public function hasRoleOnDomain($domain) : bool {
208
        if ($this->is_admin) {
209
            return true;
210
        }
211
212
        return $this->rolesOnDomain($domain)->count() > 0;
213
    }
214
215
    /**
216
     * Check if the user has at least a role on a domain or its descendants
217
     *
218
     * @param \Uccello\Core\Models\Domain $domain
219
     * @return boolean
220
     */
221
    public function hasRoleOnDescendantDomain(Domain $domain) : bool {
222
        if ($this->is_admin) {
223
            return true;
224
        }
225
226
        $hasRole = false;
227
228
        $descendants = Cache::remember('domain_'.$domain->slug.'_descendants', 600, function () use($domain) {
229
            return $domain->findDescendants()->get();
230
        });
231
232
        foreach ($descendants as $descendant) {
233
            if ($this->hasRoleOnDomain($descendant)) {
234
                $hasRole = true;
235
                break;
236
            }
237
        }
238
239
        return $hasRole;
240
    }
241
242
    /**
243
     * Returns all user capabilities on a module in a domain.
244
     * If the user has a capability in one of the parents of a domain, he also has it in that domain.
245
     *
246
     * @param \Uccello\Core\Models\Domain $domain
247
     * @param \Uccello\Core\Models\Module $module
248
     * @return \Illuminate\Support\Collection
249
     */
250
    public function capabilitiesOnModule(Domain $domain, Module $module) : Collection
251
    {
252
        $keyName = 'user_'.$this->id.'_'.$domain->slug.'_'.$module->name.'_capabilities';
253
254
        return Cache::remember($keyName, 600, function () use($domain, $module) {
255
            $capabilities = collect();
0 ignored issues
show
Bug introduced by
The function collect was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

255
            $capabilities = /** @scrutinizer ignore-call */ collect();
Loading history...
256
257
            // Get the domain and all its parents
258
            $domainParents = $domain->findAncestors()->get();
259
260
            // Get user privileges on each domain
261
            foreach ($domainParents as $_domain) {
262
                $privileges = $this->privileges->where('domain_id', $_domain->id);
263
264
                foreach ($privileges as $privilege) {
265
266
                    foreach ($privilege->role->profiles as $profile) {
267
                        $capabilities = $capabilities->merge($profile->capabilitiesOnModule($module));
268
                    }
269
                }
270
            }
271
272
            return $capabilities;
273
        });
274
    }
275
276
    /**
277
     * Checks if the user has a capability on a module in a domain.
278
     *
279
     * @param string $capabilityName
280
     * @param \Uccello\Core\Models\Domain $domain
281
     * @param \Uccello\Core\Models\Module $module
282
     * @return boolean
283
     */
284
    public function hasCapabilityOnModule(string $capabilityName, Domain $domain, Module $module) : bool
285
    {
286
        $capability = capability($capabilityName);
287
288
        $userCapabilities = $this->capabilitiesOnModule($domain, $module);
289
290
        return $this->is_admin || $userCapabilities->contains($capability);
291
    }
292
293
    /**
294
     * Checks if the user can access to settings panel.
295
     * Checks if the user has at least one admin capability on admin modules in a domain.
296
     *
297
     * @param \Uccello\Core\Models\Domain|null $domain
298
     * @return boolean
299
     */
300
    public function canAccessToSettingsPanel(?Domain $domain) : bool
301
    {
302
        if (empty($domain)) {
303
            $domain = Domain::first();
304
        }
305
306
        $keyName = 'user_'.$this->id.'_'.$domain->slug.'_can_access_to_settings_panel';
307
308
        return Cache::remember($keyName, 600, function () use($domain) {
309
310
            $hasCapability = false;
311
312
            foreach (Module::all() as $module) {
313
                if ($module->isAdminModule() === true && $this->canAdmin($domain, $module)) {
314
                    $hasCapability = true;
315
                    break;
316
                }
317
            }
318
319
            return $hasCapability;
320
        });
321
    }
322
323
    /**
324
     * Checks if the user can admin a module in a domain.
325
     *
326
     * @param \Uccello\Core\Models\Domain $domain
327
     * @param \Uccello\Core\Models\Module $module
328
     * @return boolean
329
     */
330
    public function canAdmin(Domain $domain, Module $module) : bool
331
    {
332
        return $this->hasCapabilityOnModule('admin', $domain, $module);
333
    }
334
335
    /**
336
     * Checks if the user can create a module in a domain.
337
     *
338
     * @param \Uccello\Core\Models\Domain $domain
339
     * @param \Uccello\Core\Models\Module $module
340
     * @return boolean
341
     */
342
    public function canCreate(Domain $domain, Module $module) : bool
343
    {
344
        return $this->hasCapabilityOnModule('create', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
345
    }
346
347
    /**
348
     * Checks if the user can retrieve a module in a domain.
349
     *
350
     * @param \Uccello\Core\Models\Domain $domain
351
     * @param \Uccello\Core\Models\Module $module
352
     * @return boolean
353
     */
354
    public function canRetrieve(Domain $domain, Module $module) : bool
355
    {
356
        return $this->hasCapabilityOnModule('retrieve', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
357
    }
358
359
    /**
360
     * Checks if the user can update a module in a domain.
361
     *
362
     * @param \Uccello\Core\Models\Domain $domain
363
     * @param \Uccello\Core\Models\Module $module
364
     * @return boolean
365
     */
366
    public function canUpdate(Domain $domain, Module $module) : bool
367
    {
368
        return $this->hasCapabilityOnModule('update', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
369
    }
370
371
    /**
372
     * Checks if the user can delete a module in a domain.
373
     *
374
     * @param \Uccello\Core\Models\Domain $domain
375
     * @param \Uccello\Core\Models\Module $module
376
     * @return boolean
377
     */
378
    public function canDelete(Domain $domain, Module $module) : bool
379
    {
380
        return $this->hasCapabilityOnModule('delete', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
381
    }
382
383
    /**
384
     * Checks if the user can create by API a module in a domain.
385
     *
386
     * @param \Uccello\Core\Models\Domain $domain
387
     * @param \Uccello\Core\Models\Module $module
388
     * @return boolean
389
     */
390
    public function canCreateByApi(Domain $domain, Module $module) : bool
391
    {
392
        return $this->hasCapabilityOnModule('api-create', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
393
    }
394
395
    /**
396
     * Checks if the user can retrieve by API a module in a domain.
397
     *
398
     * @param \Uccello\Core\Models\Domain $domain
399
     * @param \Uccello\Core\Models\Module $module
400
     * @return boolean
401
     */
402
    public function canRetrieveByApi(Domain $domain, Module $module) : bool
403
    {
404
        return $this->hasCapabilityOnModule('api-retrieve', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
405
    }
406
407
    /**
408
     * Checks if the user can update by API a module in a domain.
409
     *
410
     * @param \Uccello\Core\Models\Domain $domain
411
     * @param \Uccello\Core\Models\Module $module
412
     * @return boolean
413
     */
414
    public function canUpdateByApi(Domain $domain, Module $module) : bool
415
    {
416
        return $this->hasCapabilityOnModule('api-update', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
417
    }
418
419
    /**
420
     * Checks if the user can delete by API a module in a domain.
421
     *
422
     * @param \Uccello\Core\Models\Domain $domain
423
     * @param \Uccello\Core\Models\Module $module
424
     * @return boolean
425
     */
426
    public function canDeleteByApi(Domain $domain, Module $module) : bool
427
    {
428
        return $this->hasCapabilityOnModule('api-delete', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
429
    }
430
431
    /**
432
     * Checks if the user has almost a role allowing to view data transversally
433
     *
434
     * @param \Uccello\Core\Models\Domain $domain
435
     * @return boolean
436
     */
437
    public function canSeeDescendantsRecords(Domain $domain) : bool
438
    {
439
        $allowed = false;
440
441
        if ($this->is_admin) {
442
            $allowed = true;
443
        } else {
444
            $roles = $this->rolesOnDomain($domain);
445
            foreach ($roles as $role) {
446
                if ($role->see_descendants_records) {
447
                    $allowed = true;
448
                    break;
449
                }
450
            }
451
        }
452
453
        return $allowed;
454
    }
455
}
456