new: [user:permissionLimitation] Added current permission status while in `add` or `edit` context

Also moved the notification key from meta-fields to meta-template-fields
refacto/CRUDComponent
Sami Mokaddem 2023-02-24 15:22:18 +01:00
parent aead79a4c3
commit 59f8608d50
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
3 changed files with 192 additions and 124 deletions

View File

@ -120,6 +120,12 @@ class UsersController extends AppController
if (Configure::read('keycloak.enabled')) { if (Configure::read('keycloak.enabled')) {
$this->Users->enrollUserRouter($data); $this->Users->enrollUserRouter($data);
} }
},
'afterFind' => function ($user, &$params) use ($currentUser) {
if (!empty($user)) { // We don't have a 404
$user = $this->fetchTable('PermissionLimitations')->attachLimitations($user);
}
return $user;
} }
]); ]);
$responsePayload = $this->CRUD->getResponsePayload(); $responsePayload = $this->CRUD->getResponsePayload();
@ -227,6 +233,14 @@ class UsersController extends AppController
if (!$this->ACL->canEditUser($currentUser, $user)) { if (!$this->ACL->canEditUser($currentUser, $user)) {
throw new MethodNotAllowedException(__('You cannot edit the given user.')); throw new MethodNotAllowedException(__('You cannot edit the given user.'));
} }
$user = $this->fetchTable('PermissionLimitations')->attachLimitations($user);
}
return $user;
};
} else {
$params['afterFind'] = function ($user, &$params) use ($currentUser) {
if (!empty($user)) { // We don't have a 404
$user = $this->fetchTable('PermissionLimitations')->attachLimitations($user);
} }
return $user; return $user;
}; };

View File

