new: [user:permissionLimitation] Added current permission status while in `add` or `edit` context
Also moved the notification key from meta-fields to meta-template-fieldsrefacto/CRUDComponent
parent
aead79a4c3
commit
59f8608d50
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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']
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue