Passed
Push — master ( 6bbf15...aead9d )
by Jonathan
19:29
created

User::getSettings()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 2
rs 10
cc 1
nc 1
nop 2
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
use Uccello\Core\Support\Traits\UccelloModule;
14
use Uccello\Core\Models\Group;
15
16
class User extends Authenticatable implements Searchable
17
{
18
    use SoftDeletes;
19
    use Notifiable;
20
    use RelatedlistTrait;
21
    use UccelloModule;
22
23
    /**
24
     * The table associated with the model.
25
     *
26
     * @var string
27
     */
28
    protected $table = 'users';
29
30
    /**
31
     * The attributes that should be mutated to dates.
32
     *
33
     * @var array
34
     */
35
    protected $dates = [ 'deleted_at' ];
36
37
    /**
38
     * The attributes that should be casted to native types.
39
     *
40
     * @var array
41
     */
42
    protected $casts = [
43
        'avatar' => 'object',
44
    ];
45
46
    /**
47
     * The attributes that are mass assignable.
48
     *
49
     * @var array
50
     */
51
    protected $fillable = [
52
        'username',
53
        'name',
54
        'email',
55
        'password',
56
        'is_admin',
57
        'domain_id'
58
    ];
59
60
    /**
61
     * The attributes that should be hidden for arrays.
62
     *
63
     * @var array
64
     */
65
    protected $hidden = [
66
        'password', 'remember_token',
67
    ];
68
69
    /**
70
     * The accessors to append to the model's array form.
71
     *
72
     * @var array
73
     */
74
    protected $appends = [
75
        'recordLabel'
76
    ];
77
78
    public $searchableType = 'user';
79
80
    public $searchableColumns = [
81
        'name'
82
    ];
83
84
    public function getSearchResult(): SearchResult
85
    {
86
        return new SearchResult(
87
            $this,
88
            $this->recordLabel
89
        );
90
    }
91
92
    public function domain()
93
    {
94
        return $this->belongsTo(Domain::class);
95
    }
96
97
    public function lastDomain()
98
    {
99
        return $this->belongsTo(Domain::class);
100
    }
101
102
    public function privileges()
103
    {
104
        return $this->hasMany(Privilege::class);
105
    }
106
107
    public function menus()
108
    {
109
        return $this->hasMany(Menu::class);
110
    }
111
112
    public function groups()
113
    {
114
        return $this->belongsToMany(Group::class, 'uccello_rl_groups_users');
115
    }
116
117
    public function userSettings()
118
    {
119
        return $this->hasOne(UserSettings::class, 'user_id');
120
    }
121
122
    /**
123
     * Returns record label
124
     *
125
     * @return string
126
     */
127
    public function getRecordLabelAttribute() : string
128
    {
129
        return trim($this->name) ?? $this->username;
130
    }
131
132
    /**
133
     * Get avatar type
134
     *
135
     * @return string
136
     */
137
    public function getAvatarTypeAttribute() : string
138
    {
139
        return $this->avatar->type ?? 'initials';
140
    }
141
142
    /**
143
     * Returns initals generated from the user name
144
     *
145
     * @return string
146
     */
147
    public function getInitialsAttribute() : string
148
    {
149
        $initials = "";
150
151
        $words = explode(" ", strtoupper($this->name));
152
153
        $i = 0;
154
        foreach ($words as $w) {
155
            $initials .= $w[0];
156
            $i++;
157
158
            if ($i === 3) { // Maximum: 3 letters
159
                break;
160
            }
161
        }
162
163
        return $initials;
164
    }
165
166
    /**
167
     * Returns the image to use as the user avatar
168
     *
169
     * @return string
170
     */
171
    public function getImageAttribute() : string
172
    {
173
        $image = 'vendor/uccello/uccello/images/user-no-image.png';
174
175
        if ($this->avatarType === 'gravatar') {
176
            $image = 'https://www.gravatar.com/avatar/' . md5($this->email) . '?d=mm';
177
178
        } elseif ($this->avatarType === 'image' && !empty($this->avatar->path)) {
179
            $image = $this->avatar->path;
180
        }
181
182
        return $image;
183
    }
184
185
    /**
186
     * Returns user settings
187
     *
188
     * @return \stdClass;
189
     */
190
    public function getSettingsAttribute()
191
    {
192
        return $this->userSettings->data ?? new \stdClass;
193
    }
194
195
    /**
196
     * Searches a settings by key and returns the current value
197
     *
198
     * @param string $key
199
     * @param mixed $defaultValue
200
     * @return \stdClass|null;
201
     */
202
    public function getSettings($key, $defaultValue=null) {
203
        return $this->userSettings->data->{$key} ?? $defaultValue;
204
    }
205
206
    /**
207
     * Returns user's roles on a domain
208
     *
209
     * @param \Uccello\Core\Models\Domain $domain
210
     * @return \Illuminate\Support\Collection
211
     */
212
    public function rolesOnDomain($domain) : Collection
213
    {
214
        return Cache::remember('user_'.$this->id.'_domain_'.$domain->slug.'roles', 600, function () use($domain) {
215
            $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

215
            $roles = /** @scrutinizer ignore-call */ collect();
Loading history...
216
217
            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

217
            if (/** @scrutinizer ignore-call */ config('uccello.roles.display_ancestors_roles')) {
Loading history...
218
                $treeDomainsIds = $domain->findAncestors()->pluck('id');
219
            } else {
220
                $treeDomainsIds = collect([ $domain->id ]);
221
            }
222
223
            foreach ($treeDomainsIds as $treeDomainId) {
224
                $_domain = Domain::find($treeDomainId);
225
                foreach ($this->privileges->where('domain_id', $_domain->id) as $privilege) {
226
                    $roles[ ] = $privilege->role;
227
                }
228
            }
229
230
            return $roles;
231
        });
232
233
    }
234
235
    /**
236
     * Check if the user has at least a role on a domain
237
     *
238
     * @param \Uccello\Core\Models\Domain $domain
239
     * @return boolean
240
     */
241
    public function hasRoleOnDomain($domain) : bool {
242
        if ($this->is_admin) {
243
            return true;
244
        }
245
246
        return $this->rolesOnDomain($domain)->count() > 0;
247
    }
248
249
    /**
250
     * Check if the user has at least a role on a domain or its descendants
251
     *
252
     * @param \Uccello\Core\Models\Domain $domain
253
     * @return boolean
254
     */
255
    public function hasRoleOnDescendantDomain(Domain $domain) : bool {
256
        if ($this->is_admin) {
257
            return true;
258
        }
259
260
        $hasRole = false;
261
262
        $descendants = Cache::remember('domain_'.$domain->slug.'_descendants', 600, function () use($domain) {
263
            return $domain->findDescendants()->get();
264
        });
265
266
        foreach ($descendants as $descendant) {
267
            if ($this->hasRoleOnDomain($descendant)) {
268
                $hasRole = true;
269
                break;
270
            }
271
        }
272
273
        return $hasRole;
274
    }
275
276
    /**
277
     * Returns all user capabilities on a module in a domain.
278
     * If the user has a capability in one of the parents of a domain, he also has it in that domain.
279
     *
280
     * @param \Uccello\Core\Models\Domain $domain
281
     * @param \Uccello\Core\Models\Module $module
282
     * @return \Illuminate\Support\Collection
283
     */
284
    public function capabilitiesOnModule(Domain $domain, Module $module) : Collection
285
    {
286
        $keyName = 'user_'.$this->id.'_'.$domain->slug.'_'.$module->name.'_capabilities';
287
288
        return Cache::remember($keyName, 600, function () use($domain, $module) {
289
            $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

289
            $capabilities = /** @scrutinizer ignore-call */ collect();
Loading history...
290
291
            // Get the domain and all its parents
292
            $domainParents = $domain->findAncestors()->get();
293
294
            // Get user privileges on each domain
295
            foreach ($domainParents as $_domain) {
296
                $privileges = $this->privileges->where('domain_id', $_domain->id);
297
298
                foreach ($privileges as $privilege) {
299
300
                    foreach ($privilege->role->profiles as $profile) {
301
                        $capabilities = $capabilities->merge($profile->capabilitiesOnModule($module));
302
                    }
303
                }
304
            }
305
306
            return $capabilities;
307
        });
308
    }
309
310
    /**
311
     * Checks if the user has a capability on a module in a domain.
312
     *
313
     * @param string $capabilityName
314
     * @param \Uccello\Core\Models\Domain $domain
315
     * @param \Uccello\Core\Models\Module $module
316
     * @return boolean
317
     */
318
    public function hasCapabilityOnModule(string $capabilityName, Domain $domain, Module $module) : bool
319
    {
320
        $capability = capability($capabilityName);
321
322
        $userCapabilities = $this->capabilitiesOnModule($domain, $module);
323
324
        return $this->is_admin || $userCapabilities->contains($capability);
325
    }
326
327
    /**
328
     * Checks if the user can access to settings panel.
329
     * Checks if the user has at least one admin capability on admin modules in a domain.
330
     *
331
     * @param \Uccello\Core\Models\Domain|null $domain
332
     * @return boolean
333
     */
334
    public function canAccessToSettingsPanel(?Domain $domain) : bool
335
    {
336
        if (empty($domain)) {
337
            $domain = Domain::first();
338
        }
339
340
        $keyName = 'user_'.$this->id.'_'.$domain->slug.'_can_access_to_settings_panel';
341
342
        return Cache::remember($keyName, 600, function () use($domain) {
343
344
            $hasCapability = false;
345
346
            foreach (Module::all() as $module) {
347
                if ($module->isAdminModule() === true && $this->canAdmin($domain, $module)) {
348
                    $hasCapability = true;
349
                    break;
350
                }
351
            }
352
353
            return $hasCapability;
354
        });
355
    }
356
357
    /**
358
     * Checks if the user can admin a module in a domain.
359
     *
360
     * @param \Uccello\Core\Models\Domain $domain
361
     * @param \Uccello\Core\Models\Module $module
362
     * @return boolean
363
     */
364
    public function canAdmin(Domain $domain, Module $module) : bool
365
    {
366
        return $this->hasCapabilityOnModule('admin', $domain, $module);
367
    }
368
369
    /**
370
     * Checks if the user can create a module in a domain.
371
     *
372
     * @param \Uccello\Core\Models\Domain $domain
373
     * @param \Uccello\Core\Models\Module $module
374
     * @return boolean
375
     */
376
    public function canCreate(Domain $domain, Module $module) : bool
377
    {
378
        return $this->hasCapabilityOnModule('create', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
379
    }
380
381
    /**
382
     * Checks if the user can retrieve a module in a domain.
383
     *
384
     * @param \Uccello\Core\Models\Domain $domain
385
     * @param \Uccello\Core\Models\Module $module
386
     * @return boolean
387
     */
388
    public function canRetrieve(Domain $domain, Module $module) : bool
389
    {
390
        return $this->hasCapabilityOnModule('retrieve', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
391
    }
392
393
    /**
394
     * Checks if the user can update a module in a domain.
395
     *
396
     * @param \Uccello\Core\Models\Domain $domain
397
     * @param \Uccello\Core\Models\Module $module
398
     * @return boolean
399
     */
400
    public function canUpdate(Domain $domain, Module $module) : bool
401
    {
402
        return $this->hasCapabilityOnModule('update', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
403
    }
404
405
    /**
406
     * Checks if the user can delete a module in a domain.
407
     *
408
     * @param \Uccello\Core\Models\Domain $domain
409
     * @param \Uccello\Core\Models\Module $module
410
     * @return boolean
411
     */
412
    public function canDelete(Domain $domain, Module $module) : bool
413
    {
414
        return $this->hasCapabilityOnModule('delete', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
415
    }
416
417
    /**
418
     * Checks if the user can create by API a module in a domain.
419
     *
420
     * @param \Uccello\Core\Models\Domain $domain
421
     * @param \Uccello\Core\Models\Module $module
422
     * @return boolean
423
     */
424
    public function canCreateByApi(Domain $domain, Module $module) : bool
425
    {
426
        return $this->hasCapabilityOnModule('api-create', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
427
    }
428
429
    /**
430
     * Checks if the user can retrieve by API a module in a domain.
431
     *
432
     * @param \Uccello\Core\Models\Domain $domain
433
     * @param \Uccello\Core\Models\Module $module
434
     * @return boolean
435
     */
436
    public function canRetrieveByApi(Domain $domain, Module $module) : bool
437
    {
438
        return $this->hasCapabilityOnModule('api-retrieve', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
439
    }
440
441
    /**
442
     * Checks if the user can update by API a module in a domain.
443
     *
444
     * @param \Uccello\Core\Models\Domain $domain
445
     * @param \Uccello\Core\Models\Module $module
446
     * @return boolean
447
     */
448
    public function canUpdateByApi(Domain $domain, Module $module) : bool
449
    {
450
        return $this->hasCapabilityOnModule('api-update', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
451
    }
452
453
    /**
454
     * Checks if the user can delete by API a module in a domain.
455
     *
456
     * @param \Uccello\Core\Models\Domain $domain
457
     * @param \Uccello\Core\Models\Module $module
458
     * @return boolean
459
     */
460
    public function canDeleteByApi(Domain $domain, Module $module) : bool
461
    {
462
        return $this->hasCapabilityOnModule('api-delete', $domain, $module) || ($module->isAdminModule() && $this->canAdmin($domain, $module));
463
    }
464
465
    /**
466
     * Checks if the user has almost a role allowing to view data transversally
467
     *
468
     * @param \Uccello\Core\Models\Domain $domain
469
     * @return boolean
470
     */
471
    public function canSeeDescendantsRecords(Domain $domain) : bool
472
    {
473
        $allowed = false;
474
475
        if ($this->is_admin) {
476
            $allowed = true;
477
        } else {
478
            $roles = $this->rolesOnDomain($domain);
479
            foreach ($roles as $role) {
480
                if ($role->see_descendants_records) {
481
                    $allowed = true;
482
                    break;
483
                }
484
            }
485
        }
486
487
        return $allowed;
488
    }
489
490
    public function getAllowedGroupUuids()
491
    {
492
        // Use cache
493
        $allowedGroups = Cache::rememberForever(
494
            'allowed_groups_for_' . ($this->is_admin ? 'admin' : $this->getKey()),
495
            function () {
496
                return $this->getAllowedGroupUuidsProcess();
497
            }
498
        );
499
500
        return $allowedGroups;
501
    }
502
503
    public function getAllowedGroupsAndUsers($addUsers = true)
504
    {
505
        // Use cache
506
        $allowedGroupsAndUsers = Cache::rememberForever(
507
            'allowed_group_users_for_' . ($addUsers ? 'u_' : '') . ($this->is_admin ? 'admin' : $this->getKey()),
508
            function () use ($addUsers) {
509
                return $this->getAllowedGroupsAndUsersProcess($addUsers);
510
            }
511
        );
512
513
        return $allowedGroupsAndUsers;
514
    }
515
516
    protected function getAllowedGroupUuidsProcess()
517
    {
518
        $allowedUserUuids = collect([$this->uuid]);
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

518
        $allowedUserUuids = /** @scrutinizer ignore-call */ collect([$this->uuid]);
Loading history...
519
520
        if ($this->is_admin) {
521
            $groups = Group::all();
522
        } else {
523
            $groups = [];
524
            $users = [];
525
526
            foreach ($this->groups as $group) {
527
                $groups[$group->uuid] = $group;
528
            };
529
530
            $this->addRecursiveChildrenGroups($groups, $users, $groups, false);
531
532
            $groups = collect($groups);
533
        }
534
535
        foreach ($groups as $uuid => $group) {
536
            $allowedUserUuids[] = $uuid;
537
        }
538
539
        return $allowedUserUuids;
540
    }
541
542
    protected function addRecursiveChildrenGroups(&$groups, &$users, $searchGroups, $addUsers = false)
543
    {
544
        foreach ($searchGroups as $uuid => $searchGroup) {
545
            $searchChildrenGroups = [];
546
547
            foreach ($searchGroup->childrenGroups as $childrenGroup) {
548
                if (empty($groups[$childrenGroup->uuid])) {
549
                    $groups[$childrenGroup->uuid] = $childrenGroup;
550
                    $searchChildrenGroups[$childrenGroup->uuid] = $childrenGroup;
551
                }
552
553
                if($addUsers)
554
                {
555
                    foreach ($childrenGroup->users as $user) {
556
                        if (empty($users[$user->uuid])) {
557
                            $users[$user->uuid] = $user;
558
                        }
559
                    }
560
                }
561
            }
562
563
            $this->addRecursiveChildrenGroups($groups, $users, $searchChildrenGroups, $addUsers);
564
        }
565
    }
566
567
    protected function getAllowedGroupsAndUsersProcess($addUsers = true)
568
    {
569
        $allowedUserUuids = 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

569
        $allowedUserUuids = /** @scrutinizer ignore-call */ collect([[
Loading history...
570
            'uuid' => $this->uuid,
571
            'recordLabel' => uctrans('me', $this->module)
572
        ]]);
573
574
        if ($this->is_admin) {
575
            $groups = Group::orderBy('name')->get();
576
            $users  = \App\User::orderBy('name')->get();
0 ignored issues
show
Bug introduced by
The type App\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...
577
        } else {
578
            $groups = [];
579
            $users = [];
580
581
            foreach ($this->groups as $group) {
582
                $groups[$group->uuid] = $group;
583
584
                if($addUsers)
585
                {
586
                    foreach ($group->users as $user) {
587
                        if (empty($users[$user->uuid])) {
588
                            $users[$user->uuid] = $user;
589
                        }
590
                    }
591
                }
592
            };
593
594
            $this->addRecursiveChildrenGroups($groups, $users, $groups, $addUsers);
595
596
            $groups = collect($groups)->sortBy('name');
597
            $users  = collect($users)->sortBy('name');
598
        }
599
600
        foreach ($groups as $uuid => $group) {
601
            $allowedUserUuids[] = [
602
                'uuid' => $group->uuid,
603
                'recordLabel' => $group->recordLabel
604
            ];
605
        }
606
607
        foreach ($users as $uuid => $user) {
608
            if($user->getKey() != $this->getKey()) {
609
                $allowedUserUuids[] = [
610
                    'uuid' => $user->uuid,
611
                    'recordLabel' => $user->recordLabel
612
                ];
613
            }
614
        }
615
616
        return $allowedUserUuids;
617
    }
618
}
619