Introduce concept of system roles
These are roles defined by a special API code, once a system role code is detected, the role becomes locked and its permissions are sourced from the AuthManager. All permissions are granted to system roles by default, unless otherwise specified. This should make it easier to create client accounts as "Publishers", hiding developer tools like the CMS and Builder plugins by default.
This commit is contained in:
parent
358a6337df
commit
4fd1ca824f
|
|
@ -31,6 +31,7 @@ class AuthManager extends RainAuthManager
|
|||
'code' => null,
|
||||
'label' => null,
|
||||
'comment' => null,
|
||||
'roles' => null,
|
||||
'order' => 500
|
||||
];
|
||||
|
||||
|
|
@ -44,6 +45,11 @@ class AuthManager extends RainAuthManager
|
|||
*/
|
||||
protected $permissions = [];
|
||||
|
||||
/**
|
||||
* @var array List of registered permission roles.
|
||||
*/
|
||||
protected $permissionRoles = false;
|
||||
|
||||
/**
|
||||
* @var array Cache of registered permissions.
|
||||
*/
|
||||
|
|
@ -157,4 +163,40 @@ class AuthManager extends RainAuthManager
|
|||
|
||||
return $tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of registered permissions belonging to a given role code
|
||||
* @param string $role
|
||||
* @return array
|
||||
*/
|
||||
public function listPermissionsForRole($role, $includeOrphans = true)
|
||||
{
|
||||
if ($this->permissionRoles === false) {
|
||||
$this->permissionRoles = [];
|
||||
|
||||
foreach ($this->listPermissions() as $permission) {
|
||||
if ($permission->roles) {
|
||||
foreach ((array) $permission->roles as $_role) {
|
||||
$this->permissionRoles[$_role][$permission->code] = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->permissionRoles['*'][$permission->code] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->permissionRoles[$role] ?? [];
|
||||
|
||||
if ($includeOrphans) {
|
||||
$result += $this->permissionRoles['*'] ?? [];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function hasPermissionsForRole($role)
|
||||
{
|
||||
return !!$this->listPermissionsForRole($role, false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php namespace Backend\Controllers;
|
||||
|
||||
use View;
|
||||
use Response;
|
||||
use BackendMenu;
|
||||
use BackendAuth;
|
||||
use Backend\Classes\Controller;
|
||||
|
|
@ -30,6 +32,15 @@ class UserRoles extends Controller
|
|||
|
||||
BackendMenu::setContext('October.System', 'system', 'users');
|
||||
SettingsManager::setContext('October.System', 'administrators');
|
||||
|
||||
/*
|
||||
* Only super users can access
|
||||
*/
|
||||
$this->bindEvent('page.beforeDisplay', function() {
|
||||
if (!$this->user->isSuperUser()) {
|
||||
return Response::make(View::make('backend::access_denied'), 403);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ class Users extends Controller
|
|||
|
||||
if (!$this->user->isSuperUser()) {
|
||||
$form->removeField('is_superuser');
|
||||
$form->removeField('role');
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ list: ~/modules/backend/models/usergroup/columns.yaml
|
|||
modelClass: Backend\Models\UserGroup
|
||||
recordUrl: backend/usergroups/update/:id
|
||||
noRecordsMessage: backend::lang.list.no_records
|
||||
recordsPerPage: 5
|
||||
recordsPerPage: 25
|
||||
showSetup: true
|
||||
|
||||
toolbar:
|
||||
buttons: list_toolbar
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ list: ~/modules/backend/models/userrole/columns.yaml
|
|||
modelClass: Backend\Models\UserRole
|
||||
recordUrl: backend/userroles/update/:id
|
||||
noRecordsMessage: backend::lang.list.no_records
|
||||
recordsPerPage: 5
|
||||
recordsPerPage: 25
|
||||
showSetup: true
|
||||
|
||||
toolbar:
|
||||
buttons: list_toolbar
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
<a href="<?= Backend::url('backend/users/create') ?>" class="btn btn-primary oc-icon-plus">
|
||||
<?= e(trans('backend::lang.user.new')) ?>
|
||||
</a>
|
||||
<a href="<?= Backend::url('backend/userroles') ?>" class="btn btn-default oc-icon-address-card">
|
||||
<?= e(trans('backend::lang.user.role.list_title')) ?>
|
||||
</a>
|
||||
<?php if ($this->user->isSuperUser()): ?>
|
||||
<a href="<?= Backend::url('backend/userroles') ?>" class="btn btn-default oc-icon-address-card">
|
||||
<?= e(trans('backend::lang.user.role.list_title')) ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
<a href="<?= Backend::url('backend/usergroups') ?>" class="btn btn-default oc-icon-group">
|
||||
<?= e(trans('backend::lang.user.group.list_title')) ?>
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use Backend\Models\UserRole;
|
||||
use October\Rain\Database\Schema\Blueprint;
|
||||
use October\Rain\Database\Updates\Migration;
|
||||
|
||||
|
|
@ -14,6 +15,7 @@ class DbBackendUserRoles extends Migration
|
|||
$table->string('code')->nullable()->index('role_code_index');
|
||||
$table->text('description')->nullable();
|
||||
$table->text('permissions')->nullable();
|
||||
$table->boolean('is_system')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
|
|
@ -33,11 +35,12 @@ class DbBackendUserRoles extends Migration
|
|||
// Role not found in the users table, perform a complete migration.
|
||||
// Merging group permissions with the user and assigning the user
|
||||
// with the first available role.
|
||||
if (Schema::hasColumn('backend_users', 'role_id')) {
|
||||
if (!Schema::hasColumn('backend_users', 'role_id')) {
|
||||
Schema::table('backend_users', function (Blueprint $table) {
|
||||
$table->integer('role_id')->unsigned()->nullable()->index('admin_role_index');
|
||||
});
|
||||
|
||||
$this->createSystemUserRoles();
|
||||
$this->migratePermissionsFromGroupsToRoles();
|
||||
}
|
||||
|
||||
|
|
@ -49,6 +52,21 @@ class DbBackendUserRoles extends Migration
|
|||
}
|
||||
}
|
||||
|
||||
protected function createSystemUserRoles()
|
||||
{
|
||||
Db::table('backend_user_roles')->insert([
|
||||
'name' => 'Publisher',
|
||||
'code' => UserRole::CODE_PUBLISHER,
|
||||
'description' => 'Site editor with access to publishing tools.',
|
||||
]);
|
||||
|
||||
Db::table('backend_user_roles')->insert([
|
||||
'name' => 'Developer',
|
||||
'code' => UserRole::CODE_DEVELOPER,
|
||||
'description' => 'Site administrator with access to developer tools.',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function migratePermissionsFromGroupsToRoles()
|
||||
{
|
||||
$groups = Db::table('backend_user_groups')->get();
|
||||
|
|
@ -66,6 +84,7 @@ class DbBackendUserRoles extends Migration
|
|||
try {
|
||||
$roles[$group->id] = Db::table('backend_user_roles')->insertGetId([
|
||||
'name' => $group->name,
|
||||
'description' => $group->description,
|
||||
'permissions' => $group->permissions ?? null
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ use Eloquent;
|
|||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
use Seeder;
|
||||
use Backend\Models\User;
|
||||
use Backend\Models\UserRole;
|
||||
use Backend\Models\UserGroup;
|
||||
|
||||
class SeedSetupAdmin extends Seeder
|
||||
{
|
||||
|
||||
public static $email = 'admin@domain.tld';
|
||||
public static $login = 'admin';
|
||||
public static $password = 'admin';
|
||||
|
|
@ -25,9 +25,21 @@ class SeedSetupAdmin extends Seeder
|
|||
|
||||
public function run()
|
||||
{
|
||||
UserRole::create([
|
||||
'name' => 'Publisher',
|
||||
'code' => UserRole::CODE_PUBLISHER,
|
||||
'description' => 'Site editor with access to publishing tools.',
|
||||
]);
|
||||
|
||||
$role = UserRole::create([
|
||||
'name' => 'Developer',
|
||||
'code' => UserRole::CODE_DEVELOPER,
|
||||
'description' => 'Site administrator with access to developer tools.',
|
||||
]);
|
||||
|
||||
$group = UserGroup::create([
|
||||
'name' => 'Owners',
|
||||
'code' => UserGroup::DEFAULT_CODE,
|
||||
'code' => UserGroup::CODE_OWNERS,
|
||||
'description' => 'Default group for website owners.',
|
||||
'is_new_user_default' => false
|
||||
]);
|
||||
|
|
@ -41,10 +53,10 @@ class SeedSetupAdmin extends Seeder
|
|||
'last_name' => static::$lastName,
|
||||
'permissions' => [],
|
||||
'is_superuser' => true,
|
||||
'is_activated' => true
|
||||
'is_activated' => true,
|
||||
'role_id' => $role->id
|
||||
]);
|
||||
|
||||
$user->addGroup($group);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ use BackendAuth;
|
|||
*/
|
||||
class PermissionEditor extends FormWidgetBase
|
||||
{
|
||||
protected $user;
|
||||
|
||||
public $mode;
|
||||
|
||||
/**
|
||||
|
|
@ -22,6 +24,8 @@ class PermissionEditor extends FormWidgetBase
|
|||
$this->fillFromConfig([
|
||||
'mode'
|
||||
]);
|
||||
|
||||
$this->user = BackendAuth::getUser();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -38,6 +42,10 @@ class PermissionEditor extends FormWidgetBase
|
|||
*/
|
||||
public function prepareVars()
|
||||
{
|
||||
if ($this->formField->disabled) {
|
||||
$this->previewMode = true;
|
||||
}
|
||||
|
||||
$permissionsData = $this->formField->getValueFromData($this->model);
|
||||
if (!is_array($permissionsData)) {
|
||||
$permissionsData = [];
|
||||
|
|
@ -54,6 +62,36 @@ class PermissionEditor extends FormWidgetBase
|
|||
* @inheritDoc
|
||||
*/
|
||||
public function getSaveValue($value)
|
||||
{
|
||||
if ($this->user->isSuperUser()) {
|
||||
return is_array($value) ? $value : [];
|
||||
}
|
||||
|
||||
return $this->getSaveValueSecure($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function loadAssets()
|
||||
{
|
||||
$this->addCss('css/permissioneditor.css', 'core');
|
||||
$this->addJs('js/permissioneditor.js', 'core');
|
||||
}
|
||||
|
||||
protected function getControlMode()
|
||||
{
|
||||
return strlen($this->mode) ? $this->mode : 'radio';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a safely parsed set of permissions, ensuring the user cannot elevate
|
||||
* their own permissions or permissions of another user above their own.
|
||||
*
|
||||
* @param string $value
|
||||
* @return array
|
||||
*/
|
||||
protected function getSaveValueSecure($value)
|
||||
{
|
||||
$newPermissions = is_array($value) ? array_map('intval', $value) : [];
|
||||
|
||||
|
|
@ -80,20 +118,6 @@ class PermissionEditor extends FormWidgetBase
|
|||
return $newPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function loadAssets()
|
||||
{
|
||||
$this->addCss('css/permissioneditor.css', 'core');
|
||||
$this->addJs('js/permissioneditor.js', 'core');
|
||||
}
|
||||
|
||||
protected function getControlMode()
|
||||
{
|
||||
return strlen($this->mode) ? $this->mode : 'radio';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the available permissions; removing those that the logged-in user does not have access to
|
||||
*
|
||||
|
|
@ -102,17 +126,22 @@ class PermissionEditor extends FormWidgetBase
|
|||
protected function getFilteredPermissions()
|
||||
{
|
||||
$permissions = BackendAuth::listTabbedPermissions();
|
||||
$user = BackendAuth::getUser();
|
||||
|
||||
if ($this->user->isSuperUser()) {
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
foreach ($permissions as $tab => $permissionsArray) {
|
||||
foreach ($permissionsArray as $index => $permission) {
|
||||
if (!$user->hasAccess($permission->code)) {
|
||||
if (!$this->user->hasAccess($permission->code)) {
|
||||
unset($permissionsArray[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($permissionsArray)) {
|
||||
unset($permissions[$tab]);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
$permissions[$tab] = $permissionsArray;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,38 @@
|
|||
<div class="permissioneditor" <?= $field->getAttributes() ?>>
|
||||
<div class="permissioneditor <?= $this->previewMode ? 'control-disabled' : '' ?>" <?= $field->getAttributes() ?>>
|
||||
<table>
|
||||
<?php
|
||||
<?php
|
||||
$firstTab = true;
|
||||
$globalIndex = 0;
|
||||
foreach ($permissions as $tab=>$tabPermissions): ?>
|
||||
?>
|
||||
<?php foreach ($permissions as $tab => $tabPermissions): ?>
|
||||
<tr class="section">
|
||||
<th class="tab"><?= e(trans($tab)) ?></th>
|
||||
<th class="permission-type"><?= $firstTab ? e(Lang::get('backend::lang.user.allow')) : '' ?></th>
|
||||
<th class="permission-type"><?= $firstTab ? e(trans('backend::lang.user.allow')) : '' ?></th>
|
||||
|
||||
<?php if (!$checkboxMode): ?>
|
||||
<th class="permission-type"><?= $firstTab ? e(Lang::get('backend::lang.user.inherit')) : '' ?></th>
|
||||
<th class="permission-type"><?= $firstTab ? e(Lang::get('backend::lang.user.deny')) : '' ?></th>
|
||||
<th class="permission-type"><?= $firstTab ? e(trans('backend::lang.user.inherit')) : '' ?></th>
|
||||
<th class="permission-type"><?= $firstTab ? e(trans('backend::lang.user.deny')) : '' ?></th>
|
||||
<?php endif ?>
|
||||
|
||||
<th></th>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
$lastIndex = count($tabPermissions)-1;
|
||||
foreach ($tabPermissions as $index=>$permission):
|
||||
<?php
|
||||
$lastIndex = count($tabPermissions) - 1;
|
||||
?>
|
||||
<?php foreach ($tabPermissions as $index => $permission): ?>
|
||||
<?php
|
||||
$globalIndex++;
|
||||
|
||||
if (!$checkboxMode) {
|
||||
$permissionValue = array_key_exists($permission->code, $permissionsData) ?
|
||||
$permissionsData[$permission->code] : 0;
|
||||
}
|
||||
$permissionValue = array_key_exists($permission->code, $permissionsData)
|
||||
? $permissionsData[$permission->code]
|
||||
: 0;
|
||||
}
|
||||
else {
|
||||
$isChecked = array_key_exists($permission->code, $permissionsData);
|
||||
}
|
||||
?>
|
||||
?>
|
||||
<tr class="<?= $lastIndex == $index ? 'last-section-row' : '' ?>
|
||||
<?= $checkboxMode ? 'mode-checkbox' : 'mode-radio' ?>
|
||||
<?= $checkboxMode && !$isChecked ? 'disabled' : '' ?>
|
||||
|
|
@ -101,9 +105,10 @@
|
|||
<td></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
<?php
|
||||
<?php
|
||||
$firstTab = false;
|
||||
endforeach ?>
|
||||
?>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<div class="permissions-overlay"></div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ return [
|
|||
'group' => [
|
||||
'name' => 'Group',
|
||||
'name_field' => 'Name',
|
||||
'name_comment' => 'The name is displayed in the group list on the Create/Edit Administrator form.',
|
||||
'name_comment' => 'The name is displayed in the group list on the Administrator form.',
|
||||
'description_field' => 'Description',
|
||||
'is_new_user_default_field_label' => 'Default group',
|
||||
'is_new_user_default_field_comment' => 'Add new administrators to this group by default',
|
||||
|
|
@ -157,7 +157,7 @@ return [
|
|||
'role' => [
|
||||
'name' => 'Role',
|
||||
'name_field' => 'Name',
|
||||
'name_comment' => 'The name is displayed in the role list on the Create/Edit Administrator form.',
|
||||
'name_comment' => 'The name is displayed in the role list on the Administrator form.',
|
||||
'description_field' => 'Description',
|
||||
'code_field' => 'Code',
|
||||
'code_comment' => 'Enter a unique code if you want to access the role object with the API.',
|
||||
|
|
|
|||
|
|
@ -10,10 +10,7 @@ use October\Rain\Auth\Models\Group as GroupBase;
|
|||
*/
|
||||
class UserGroup extends GroupBase
|
||||
{
|
||||
/**
|
||||
* @var string The default group code.
|
||||
*/
|
||||
const DEFAULT_CODE = 'owners';
|
||||
const CODE_OWNERS = 'owners';
|
||||
|
||||
/**
|
||||
* @var string The database table used by the model.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?php namespace Backend\Models;
|
||||
|
||||
use Backend\Classes\AuthManager;
|
||||
use October\Rain\Auth\Models\Role as RoleBase;
|
||||
|
||||
/**
|
||||
|
|
@ -10,10 +11,8 @@ use October\Rain\Auth\Models\Role as RoleBase;
|
|||
*/
|
||||
class UserRole extends RoleBase
|
||||
{
|
||||
/**
|
||||
* @var string The default role code.
|
||||
*/
|
||||
const DEFAULT_CODE = 'default';
|
||||
const CODE_DEVELOPER = 'developer';
|
||||
const CODE_PUBLISHER = 'publisher';
|
||||
|
||||
/**
|
||||
* @var string The database table used by the model.
|
||||
|
|
@ -25,6 +24,7 @@ class UserRole extends RoleBase
|
|||
*/
|
||||
public $rules = [
|
||||
'name' => 'required|between:2,128|unique:backend_user_roles',
|
||||
'code' => 'unique:backend_user_roles',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -34,4 +34,48 @@ class UserRole extends RoleBase
|
|||
'users' => [User::class, 'key' => 'role_id'],
|
||||
'users_count' => [User::class, 'key' => 'role_id', 'count' => true]
|
||||
];
|
||||
|
||||
public function filterFields($fields)
|
||||
{
|
||||
if ($this->is_system) {
|
||||
$fields->code->disabled = true;
|
||||
$fields->permissions->disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function afterFetch()
|
||||
{
|
||||
if ($this->is_system) {
|
||||
$this->permissions = $this->getDefaultPermissions();
|
||||
}
|
||||
}
|
||||
|
||||
public function beforeSave()
|
||||
{
|
||||
if ($this->isSystemRole()) {
|
||||
$this->is_system = true;
|
||||
$this->permissions = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function isSystemRole()
|
||||
{
|
||||
if (!$this->code || !strlen(trim($this->code))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->is_system || in_array($this->code, [
|
||||
self::CODE_DEVELOPER,
|
||||
self::CODE_PUBLISHER
|
||||
])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return AuthManager::instance()->hasPermissionsForRole($this->code);
|
||||
}
|
||||
|
||||
public function getDefaultPermissions()
|
||||
{
|
||||
return AuthManager::instance()->listPermissionsForRole($this->code);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,16 @@
|
|||
columns:
|
||||
name:
|
||||
label: backend::lang.user.group.name_field
|
||||
searchable: yes
|
||||
searchable: true
|
||||
|
||||
code:
|
||||
label: backend::lang.user.group.code_field
|
||||
searchable: true
|
||||
invisible: true
|
||||
|
||||
description:
|
||||
label: backend::lang.user.group.description_field
|
||||
searchable: yes
|
||||
searchable: true
|
||||
|
||||
users_count:
|
||||
label: backend::lang.user.group.users_count
|
||||
|
|
|
|||
|
|
@ -5,7 +5,12 @@
|
|||
columns:
|
||||
name:
|
||||
label: backend::lang.user.role.name_field
|
||||
searchable: yes
|
||||
searchable: true
|
||||
|
||||
code:
|
||||
label: backend::lang.user.role.code_field
|
||||
searchable: true
|
||||
invisible: true
|
||||
|
||||
description:
|
||||
label: backend::lang.user.role.description_field
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use Event;
|
|||
use Backend;
|
||||
use BackendMenu;
|
||||
use BackendAuth;
|
||||
use Backend\Models\UserRole;
|
||||
use Backend\Classes\WidgetManager;
|
||||
use October\Rain\Support\ModuleServiceProvider;
|
||||
use System\Classes\SettingsManager;
|
||||
|
|
@ -123,6 +124,7 @@ class ServiceProvider extends ModuleServiceProvider
|
|||
'icon' => 'icon-magic',
|
||||
'iconSvg' => 'modules/cms/assets/images/cms-icon.svg',
|
||||
'url' => Backend::url('cms'),
|
||||
'order' => 100,
|
||||
'permissions' => [
|
||||
'cms.manage_content',
|
||||
'cms.manage_assets',
|
||||
|
|
@ -130,7 +132,6 @@ class ServiceProvider extends ModuleServiceProvider
|
|||
'cms.manage_layouts',
|
||||
'cms.manage_partials'
|
||||
],
|
||||
'order' => 100,
|
||||
'sideMenu' => [
|
||||
'pages' => [
|
||||
'label' => 'cms::lang.page.menu_label',
|
||||
|
|
@ -216,31 +217,37 @@ class ServiceProvider extends ModuleServiceProvider
|
|||
'cms.manage_content' => [
|
||||
'label' => 'cms::lang.permissions.manage_content',
|
||||
'tab' => 'cms::lang.permissions.name',
|
||||
'roles' => UserRole::CODE_DEVELOPER,
|
||||
'order' => 100
|
||||
],
|
||||
'cms.manage_assets' => [
|
||||
'label' => 'cms::lang.permissions.manage_assets',
|
||||
'tab' => 'cms::lang.permissions.name',
|
||||
'roles' => UserRole::CODE_DEVELOPER,
|
||||
'order' => 100
|
||||
],
|
||||
'cms.manage_pages' => [
|
||||
'label' => 'cms::lang.permissions.manage_pages',
|
||||
'tab' => 'cms::lang.permissions.name',
|
||||
'roles' => UserRole::CODE_DEVELOPER,
|
||||
'order' => 100
|
||||
],
|
||||
'cms.manage_layouts' => [
|
||||
'label' => 'cms::lang.permissions.manage_layouts',
|
||||
'tab' => 'cms::lang.permissions.name',
|
||||
'roles' => UserRole::CODE_DEVELOPER,
|
||||
'order' => 100
|
||||
],
|
||||
'cms.manage_partials' => [
|
||||
'label' => 'cms::lang.permissions.manage_partials',
|
||||
'tab' => 'cms::lang.permissions.name',
|
||||
'roles' => UserRole::CODE_DEVELOPER,
|
||||
'order' => 100
|
||||
],
|
||||
'cms.manage_themes' => [
|
||||
'label' => 'cms::lang.permissions.manage_themes',
|
||||
'tab' => 'cms::lang.permissions.name',
|
||||
'roles' => UserRole::CODE_DEVELOPER,
|
||||
'order' => 100
|
||||
],
|
||||
'media.manage_media' => [
|
||||
|
|
|
|||
Loading…
Reference in New Issue