diff --git a/docs/content/1_docs/10_user-management/2_advanced-permissions.md b/docs/content/1_docs/10_user-management/2_advanced-permissions.md index 5e3f92c99..58b3cd1ac 100644 --- a/docs/content/1_docs/10_user-management/2_advanced-permissions.md +++ b/docs/content/1_docs/10_user-management/2_advanced-permissions.md @@ -29,6 +29,9 @@ In addition to this we have to configure the permissions' system. There are 3 le Set the `twill.permissions.level` to the desired type. And also set the modules to manage in the `twill.permissions.modules` key. +Beside setting global `twill.permissions.level` it is also possible to set per-module level by adding +it to `twill.permissions.modules` as keyed item as shown for model `pages` in example below. + ```php {7-10} [ - 'level' => \A17\Twill\Enums\PermissionLevel::LEVEL_ROLE, - 'modules' => ['blogs'], + 'level' => \A17\Twill\Enums\PermissionLevel::LEVEL_ROLE_GROUP, + 'modules' => [ + 'blogs', + 'pages' => \A17\Twill\Enums\PermissionLevel::LEVEL_ROLE_GROUP_ITEM + ], ], ] ``` diff --git a/src/Http/Controllers/Admin/GroupController.php b/src/Http/Controllers/Admin/GroupController.php index 5451cd1b9..ef8f89fbf 100644 --- a/src/Http/Controllers/Admin/GroupController.php +++ b/src/Http/Controllers/Admin/GroupController.php @@ -2,6 +2,7 @@ namespace A17\Twill\Http\Controllers\Admin; +use A17\Twill\Enums\PermissionLevel; use A17\Twill\Facades\TwillPermissions; use A17\Twill\Models\Contracts\TwillModelContract; use A17\Twill\Services\Listings\Columns\Text; @@ -67,8 +68,10 @@ protected function getIndexOption($option, $item = null): mixed protected function formData($request): array { + // Divide permissions based on level to minimize instance fetching return [ - 'permissionModules' => Permission::permissionableParentModuleItems(), + 'permissionModulesLevelGroup' => Permission::permissionableModulesForLevel([PermissionLevel::LEVEL_ROLE_GROUP, PermissionLevel::LEVEL_ROLE_GROUP_ITEM]), + 'permissionModulesLevelItem' => Permission::permissionableParentModuleItemsForLevel(PermissionLevel::LEVEL_ROLE_GROUP_ITEM), ]; } diff --git a/src/Http/Controllers/Admin/UserController.php b/src/Http/Controllers/Admin/UserController.php index 7cf38c4a9..36f535169 100755 --- a/src/Http/Controllers/Admin/UserController.php +++ b/src/Http/Controllers/Admin/UserController.php @@ -201,12 +201,7 @@ protected function formData($request) $titleThumbnail = $user->cmsImage($role, $crop, $params); } - if (TwillPermissions::levelIs(PermissionLevel::LEVEL_ROLE_GROUP_ITEM)) { - $permissionsData = [ - 'permissionModules' => $this->getPermissionModules(), - ]; - } - + // Fetch modules with LEVEL_ROLE_GROUP_ITEM permission level return [ 'roleList' => $this->getRoleList(), 'titleThumbnail' => $titleThumbnail ?? null, @@ -214,7 +209,8 @@ protected function formData($request) 'qrCode' => $qrCode ?? null, 'groupPermissionMapping' => $this->getGroupPermissionMapping(), 'groupOptions' => $this->getGroups(), - ] + ($permissionsData ?? []); + 'permissionModules' => Permission::permissionableParentModuleItemsForLevel(PermissionLevel::LEVEL_ROLE_GROUP_ITEM) + ]; } /** @@ -354,15 +350,6 @@ private function getRoleList() })->values()->toArray(); } - private function getPermissionModules() - { - if (config('twill.enabled.permissions-management')) { - return Permission::permissionableParentModuleItems(); - } - - return []; - } - public function getSubmitOptions(Model $item): ?array { // Use options from form template diff --git a/src/Models/Permission.php b/src/Models/Permission.php index 743a39142..ddd46f76e 100644 --- a/src/Models/Permission.php +++ b/src/Models/Permission.php @@ -5,12 +5,15 @@ use A17\Twill\Enums\PermissionLevel; use A17\Twill\Exceptions\ModuleNotFoundException; use A17\Twill\Facades\TwillPermissions; +use Illuminate\Support\Collection; use Illuminate\Support\Str; use Illuminate\Foundation\Auth\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Model as BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; class Permission extends BaseModel { @@ -87,6 +90,35 @@ public static function available($scope) } } + /** + * Retrieve the list of modules with specific permission levels that can be applied. + * + * @return Collection + */ + public static function permissionableModulesWithLevel() + { + return collect(config('twill.permissions.modules', [])) + ->mapWithKeys(function ($item, $key) { + if (is_integer($key)) { + return [$item => config('twill.permissions.level', PermissionLevel::LEVEL_ROLE)]; + } + return [$key => $item]; + }); + } + + /** + * Retrieve the list of modules that permissions can be applied to for specific permission level. + * + * @return Collection + */ + public static function permissionableModulesForLevel(string|array $level) + { + if (is_string($level)) { + return self::permissionableModulesWithLevel()->filter(fn($item, $key) => $item === $level)->keys(); + } + return self::permissionableModulesWithLevel()->filter(fn($item, $key) => in_array($item, $level))->keys(); + } + /** * Retrieve the list of modules that permissions can be applied to. * @@ -94,7 +126,7 @@ public static function available($scope) */ public static function permissionableModules() { - return collect(config('twill.permissions.modules', [])); + return self::permissionableModulesWithLevel()->keys(); } /** @@ -115,6 +147,31 @@ public static function permissionableParentModuleItems() }); } + /** + * Retrieve a collection of items that belongs to models with specific permission level. + * + * @param string $level + * @return Collection + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public static function permissionableParentModuleItemsForLevel(string $level) + { + return self::permissionableModulesWithLevel() + ->filter(fn($item, $key) => $item === $level) + ->keys() + ->filter(function ($module) { + return !strpos($module, '.'); + }) + ->mapWithKeys(function ($module) { + try { + return [$module => getRepositoryByModuleName($module)->get([], [], [], -1)]; + } catch (ModuleNotFoundException $e) { + return []; + } + }); + } + /** * Get the parent permissionable model (one of the permissionale module). * diff --git a/src/Repositories/Behaviors/HandleGroupPermissions.php b/src/Repositories/Behaviors/HandleGroupPermissions.php index 7ea303cb8..9e16d9890 100644 --- a/src/Repositories/Behaviors/HandleGroupPermissions.php +++ b/src/Repositories/Behaviors/HandleGroupPermissions.php @@ -27,7 +27,7 @@ public function getFormFieldsHandleGroupPermissions($object, $fields) } // Add active module permissions - foreach (Permission::permissionableModules() as $moduleName) { + foreach (Permission::permissionableModulesForLevel([PermissionLevel::LEVEL_ROLE_GROUP, PermissionLevel::LEVEL_ROLE_GROUP_ITEM]) as $moduleName) { $modulePermission = $object->permissions()->module()->ofModuleName($moduleName)->first(); if ($modulePermission) { $fields['module_' . $moduleName . '_permissions'] = $modulePermission->name; @@ -35,6 +35,13 @@ public function getFormFieldsHandleGroupPermissions($object, $fields) $fields['module_' . $moduleName . '_permissions'] = 'none'; } } + + // Add LEVEL_ROLE_GROUP_ITEM permission level modules with items + foreach ($object->permissions()->moduleItem()->get() as $permission) { + $model = $permission->permissionable()->first(); + $moduleName = getModuleNameByModel($model); + $fields[$moduleName . '_' . $model->id . '_permission'] = $permission->name; + } } elseif (TwillPermissions::levelIs(PermissionLevel::LEVEL_ROLE_GROUP_ITEM)) { // Add active item permissions foreach ($object->permissions()->moduleItem()->get() as $permission) { diff --git a/src/Repositories/Behaviors/HandlePermissions.php b/src/Repositories/Behaviors/HandlePermissions.php index a0d398c26..55a87493b 100644 --- a/src/Repositories/Behaviors/HandlePermissions.php +++ b/src/Repositories/Behaviors/HandlePermissions.php @@ -134,7 +134,7 @@ public function afterSaveHandlePermissions($object, $fields) private function shouldProcessPermissions($moduleName): bool { - return TwillPermissions::levelIs(PermissionLevel::LEVEL_ROLE_GROUP_ITEM) + return TwillPermissions::levelForModuleIs($moduleName, PermissionLevel::LEVEL_ROLE_GROUP_ITEM) && TwillPermissions::getPermissionModule($moduleName); } diff --git a/src/Repositories/Behaviors/HandleUserPermissions.php b/src/Repositories/Behaviors/HandleUserPermissions.php index a40cde8db..5104fe5bd 100644 --- a/src/Repositories/Behaviors/HandleUserPermissions.php +++ b/src/Repositories/Behaviors/HandleUserPermissions.php @@ -3,7 +3,6 @@ namespace A17\Twill\Repositories\Behaviors; use A17\Twill\Enums\PermissionLevel; -use A17\Twill\Facades\TwillPermissions; use A17\Twill\Models\Model; use A17\Twill\Models\Permission; use A17\Twill\Models\User; @@ -23,7 +22,7 @@ public function getFormFieldsHandleUserPermissions($object, $fields) { if ( !config('twill.enabled.permissions-management') || - !TwillPermissions::levelIs(PermissionLevel::LEVEL_ROLE_GROUP_ITEM) + Permission::permissionableModulesForLevel(PermissionLevel::LEVEL_ROLE_GROUP_ITEM)->isEmpty() ) { return $fields; } @@ -58,7 +57,7 @@ public function afterSaveHandleUserPermissions($object, $fields) $this->addOrRemoveUserToEveryoneGroup($object); - if (TwillPermissions::levelIs(PermissionLevel::LEVEL_ROLE_GROUP_ITEM)) { + if (Permission::permissionableModulesForLevel(PermissionLevel::LEVEL_ROLE_GROUP_ITEM)->isNotEmpty()) { $this->updateUserItemPermissions($object, $fields); } } diff --git a/src/TwillPermissions.php b/src/TwillPermissions.php index 3ff0fafae..a76f8a0cd 100644 --- a/src/TwillPermissions.php +++ b/src/TwillPermissions.php @@ -64,6 +64,23 @@ public function levelIs(string $level): bool return $this->enabled() && config('twill.permissions.level') === $level; } + /** + * Check specific permission level for module + * + * @param string $module + * @param string $level + * @return bool + * @throws \Exception + */ + public function levelForModuleIs(string $module, string $level): bool + { + if (!PermissionLevel::isValid($level)) { + throw new \Exception('Invalid permission level. Check TwillPermissions for available levels'); + } + + return $this->enabled() && Permission::permissionableModulesWithLevel()->get($module) === $level; + } + public function levelIsOneOf(array $levels): bool { foreach ($levels as $level) { diff --git a/views/groups/form.blade.php b/views/groups/form.blade.php index 962916289..6bfdb4a1e 100644 --- a/views/groups/form.blade.php +++ b/views/groups/form.blade.php @@ -17,8 +17,22 @@ :max="999" /> - @if(\A17\Twill\Facades\TwillPermissions::levelIs(\A17\Twill\Enums\PermissionLevel::LEVEL_ROLE_GROUP)) - + @if(config('twill.support_subdomain_admin_routing')) + + @foreach(config('twill.app_names') as $subdomain => $subdomainTitle) + + @endforeach + + @endif +@stop + + +@section('fieldsets') + @if($permissionModulesLevelGroup->isNotEmpty()) + - @foreach($permissionModules as $moduleName => $moduleItems) + @foreach($permissionModulesLevelGroup as $moduleName) @endforeach - + @endif - @if(config('twill.support_subdomain_admin_routing')) - - @foreach(config('twill.app_names') as $subdomain => $subdomainTitle) - - @endforeach - - @endif -@stop - -@if(\A17\Twill\Facades\TwillPermissions::levelIs(\A17\Twill\Enums\PermissionLevel::LEVEL_ROLE_GROUP_ITEM)) - @can('edit-user-groups') - @section('fieldsets') - @foreach($permissionModules as $moduleName => $moduleItems) + @if($permissionModulesLevelItem->isNotEmpty()) + @can('edit-user-groups') + @foreach($permissionModulesLevelItem as $moduleName => $moduleItems) @endforeach - @stop - @endcan -@endif + @endcan + @endif +@stop diff --git a/views/layouts/form.blade.php b/views/layouts/form.blade.php index 23dc2d38f..9c8317ba7 100644 --- a/views/layouts/form.blade.php +++ b/views/layouts/form.blade.php @@ -126,7 +126,7 @@ @yield('fieldsets') @endif - @if(\A17\Twill\Facades\TwillPermissions::levelIs(\A17\Twill\Enums\PermissionLevel::LEVEL_ROLE_GROUP_ITEM)) + @if(\A17\Twill\Facades\TwillPermissions::levelForModuleIs(getModuleNameByModel($item), \A17\Twill\Enums\PermissionLevel::LEVEL_ROLE_GROUP_ITEM)) @if($showPermissionFieldset ?? null) @can('manage-item', isset($item) ? $item : null) 'Edit ' . $module_name ] ], - (\A17\Twill\Facades\TwillPermissions::levelIs(\A17\Twill\Enums\PermissionLevel::LEVEL_ROLE_GROUP_ITEM) ? [['value' => 'manage-module', 'label' => 'Manage ' . $module_name ]] : []))" + (\A17\Twill\Facades\TwillPermissions::levelForModuleIs($module_name, \A17\Twill\Enums\PermissionLevel::LEVEL_ROLE_GROUP_ITEM) ? [['value' => 'manage-module', 'label' => 'Manage ' . $module_name ]] : []))" /> @endforeach diff --git a/views/users/form.blade.php b/views/users/form.blade.php index fc5845844..f3c9374c9 100644 --- a/views/users/form.blade.php +++ b/views/users/form.blade.php @@ -156,7 +156,7 @@ function($locale) { @section('fieldsets') - @if(\A17\Twill\Facades\TwillPermissions::levelIs(\A17\Twill\Enums\PermissionLevel::LEVEL_ROLE_GROUP_ITEM)) + @if($permissionModules->isNotEmpty()) @can('edit-users') @unless($item->isSuperAdmin() || $item->id === $currentUser->id)