@ -30,13 +30,17 @@ class PermissionLimitationsTable extends AppTable
public function getListOfLimitations(\App\Model\Entity\User $data) public function getListOfLimitations(\App\Model\Entity\User $data)
{ {
$Users = TableRegistry::getTableLocator()->get('Users'); $Users = TableRegistry::getTableLocator()->get('Users');
$ownOrgUserIds = $Users->find('list', [ $includeOrganisationPermissions = !empty($data['organisation_id']);
'keyField' => 'id', $ownOrgUserIds = [];
'valueField' => 'id', if ($includeOrganisationPermissions) {
'conditions' => [ $ownOrgUserIds = $Users->find('list', [
'organisation_id' => $data['organisation_id'] 'keyField' => 'id',
] 'valueField' => 'id',
])->all()->toList(); 'conditions' => [
'organisation_id' => $data['organisation_id']
]
])->all()->toList();
}
$MetaFields = TableRegistry::getTableLocator()->get('MetaFields'); $MetaFields = TableRegistry::getTableLocator()->get('MetaFields');
$raw = $this->find()->select(['scope', 'permission', 'max_occurrence'])->disableHydration()->toArray(); $raw = $this->find()->select(['scope', 'permission', 'max_occurrence'])->disableHydration()->toArray();
$limitations = []; $limitations = [];
@ -70,9 +74,12 @@ class PermissionLimitationsTable extends AppTable
if (!empty($ownOrgUserIds)) { if (!empty($ownOrgUserIds)) {
$conditions['parent_id IN'] = array_values($ownOrgUserIds); $conditions['parent_id IN'] = array_values($ownOrgUserIds);
} }
$limitations[$field]['organisation']['current'] = $MetaFields->find('all', [ $limitations[$field]['organisation']['current'] = '?';
'conditions' => $conditions, if ($includeOrganisationPermissions) {
])->count(); $limitations[$field]['organisation']['current'] = $MetaFields->find('all', [
'conditions' => $conditions,
])->count();
}
} }
} }
return $limitations; return $limitations;
@ -89,34 +96,35 @@ class PermissionLimitationsTable extends AppTable
if (!empty($data['MetaTemplates'])) { if (!empty($data['MetaTemplates'])) {
foreach ($data['MetaTemplates'] as &$metaTemplate) { foreach ($data['MetaTemplates'] as &$metaTemplate) {
foreach ($metaTemplate['meta_template_fields'] as &$meta_template_field) { foreach ($metaTemplate['meta_template_fields'] as &$meta_template_field) {
$boolean = $meta_template_field['type'] === 'boolean'; if (isset($permissionLimitations[$meta_template_field['field']])) {
foreach ($meta_template_field['metaFields'] as &$metaField) { foreach ($permissionLimitations[$meta_template_field['field']] as $scope => $value) {
if (isset($permissionLimitations[$metaField['field']])) { $messageType = 'warning';
foreach ($permissionLimitations[$metaField['field']] as $scope => $value) { if ($value['current'] == '?') {
$messageType = 'warning'; $messageType = 'info';
} else {
if ($value['limit'] > $value['current']) { if ($value['limit'] > $value['current']) {
$messageType = 'info'; $messageType = 'info';
} }
if ($value['limit'] < $value['current']) { if ($value['limit'] < $value['current']) {
$messageType = 'danger'; $messageType = 'danger';
} }
if (empty($metaField[$messageType])) {
$metaField[$messageType] = '';
}
$altText = __(
'There is a limitation enforced on the number of users with this permission {0}. Currently {1} slot(s) are used up of a maximum of {2} slot(s).',
$scope === 'global' ? __('instance wide') : __('for your organisation'),
$value['current'],
$value['limit']
);
$metaField[$messageType] .= sprintf(
' <span title="%s"><span class="text-dark"><i class="fas fa-%s"></i>: </span>%s/%s</span>',
$altText,
$icons[$scope],
$value['current'],
$value['limit']
);
} }
if (empty($metaField[$messageType])) {
$metaField[$messageType] = '';
}
$altText = __(
'There is a limitation enforced on the number of users with this permission {0}. Currently {1} slot(s) are used up of a maximum of {2} slot(s).',
$scope === 'global' ? __('instance wide') : __('for your organisation'),
$value['current'],
$value['limit']
);
$meta_template_field[$messageType] .= sprintf(
' <span title="%s"><span class="text-dark"><i class="fas fa-%s"></i>: </span>%s/%s</span>',
$altText,
$icons[$scope],
$value['current'],
$value['limit']
);
} }
} }
} }

View File

@ -1,99 +1,145 @@
<?php <?php
use Cake\Core\Configure;
$passwordRequired = false; use Cake\Core\Configure;
$showPasswordField = false;
if ($this->request->getParam('action') === 'add') { $passwordRequired = false;
$dropdownData['individual'] = ['new' => __('New individual')] + $dropdownData['individual']; $showPasswordField = false;
if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) { if ($this->request->getParam('action') === 'add') {
$passwordRequired = 'required'; $dropdownData['individual'] = ['new' => __('New individual')] + $dropdownData['individual'];
}
}
if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) { if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) {
$showPasswordField = true; $passwordRequired = 'required';
} }
echo $this->element('genericElements/Form/genericForm', [ }
'data' => [ if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) {
'description' => __('Roles define global rules for a set of users, including first and foremost access controls to certain functionalities.'), $showPasswordField = true;
'model' => 'Roles', }
'fields' => [ echo $this->element('genericElements/Form/genericForm', [
[ 'data' => [
'field' => 'individual_id', 'description' => __('Roles define global rules for a set of users, including first and foremost access controls to certain functionalities.'),
'type' => 'dropdown', 'model' => 'Roles',
'label' => __('Associated individual'), 'fields' => [
'options' => isset($dropdownData['individual']) ? $dropdownData['individual'] : [], [
'conditions' => $this->request->getParam('action') === 'add' 'field' => 'individual_id',
], 'type' => 'dropdown',
[ 'label' => __('Associated individual'),
'field' => 'individual.email', 'options' => isset($dropdownData['individual']) ? $dropdownData['individual'] : [],
'stateDependence' => [ 'conditions' => $this->request->getParam('action') === 'add'
'source' => '#individual_id-field',
'option' => 'new'
],
'required' => false
],
[
'field' => 'individual.first_name',
'label' => 'First name',
'stateDependence' => [
'source' => '#individual_id-field',
'option' => 'new'
],
'required' => false
],
[
'field' => 'individual.last_name',
'label' => 'Last name',
'stateDependence' => [
'source' => '#individual_id-field',
'option' => 'new'
],
'required' => false
],
[
'field' => 'username',
'autocomplete' => 'off'
],
[
'field' => 'organisation_id',
'type' => 'dropdown',
'label' => __('Associated organisation'),
'options' => $dropdownData['organisation'],
'default' => $loggedUser['organisation_id']
],
[
'field' => 'password',
'label' => __('Password'),
'type' => 'password',
'required' => $passwordRequired,
'autocomplete' => 'new-password',
'value' => '',
'requirements' => $showPasswordField,
],
[
'field' => 'confirm_password',
'label' => __('Confirm Password'),
'type' => 'password',
'required' => $passwordRequired,
'autocomplete' => 'off',
'requirements' => $showPasswordField,
],
[
'field' => 'role_id',
'type' => 'dropdown',
'label' => __('Role'),
'options' => $dropdownData['role'],
'default' => $defaultRole ?? null
],
[
'field' => 'disabled',
'type' => 'checkbox',
'label' => 'Disable'
],
], ],
'submit' => [ [
'action' => $this->request->getParam('action') 'field' => 'individual.email',
] 'stateDependence' => [
'source' => '#individual_id-field',
'option' => 'new'
],
'required' => false
],
[
'field' => 'individual.first_name',
'label' => 'First name',
'stateDependence' => [
'source' => '#individual_id-field',
'option' => 'new'
],
'required' => false
],
[
'field' => 'individual.last_name',
'label' => 'Last name',
'stateDependence' => [
'source' => '#individual_id-field',
'option' => 'new'
],
'required' => false
],
[
'field' => 'username',
'autocomplete' => 'off'
],
[
'field' => 'organisation_id',
'type' => 'dropdown',
'label' => __('Associated organisation'),
'options' => $dropdownData['organisation'],
'default' => $loggedUser['organisation_id']
],
[
'field' => 'password',
'label' => __('Password'),
'type' => 'password',
'required' => $passwordRequired,
'autocomplete' => 'new-password',
'value' => '',
'requirements' => $showPasswordField,
],
[
'field' => 'confirm_password',
'label' => __('Confirm Password'),
'type' => 'password',
'required' => $passwordRequired,
'autocomplete' => 'off',
'requirements' => $showPasswordField,
],
[
'field' => 'role_id',
'type' => 'dropdown',
'label' => __('Role'),
'options' => $dropdownData['role'],
'default' => $defaultRole ?? null
],
[
'field' => 'disabled',
'type' => 'checkbox',
'label' => 'Disable'
],
],
'submit' => [
'action' => $this->request->getParam('action')
] ]
]); ]
]);
?> ?>
</div>
<script>
$(document).ready(function() {
const entity = <?= json_encode($entity) ?>;
console.log(entity);
if (entity.MetaTemplates) {
for (const [metaTemplateId, metaTemplate] of Object.entries(entity.MetaTemplates)) {
for (const [metaTemplateFieldId, metaTemplateField] of Object.entries(metaTemplate.meta_template_fields)) {
let metaFieldId = false
if (metaTemplateField.metaFields !== undefined && Object.keys(metaTemplateField.metaFields).length > 0) {
metaFieldId = Object.keys(metaTemplateField.metaFields)[0]
}
let metafieldInput
const baseQueryPath = `MetaTemplates.${metaTemplateId}.meta_template_fields.${metaTemplateFieldId}.metaFields`
if (metaFieldId) {
metafieldInput = document.getElementById(`${baseQueryPath}.${metaFieldId}.value-field`)
} else {
metafieldInput = document.getElementById(`${baseQueryPath}.new.0-field`)
}
if (metafieldInput !== null) {
const permissionWarnings = buildPermissionElement(metaTemplateField)
$(metafieldInput.parentElement).append(permissionWarnings)
}
}
}
}
function buildPermissionElement(metaTemplateField) {
const warningTypes = ['danger', 'warning', 'info', ]
const $span = $('<span>').addClass('ms-2')
warningTypes.forEach(warningType => {
if (metaTemplateField[warningType]) {
$theWarning = $('<span>')
.addClass([
`text-${warningType}`,
'ms-1',
])
.append($(metaTemplateField[warningType]))
$span.append($theWarning)
}
});
return $span
}
})
</script>