Merge branch 'develop'
commit
218f544b07
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Migrations\AbstractMigration;
|
||||
use Phinx\Db\Adapter\MysqlAdapter;
|
||||
|
||||
final class OrgGrouping extends AbstractMigration
|
||||
{
|
||||
public $autoId = false; // turn off automatic `id` column create. We want it to be `int(10) unsigned`
|
||||
|
||||
/**
|
||||
* Change Method.
|
||||
*
|
||||
* Write your reversible migrations using this method.
|
||||
*
|
||||
* More information on writing migrations is available here:
|
||||
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
|
||||
*
|
||||
* Remember to call "create()" or "update()" and NOT "save()" when working
|
||||
* with the Table class.
|
||||
*/
|
||||
public function change(): void
|
||||
{
|
||||
$exists = $this->hasTable('org_groups');
|
||||
if (!$exists) {
|
||||
$orgGroupsTable = $this->table('org_groups', [
|
||||
'signed' => false,
|
||||
'collation' => 'utf8mb4_unicode_ci'
|
||||
]);
|
||||
$orgGroupsTable
|
||||
->addColumn('id', 'integer', [
|
||||
'autoIncrement' => true,
|
||||
'limit' => 10,
|
||||
'signed' => false,
|
||||
])
|
||||
->addPrimaryKey('id')
|
||||
->addColumn('uuid', 'string', [
|
||||
'null' => false,
|
||||
'limit' => 40,
|
||||
'collation' => 'ascii_general_ci',
|
||||
'encoding' => 'ascii',
|
||||
])
|
||||
->addColumn('name', 'string', [
|
||||
'null' => false,
|
||||
'limit' => 191,
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
'encoding' => 'utf8mb4',
|
||||
])
|
||||
->addColumn('description', 'text', [
|
||||
'default' => null,
|
||||
'null' => true
|
||||
])
|
||||
->addColumn('created', 'datetime', [
|
||||
'null' => false
|
||||
])
|
||||
->addColumn('modified', 'datetime', [
|
||||
'null' => false
|
||||
])
|
||||
->addIndex('name')
|
||||
->addIndex('uuid', ['unique' => true]);
|
||||
|
||||
$orgGroupsTable->create();
|
||||
}
|
||||
|
||||
|
||||
$exists = $this->hasTable('org_groups_organisations');
|
||||
if (!$exists) {
|
||||
$orgGroupsTable = $this->table('org_groups_organisations', [
|
||||
'signed' => false,
|
||||
'collation' => 'utf8mb4_unicode_ci'
|
||||
]);
|
||||
$orgGroupsTable
|
||||
->addColumn('id', 'integer', [
|
||||
'autoIncrement' => true,
|
||||
'limit' => 10,
|
||||
'signed' => false,
|
||||
])
|
||||
->addPrimaryKey('id')
|
||||
->addColumn('org_group_id', 'integer', [
|
||||
'limit' => 10,
|
||||
'signed' => false,
|
||||
])
|
||||
->addIndex('org_group_id')
|
||||
->addColumn('organisation_id', 'integer', [
|
||||
'limit' => 10,
|
||||
'signed' => false,
|
||||
])
|
||||
->addIndex('organisation_id')
|
||||
->addIndex(['org_group_id', 'organisation_id'], ['unique' => true]);
|
||||
|
||||
$orgGroupsTable->create();
|
||||
}
|
||||
|
||||
|
||||
$exists = $this->hasTable('org_groups_admins');
|
||||
if (!$exists) {
|
||||
$orgGroupsTable = $this->table('org_groups_admins', [
|
||||
'signed' => false,
|
||||
'collation' => 'utf8mb4_unicode_ci'
|
||||
]);
|
||||
$orgGroupsTable
|
||||
->addColumn('id', 'integer', [
|
||||
'autoIncrement' => true,
|
||||
'limit' => 10,
|
||||
'signed' => false,
|
||||
])
|
||||
->addPrimaryKey('id')
|
||||
->addColumn('org_group_id', 'integer', [
|
||||
'limit' => 10,
|
||||
'signed' => false,
|
||||
])
|
||||
->addIndex('org_group_id')
|
||||
->addColumn('user_id', 'integer', [
|
||||
'limit' => 10,
|
||||
'signed' => false,
|
||||
])
|
||||
->addIndex('user_id')
|
||||
->addIndex(['org_group_id', 'user_id'], ['unique' => true]);
|
||||
|
||||
$orgGroupsTable->create();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Migrations\AbstractMigration;
|
||||
use Phinx\Db\Adapter\MysqlAdapter;
|
||||
|
||||
final class GroupAdminRole extends AbstractMigration
|
||||
{
|
||||
public $autoId = false; // turn off automatic `id` column create. We want it to be `int(10) unsigned`
|
||||
|
||||
/**
|
||||
* Change Method.
|
||||
*
|
||||
* Write your reversible migrations using this method.
|
||||
*
|
||||
* More information on writing migrations is available here:
|
||||
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
|
||||
*
|
||||
* Remember to call "create()" or "update()" and NOT "save()" when working
|
||||
* with the Table class.
|
||||
*/
|
||||
public function change(): void
|
||||
{
|
||||
$exists = $this->table('roles')->hasColumn('perm_group_admin');
|
||||
if (!$exists) {
|
||||
$this->table('roles')
|
||||
->addColumn('perm_group_admin', 'boolean', [
|
||||
'default' => 0,
|
||||
'null' => false,
|
||||
])
|
||||
->addIndex('perm_group_admin')
|
||||
->update();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,8 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
'data' => [
|
||||
'type' => 'simple',
|
||||
'text' => __('Add tag'),
|
||||
'popover_url' => '/tags/add'
|
||||
'popover_url' => '/tags/add',
|
||||
'requirement' => !empty($loggedUser['role']['perm_admin']),
|
||||
]
|
||||
]
|
||||
],
|
||||
|
@ -65,12 +66,14 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
[
|
||||
'open_modal' => '/tags/edit/[onclick_params_data_path]',
|
||||
'modal_params_data_path' => 'id',
|
||||
'icon' => 'edit'
|
||||
'icon' => 'edit',
|
||||
'requirement' => !empty($loggedUser['role']['perm_admin']),
|
||||
],
|
||||
[
|
||||
'open_modal' => '/tags/delete/[onclick_params_data_path]',
|
||||
'modal_params_data_path' => 'id',
|
||||
'icon' => 'trash'
|
||||
'icon' => 'trash',
|
||||
'requirement' => !empty($loggedUser['role']['perm_admin']),
|
||||
],
|
||||
]
|
||||
]
|
||||
|
|
|
@ -65,7 +65,8 @@ class SummaryCommand extends Command
|
|||
$folderPath = rtrim($folderPath, '/');
|
||||
$filename = sprintf('%s/%s.txt', $folderPath, $nationality);
|
||||
$file_input = fopen($filename, 'w');
|
||||
$organisationIDsForNationality = $this->_fetchOrganisationsForNationality($nationality);
|
||||
$organisationForNationality = $this->_fetchOrganisationsForNationality($nationality);
|
||||
$organisationIDsForNationality = array_keys($organisationForNationality);
|
||||
if (empty($organisationIDsForNationality)) {
|
||||
$message = sprintf('No changes for organisations with nationality `%s`', $nationality);
|
||||
fwrite($file_input, $message);
|
||||
|
@ -73,6 +74,7 @@ class SummaryCommand extends Command
|
|||
return;
|
||||
}
|
||||
$userForOrg = $this->_fetchUserForOrg($organisationIDsForNationality);
|
||||
$userEmailByID = Hash::combine($userForOrg, '{n}.id', '{n}.individual.email');
|
||||
$userID = Hash::extract($userForOrg, '{n}.id');
|
||||
$individualID = Hash::extract($userForOrg, '{n}.individual_id');
|
||||
|
||||
|
@ -80,9 +82,16 @@ class SummaryCommand extends Command
|
|||
fwrite($file_input, $message);
|
||||
$this->io->out($message);
|
||||
$logsUsers = $this->_fetchLogsForUsers($userID, $days);
|
||||
$logsUsers = array_map(function($log) use ($userEmailByID) {
|
||||
$userID = $log['model_id'];
|
||||
$log['element_id'] = $userID;
|
||||
$log['element_display_field'] = $userEmailByID[$userID];
|
||||
return $log;
|
||||
}, $logsUsers);
|
||||
|
||||
$userByIDs = Hash::combine($userForOrg, '{n}.id', '{n}');
|
||||
$logsUserMetaFields = $this->_fetchLogsForUserMetaFields($userID, $days);
|
||||
$logsUserMetaFields = $this->_formatUserMetafieldLogs($logsUserMetaFields, $userByIDs);
|
||||
$logsUserMetaFields = $this->_formatUserMetafieldLogs($logsUserMetaFields, $userEmailByID);
|
||||
$logsUsersCombined = array_merge($logsUsers, $logsUserMetaFields);
|
||||
usort($logsUsersCombined, function($a, $b) {
|
||||
return $a['created'] < $b['created'] ? -1 : 1;
|
||||
|
@ -97,6 +106,12 @@ class SummaryCommand extends Command
|
|||
fwrite($file_input, $message);
|
||||
$this->io->out($message);
|
||||
$logsOrgs = $this->_fetchLogsForOrgs($organisationIDsForNationality, $days);
|
||||
$logsOrgs = array_map(function ($log) use ($organisationIDsForNationality) {
|
||||
$orgID = $log['model_id'];
|
||||
$log['element_id'] = $orgID;
|
||||
$log['element_display_field'] = $organisationIDsForNationality[$orgID];
|
||||
return $log;
|
||||
}, $logsOrgs);
|
||||
$modifiedOrgs = $this->_formatLogsForTable($logsOrgs);
|
||||
foreach ($modifiedOrgs as $row) {
|
||||
fputcsv($file_input, $row);
|
||||
|
@ -107,6 +122,12 @@ class SummaryCommand extends Command
|
|||
fwrite($file_input, $message);
|
||||
$this->io->out($message);
|
||||
$logsIndividuals = $this->_fetchLogsForIndividuals($individualID, $days);
|
||||
$logsIndividuals = array_map(function ($log) use ($userEmailByID) {
|
||||
$individualID = $log['model_id'];
|
||||
$log['element_id'] = $individualID;
|
||||
$log['element_display_field'] = $userEmailByID[$individualID];
|
||||
return $log;
|
||||
}, $logsIndividuals);
|
||||
$modifiedIndividuals = $this->_formatLogsForTable($logsIndividuals);
|
||||
foreach ($modifiedIndividuals as $row) {
|
||||
fputcsv($file_input, $row);
|
||||
|
@ -125,12 +146,18 @@ class SummaryCommand extends Command
|
|||
|
||||
protected function _fetchOrganisationsForNationality(string $nationality): array
|
||||
{
|
||||
return array_keys($this->Organisations->find('list')
|
||||
return $this->Organisations->find('list')
|
||||
->where([
|
||||
'nationality' => $nationality,
|
||||
])
|
||||
->all()
|
||||
->toArray());
|
||||
->toArray();
|
||||
// return array_keys($this->Organisations->find('list')
|
||||
// ->where([
|
||||
// 'nationality' => $nationality,
|
||||
// ])
|
||||
// ->all()
|
||||
// ->toArray());
|
||||
}
|
||||
|
||||
protected function _fetchOrgNationalities(): array
|
||||
|
@ -139,6 +166,7 @@ class SummaryCommand extends Command
|
|||
->where([
|
||||
'nationality !=' => '',
|
||||
])
|
||||
->group('nationality')
|
||||
->all()
|
||||
->extract('nationality')
|
||||
->toList();
|
||||
|
@ -190,9 +218,14 @@ class SummaryCommand extends Command
|
|||
$metaFieldLogs = array_filter($logs, function ($log) use ($userIDs) {
|
||||
return !empty($log['changed']['scope']) && $log['changed']['scope'] === 'user' && in_array($log['changed']['parent_id'], $userIDs);
|
||||
});
|
||||
$metaFieldDeletionLogs = array_filter($logs, function ($log) use ($userIDs) {
|
||||
$metaFieldLogs = array_map(function ($log) {
|
||||
$log['modified_user_id'] = $log['changed']['parent_id'];
|
||||
return $log;
|
||||
}, $metaFieldLogs);
|
||||
$metaFieldDeletionLogs = array_filter($logs, function ($log) {
|
||||
return $log['request_action'] === 'delete';
|
||||
});
|
||||
$allLogs = $metaFieldLogs;
|
||||
foreach ($metaFieldDeletionLogs as $i => $log) {
|
||||
$latestAssociatedLog = $this->_fetchLogs([
|
||||
'contain' => ['Users'],
|
||||
|
@ -205,11 +238,14 @@ class SummaryCommand extends Command
|
|||
'limit' => 1,
|
||||
]);
|
||||
if (!empty($latestAssociatedLog)) {
|
||||
$metaFieldDeletionLogs[$i]['changed']['orig_value'] = $latestAssociatedLog[0]['changed']['value'];
|
||||
$metaFieldDeletionLogs[$i]['changed']['value'] = '';
|
||||
if (in_array($latestAssociatedLog[0]['changed']['parent_id'], $userIDs)) {
|
||||
$log['changed']['orig_value'] = $latestAssociatedLog[0]['changed']['value'];
|
||||
$log['changed']['value'] = '';
|
||||
$log['modified_user_id'] = $latestAssociatedLog[0]['changed']['parent_id'];
|
||||
$allLogs[] = $log;
|
||||
}
|
||||
}
|
||||
}
|
||||
$allLogs = array_merge($metaFieldLogs, $metaFieldDeletionLogs);
|
||||
return $allLogs;
|
||||
}
|
||||
|
||||
|
@ -268,9 +304,9 @@ class SummaryCommand extends Command
|
|||
}, $logs);
|
||||
}
|
||||
|
||||
protected function _formatUserMetafieldLogs($logEntries, $userByIDs): array
|
||||
protected function _formatUserMetafieldLogs($logEntries, $userEmailByID): array
|
||||
{
|
||||
return array_map(function($log) use ($userByIDs) {
|
||||
return array_map(function($log) use ($userEmailByID) {
|
||||
$log['model'] = 'Users';
|
||||
$log['request_action'] = 'edit';
|
||||
$log['changed'] = [
|
||||
|
@ -279,13 +315,15 @@ class SummaryCommand extends Command
|
|||
$log['changed']['value']
|
||||
]
|
||||
];
|
||||
$log['element_id'] = $log['modified_user_id'];
|
||||
$log['element_display_field'] = $userEmailByID[$log['modified_user_id']];
|
||||
return $log;
|
||||
}, $logEntries);
|
||||
}
|
||||
|
||||
protected function _formatLogsForTable($logEntries): array
|
||||
{
|
||||
$header = ['Model', 'Action', 'Editor user', 'Log ID', 'Datetime', 'Change'];
|
||||
$header = ['Model', 'Action', 'Editor user', 'Log ID', 'Datetime', 'Modified element ID', 'Modified element', 'Change'];
|
||||
$data = [$header];
|
||||
foreach ($logEntries as $logEntry) {
|
||||
$formatted = [
|
||||
|
@ -294,6 +332,8 @@ class SummaryCommand extends Command
|
|||
sprintf('%s (%s)', $logEntry['user']['username'], $logEntry['user_id']),
|
||||
$logEntry['id'],
|
||||
$logEntry['created']->i18nFormat('yyyy-MM-dd HH:mm:ss'),
|
||||
$logEntry['element_id'] ?? '-',
|
||||
$logEntry['element_display_field'] ?? '-',
|
||||
];
|
||||
if ($logEntry['request_action'] == 'edit') {
|
||||
$formatted[] = json_encode($logEntry['changed'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
|
|
@ -49,6 +49,9 @@ class AlignmentsController extends AppController
|
|||
throw new NotFoundException(__('Invalid alignment.'));
|
||||
}
|
||||
$alignment = $this->Alignments->get($id);
|
||||
if (!$this->canEditIndividual($alignment->individual_id) || !$this->canEditOrganisation($alignment->organisation_id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot delete this alignments.'));
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('delete')) {
|
||||
if ($this->Alignments->delete($alignment)) {
|
||||
$message = __('Alignments deleted.');
|
||||
|
@ -73,8 +76,21 @@ class AlignmentsController extends AppController
|
|||
if (empty($scope) || empty($source_id)) {
|
||||
throw new NotAcceptableException(__('Invalid input. scope and source_id expected as URL parameters in the format /alignments/add/[scope]/[source_id].'));
|
||||
}
|
||||
if (!in_array($scope, ['individuals', 'organisations'])) {
|
||||
throw new MethodNotAllowedException(__('Invalid scope. Should be `individuals` or `organisations`.'));
|
||||
}
|
||||
$this->loadModel('Individuals');
|
||||
$this->loadModel('Organisations');
|
||||
|
||||
$validIndividualIDs = $this->Individuals->getValidIndividualsToEdit($this->ACL->getUser());
|
||||
$validOrgs = $this->Organisations->getEditableOrganisationsForUser($this->ACL->getUser());
|
||||
|
||||
if ($scope == 'individuals' && !$this->canEditIndividual($source_id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot modify that individual.'));
|
||||
} else if ($scope == 'organisations' && !$this->canEditOrganisation($source_id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot modify that organisation.'));
|
||||
}
|
||||
|
||||
$alignment = $this->Alignments->newEmptyEntity();
|
||||
if ($this->request->is('post')) {
|
||||
$this->Alignments->patchEntity($alignment, $this->request->getData());
|
||||
|
@ -83,6 +99,11 @@ class AlignmentsController extends AppController
|
|||
} else {
|
||||
$alignment['organisation_id'] = $source_id;
|
||||
}
|
||||
if ($scope == 'individuals' && !$this->canEditOrganisation($alignment['organisation_id'])) {
|
||||
throw new MethodNotAllowedException(__('You cannot use that organisation.'));
|
||||
} else if ($scope == 'organisations' && !$this->canEditIndividual($alignment['individual_id'])) {
|
||||
throw new MethodNotAllowedException(__('You cannot assign that individual.'));
|
||||
}
|
||||
$alignment = $this->Alignments->save($alignment);
|
||||
if ($alignment) {
|
||||
$message = __('Alignment added.');
|
||||
|
@ -105,7 +126,7 @@ class AlignmentsController extends AppController
|
|||
}
|
||||
}
|
||||
if ($scope === 'organisations') {
|
||||
$individuals = $this->Individuals->find('list', ['valueField' => 'email'])->toArray();
|
||||
$individuals = $this->Individuals->find('list', ['valueField' => 'email'])->where(['id IN' => $validIndividualIDs])->toArray();
|
||||
$this->set('individuals', $individuals);
|
||||
$organisation = $this->Organisations->find()->where(['id' => $source_id])->first();
|
||||
if (empty($organisation)) {
|
||||
|
@ -113,7 +134,7 @@ class AlignmentsController extends AppController
|
|||
}
|
||||
$this->set(compact('organisation'));
|
||||
} else {
|
||||
$organisations = $this->Organisations->find('list', ['valueField' => 'name'])->toArray();
|
||||
$organisations = Hash::combine($validOrgs, '{n}.id', '{n}.name');
|
||||
$this->set('organisations', $organisations);
|
||||
$individual = $this->Individuals->find()->where(['id' => $source_id])->first();
|
||||
if (empty($individual)) {
|
||||
|
@ -124,6 +145,31 @@ class AlignmentsController extends AppController
|
|||
$this->set(compact('alignment'));
|
||||
$this->set('scope', $scope);
|
||||
$this->set('source_id', $source_id);
|
||||
$this->set('metaGroup', 'ContactDB');
|
||||
}
|
||||
|
||||
private function canEditIndividual($indId): bool
|
||||
{
|
||||
$currentUser = $this->ACL->getUser();
|
||||
if ($currentUser['role']['perm_admin']) {
|
||||
return true;
|
||||
}
|
||||
$this->loadModel('Individuals');
|
||||
$validIndividuals = $this->Individuals->getValidIndividualsToEdit($currentUser);
|
||||
if (in_array($indId, $validIndividuals)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function canEditOrganisation($orgId): bool
|
||||
{
|
||||
$currentUser = $this->ACL->getUser();
|
||||
if ($currentUser['role']['perm_admin']) {
|
||||
return true;
|
||||
}
|
||||
if ($currentUser['role']['perm_org_admin'] && $currentUser['organisation']['id'] == $orgId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,8 +41,8 @@ class ACLComponent extends Component
|
|||
'queryACL' => ['perm_admin']
|
||||
],
|
||||
'Alignments' => [
|
||||
'add' => ['perm_admin'],
|
||||
'delete' => ['perm_admin'],
|
||||
'add' => ['perm_admin', 'perm_org_admin'],
|
||||
'delete' => ['perm_admin', 'perm_org_admin'],
|
||||
'index' => ['*'],
|
||||
'view' => ['*']
|
||||
],
|
||||
|
@ -96,7 +96,7 @@ class ACLComponent extends Component
|
|||
'view' => ['perm_admin'],
|
||||
],
|
||||
'Individuals' => [
|
||||
'add' => ['perm_admin'],
|
||||
'add' => ['perm_admin', 'perm_org_admin'],
|
||||
'delete' => ['perm_admin'],
|
||||
'edit' => ['perm_admin', 'perm_org_admin'],
|
||||
'filtering' => ['*'],
|
||||
|
@ -161,10 +161,27 @@ class ACLComponent extends Component
|
|||
'MetaTemplateNameDirectory' => [
|
||||
'index' => ['perm_admin'],
|
||||
],
|
||||
'Organisations' => [
|
||||
'OrgGroups' => [
|
||||
'add' => ['perm_admin'],
|
||||
'delete' => ['perm_admin'],
|
||||
'edit' => ['perm_admin'],
|
||||
'index' => ['*'],
|
||||
'view' => ['*'],
|
||||
'filtering' => ['*'],
|
||||
'tag' => ['perm_admin'],
|
||||
'untag' => ['perm_admin'],
|
||||
'viewTags' => ['*'],
|
||||
'listAdmins' => ['*'],
|
||||
'listOrgs' => ['*'],
|
||||
'assignAdmin' => ['perm_admin'],
|
||||
'removeAdmin' => ['perm_admin'],
|
||||
'attachOrg' => ['perm_group_admin'],
|
||||
'detachOrg' => ['perm_group_admin']
|
||||
],
|
||||
'Organisations' => [
|
||||
'add' => ['perm_admin'],
|
||||
'delete' => ['perm_admin'],
|
||||
'edit' => ['perm_admin', 'perm_org_admin'],
|
||||
'filtering' => ['*'],
|
||||
'index' => ['*'],
|
||||
'tag' => ['perm_org_admin'],
|
||||
|
@ -208,6 +225,13 @@ class ACLComponent extends Component
|
|||
'removeOrg' => ['perm_org_admin'],
|
||||
'view' => ['*']
|
||||
],
|
||||
'Tags' => [
|
||||
'add' => ['perm_admin'],
|
||||
'delete' => ['perm_admin'],
|
||||
'edit' => ['perm_admin'],
|
||||
'index' => ['*'],
|
||||
'view' => ['*']
|
||||
],
|
||||
'Users' => [
|
||||
'add' => ['perm_org_admin'],
|
||||
'delete' => ['perm_org_admin'],
|
||||
|
@ -328,13 +352,25 @@ class ACLComponent extends Component
|
|||
if (empty($user) || empty($currentUser)) {
|
||||
return false;
|
||||
}
|
||||
if ($user['id'] === $currentUser['id']) {
|
||||
return true;
|
||||
}
|
||||
if (!$currentUser['role']['perm_admin']) {
|
||||
if ($user['role']['perm_admin']) {
|
||||
return false; // org_admins cannot edit admins
|
||||
}
|
||||
if ($currentUser['role']['perm_group_admin']) {
|
||||
$this->OrgGroup = TableRegistry::get('OrgGroup');
|
||||
if ($this->OrgGroup->checkIfUserBelongsToGroupAdminsGroup($currentUser, $user)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!$currentUser['role']['perm_org_admin']) {
|
||||
return false;
|
||||
} else {
|
||||
if ($currentUser['id'] == $user['id']) {
|
||||
return true;
|
||||
}
|
||||
if ($currentUser['organisation_id'] !== $user['organisation_id']) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1711,4 +1711,132 @@ class CRUDComponent extends Component
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function linkObjects(string $functionName, int $id1, string $model1, string $model2, array $params = []): void
|
||||
{
|
||||
$this->Controller->loadModel($model1);
|
||||
$this->Controller->loadModel($model2);
|
||||
if ($this->request->is(['post', 'put'])) {
|
||||
$input = $this->request->getData();
|
||||
if (empty($input['id'])) {
|
||||
throw new MethodNotAllowedException(__('No ID of target to attach defined.'));
|
||||
}
|
||||
$id2 = $input['id'];
|
||||
$obj1 = $this->Table->get($id1);
|
||||
$obj2 = $this->Table->$model2->get($id2);
|
||||
$data = [
|
||||
[
|
||||
'id' => $id1,
|
||||
'model' => $model1
|
||||
],
|
||||
[
|
||||
'id' => $id2,
|
||||
'model' => $model2
|
||||
]
|
||||
];
|
||||
|
||||
try {
|
||||
$savedData = $this->Table->$model2->link($obj1, [$obj2]);
|
||||
} catch (Exception $e) {
|
||||
$savedData = null;
|
||||
}
|
||||
|
||||
if (!empty($savedData)) {
|
||||
$message = __('{0} attached to {1}.', $model1, $model2);
|
||||
if ($this->Controller->ParamHandler->isRest()) {
|
||||
$this->Controller->restResponsePayload = $this->RestResponse->viewData($message, 'json');
|
||||
} else if ($this->Controller->ParamHandler->isAjax()) {
|
||||
if (!empty($params['displayOnSuccess'])) {
|
||||
$displayOnSuccess = $this->renderViewInVariable($params['displayOnSuccess'], ['entity' => $data]);
|
||||
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($model1, 'index', $obj1, $message, ['displayOnSuccess' => $displayOnSuccess]);
|
||||
} else {
|
||||
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($model1, 'index', $obj1, $message);
|
||||
}
|
||||
} else {
|
||||
$this->Controller->Flash->success($message);
|
||||
if (empty($params['redirect'])) {
|
||||
$this->Controller->redirect(['action' => 'view', $id1]);
|
||||
} else {
|
||||
$this->Controller->redirect($params['redirect']);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->Controller->isFailResponse = true;
|
||||
$message = __(
|
||||
'{0} could not be attached to {1}.',
|
||||
$model1,
|
||||
$model2
|
||||
);
|
||||
if ($this->Controller->ParamHandler->isRest()) {
|
||||
$this->Controller->restResponsePayload = $this->RestResponse->viewData($message, 'json');
|
||||
} else if ($this->Controller->ParamHandler->isAjax()) {
|
||||
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxFailResponse($model1, $functionName, $data, $message, []);
|
||||
} else {
|
||||
$this->Controller->Flash->error($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function unlinkObjects(string $functionName, int $id1, int $id2, string $model1, string $model2, array $params = []): void
|
||||
{
|
||||
$this->Controller->loadModel($model1);
|
||||
$this->Controller->loadModel($model2);
|
||||
$obj1 = $this->Table->get($id1);
|
||||
$obj2 = $this->Table->$model2->get($id2);
|
||||
$data = [
|
||||
[
|
||||
'id' => $id1,
|
||||
'model' => $model1
|
||||
],
|
||||
[
|
||||
'id' => $id2,
|
||||
'model' => $model2
|
||||
]
|
||||
];
|
||||
$this->Controller->set('data', $data);
|
||||
if ($this->request->is(['post', 'put'])) {
|
||||
$input = $this->request->getData();
|
||||
try {
|
||||
$savedData = $this->Table->$model2->unlink($obj1, [$obj2]);
|
||||
} catch (Exception $e) {
|
||||
$savedData = null;
|
||||
}
|
||||
|
||||
if (!empty($savedData)) {
|
||||
$message = __('{0} detached from {1}.', $model1, $model2);
|
||||
if ($this->Controller->ParamHandler->isRest()) {
|
||||
$this->Controller->restResponsePayload = $this->RestResponse->viewData($message, 'json');
|
||||
} else if ($this->Controller->ParamHandler->isAjax()) {
|
||||
if (!empty($params['displayOnSuccess'])) {
|
||||
$displayOnSuccess = $this->renderViewInVariable($params['displayOnSuccess'], ['entity' => $data]);
|
||||
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($model1, 'index', $obj1, $message, ['displayOnSuccess' => $displayOnSuccess]);
|
||||
} else {
|
||||
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxSuccessResponse($model1, 'index', $obj1, $message);
|
||||
}
|
||||
} else {
|
||||
$this->Controller->Flash->success($message);
|
||||
if (empty($params['redirect'])) {
|
||||
$this->Controller->redirect(['action' => 'view', $id1]);
|
||||
} else {
|
||||
$this->Controller->redirect($params['redirect']);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->Controller->isFailResponse = true;
|
||||
$message = __(
|
||||
'{0} could not be detached from {1}.',
|
||||
$model1,
|
||||
$model2
|
||||
);
|
||||
if ($this->Controller->ParamHandler->isRest()) {
|
||||
$this->Controller->restResponsePayload = $this->RestResponse->viewData($message, 'json');
|
||||
} else if ($this->Controller->ParamHandler->isAjax()) {
|
||||
$this->Controller->ajaxResponsePayload = $this->RestResponse->ajaxFailResponse($model1, $functionName, $data, $message, []);
|
||||
} else {
|
||||
$this->Controller->Flash->error($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,4 +5,21 @@ require_once(APP . 'Controller' . DS . 'Component' . DS . 'Navigation' . DS . 'b
|
|||
|
||||
class IndividualsNavigation extends BaseNavigation
|
||||
{
|
||||
public function addLinks()
|
||||
{
|
||||
$controller = 'Individuals';
|
||||
if (empty($this->viewVars['canEdit'])) {
|
||||
$this->bcf->removeLink($controller, 'view', $controller, 'edit');
|
||||
$this->bcf->removeLink($controller, 'edit', $controller, 'edit');
|
||||
}
|
||||
}
|
||||
|
||||
public function addActions()
|
||||
{
|
||||
$controller = 'Individuals';
|
||||
if (empty($this->viewVars['canDelete'])) {
|
||||
$this->bcf->removeAction($controller, 'view', $controller, 'delete');
|
||||
$this->bcf->removeAction($controller, 'edit', $controller, 'delete');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,4 +5,21 @@ require_once(APP . 'Controller' . DS . 'Component' . DS . 'Navigation' . DS . 'b
|
|||
|
||||
class TagsNavigation extends BaseNavigation
|
||||
{
|
||||
public function addLinks()
|
||||
{
|
||||
$controller = 'Tags';
|
||||
if (empty($this->viewVars['loggedUser']['role']['perm_admin'])) {
|
||||
$this->bcf->removeLink($controller, 'view', $controller, 'edit');
|
||||
$this->bcf->removeLink($controller, 'edit', $controller, 'edit');
|
||||
}
|
||||
}
|
||||
|
||||
public function addActions()
|
||||
{
|
||||
$controller = 'Tags';
|
||||
if (empty($this->viewVars['loggedUser']['role']['perm_admin'])) {
|
||||
$this->bcf->removeAction($controller, 'view', $controller, 'delete');
|
||||
$this->bcf->removeAction($controller, 'edit', $controller, 'delete');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,11 @@ class Sidemenu {
|
|||
'icon' => $this->iconTable['Organisations'],
|
||||
'url' => '/organisations/index',
|
||||
],
|
||||
'OrgGroups' => [
|
||||
'label' => __('Organisation Groups'),
|
||||
'icon' => $this->iconTable['OrgGroups'],
|
||||
'url' => '/orgGroups/index',
|
||||
],
|
||||
'EncryptionKeys' => [
|
||||
'label' => __('Encryption keys'),
|
||||
'icon' => $this->iconTable['EncryptionKeys'],
|
||||
|
|
|
@ -23,6 +23,7 @@ class NavigationComponent extends Component
|
|||
public $iconToTableMapping = [
|
||||
'Individuals' => 'address-book',
|
||||
'Organisations' => 'building',
|
||||
'OrgGroups' => 'city',
|
||||
'EncryptionKeys' => 'key',
|
||||
'SharingGroups' => 'user-friends',
|
||||
'MailingLists' => 'mail-bulk',
|
||||
|
@ -125,7 +126,7 @@ class NavigationComponent extends Component
|
|||
public function genBreadcrumb(): array
|
||||
{
|
||||
$request = $this->request;
|
||||
$bcf = new BreadcrumbFactory($this->iconToTableMapping);
|
||||
$bcf = new BreadcrumbFactory($this->iconToTableMapping, $this->getController());
|
||||
$fullConfig = $this->getFullConfig($bcf, $this->request);
|
||||
return $fullConfig;
|
||||
}
|
||||
|
@ -191,9 +192,10 @@ class BreadcrumbFactory
|
|||
private $endpoints = [];
|
||||
private $iconToTableMapping = [];
|
||||
|
||||
public function __construct($iconToTableMapping)
|
||||
public function __construct($iconToTableMapping, $controllerContext)
|
||||
{
|
||||
$this->iconToTableMapping = $iconToTableMapping;
|
||||
$this->controllerContext = $controllerContext;
|
||||
}
|
||||
|
||||
public function defaultCRUD(string $controller, string $action, array $overrides = []): array
|
||||
|
@ -243,7 +245,7 @@ class BreadcrumbFactory
|
|||
$item = $this->genRouteConfig($controller, $action, [
|
||||
'label' => __('Audit changes'),
|
||||
'icon' => 'history',
|
||||
'url' => "/audit-logs?model={{model}}&model_id={{id}}&sort=created&direction=desc&embedInModal=1&excludeStats=1&skipTableToolbar=1",
|
||||
'url' => "/audit-logs?model={{model}}&model_id={{id}}&sort=AuditLogs.created&direction=desc&embedInModal=1&excludeStats=1&skipTableToolbar=1",
|
||||
'url_vars' => ['id' => 'id', 'model' => ['raw' => $table->getAlias()]],
|
||||
'textGetter' => !empty($table->getDisplayField()) ? $table->getDisplayField() : 'id',
|
||||
]);
|
||||
|
@ -288,6 +290,8 @@ class BreadcrumbFactory
|
|||
|
||||
public function setDefaultCRUDForModel($controller)
|
||||
{
|
||||
$loggedUser = $this->controllerContext->ACL->getUser();
|
||||
|
||||
$this->addRoute($controller, 'index', $this->defaultCRUD($controller, 'index'));
|
||||
$this->addRoute($controller, 'view', $this->defaultCRUD($controller, 'view'));
|
||||
$this->addRoute($controller, 'add', $this->defaultCRUD($controller, 'add'));
|
||||
|
@ -307,10 +311,14 @@ class BreadcrumbFactory
|
|||
|
||||
$this->addAction($controller, 'view', $controller, 'add');
|
||||
$this->addAction($controller, 'view', $controller, 'delete');
|
||||
$this->addAction($controller, 'view', $controller, 'audit');
|
||||
if (!empty($loggedUser['role']['perm_admin'])) {
|
||||
$this->addAction($controller, 'view', $controller, 'audit');
|
||||
}
|
||||
$this->addAction($controller, 'edit', $controller, 'add');
|
||||
$this->addAction($controller, 'edit', $controller, 'delete');
|
||||
$this->addAction($controller, 'edit', $controller, 'audit');
|
||||
if (!empty($loggedUser['role']['perm_admin'])) {
|
||||
$this->addAction($controller, 'edit', $controller, 'audit');
|
||||
}
|
||||
}
|
||||
|
||||
public function get($controller, $action)
|
||||
|
@ -402,6 +410,20 @@ class BreadcrumbFactory
|
|||
}
|
||||
}
|
||||
|
||||
public function removeAction(string $sourceController, string $sourceAction, string $targetController, string $targetAction)
|
||||
{
|
||||
$routeSourceConfig = $this->getRouteConfig($sourceController, $sourceAction, true);
|
||||
if (!empty($routeSourceConfig['actions'])) {
|
||||
foreach ($routeSourceConfig['actions'] as $i => $routeConfig) {
|
||||
if ($routeConfig['controller'] == $targetController && $routeConfig['action'] == $targetAction) {
|
||||
unset($routeSourceConfig['actions'][$i]);
|
||||
$this->endpoints[$sourceController][$sourceAction]['actions'] = $routeSourceConfig['actions'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getRouteConfig($controller, $action, $fullRoute = false)
|
||||
{
|
||||
$routeConfig = $this->get($controller, $action);
|
||||
|
|
|
@ -69,6 +69,7 @@ class IndividualsController extends AppController
|
|||
return $responsePayload;
|
||||
}
|
||||
$this->set('canEdit', $this->canEdit($id));
|
||||
$this->set('canDelete', $this->canDelete($id));
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
|
@ -89,6 +90,8 @@ class IndividualsController extends AppController
|
|||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
$this->set('canEdit', $this->canEdit($id));
|
||||
$this->set('canDelete', $this->canDelete($id));
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
|
@ -155,4 +158,17 @@ class IndividualsController extends AppController
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function canDelete($indId): bool
|
||||
{
|
||||
$associatedUsersCount = $this->Individuals->Users->find()->select(['id'])->where(['individual_id' => $indId])->count();
|
||||
if ($associatedUsersCount > 0) {
|
||||
return false;
|
||||
}
|
||||
$currentUser = $this->ACL->getUser();
|
||||
if ($currentUser['role']['perm_admin']) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class MailingListsController extends AppController
|
|||
'quickFilters' => $this->quickFilterFields,
|
||||
'statisticsFields' => $this->statisticsFields,
|
||||
'afterFind' => function ($row) use ($currentUser) {
|
||||
if (empty($currentUser['role']['perm_admin']) || $row['user_id'] != $currentUser['id']) {
|
||||
if (empty($currentUser['role']['perm_admin']) && $row['user_id'] != $currentUser['id']) {
|
||||
if (!$this->MailingLists->isIndividualListed($currentUser['individual_id'], $row)) {
|
||||
$row = false;
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class MailingListsController extends AppController
|
|||
$this->CRUD->view($id, [
|
||||
'contain' => $this->containFields,
|
||||
'afterFind' => function($data) use ($currentUser) {
|
||||
if (empty($currentUser['role']['perm_admin']) || $data['user_id'] != $currentUser['id']) {
|
||||
if (empty($currentUser['role']['perm_admin']) && $data['user_id'] != $currentUser['id']) {
|
||||
if (!$this->MailingLists->isIndividualListed($currentUser['individual_id'], $data)) {
|
||||
$data = [];
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ class MailingListsController extends AppController
|
|||
if (is_null($mailingList)) {
|
||||
throw new NotFoundException(__('Invalid {0}.', Inflector::singularize($this->MailingLists->getAlias())));
|
||||
}
|
||||
if (empty($currentUser['role']['perm_admin']) || $mailingList['user_id'] != $currentUser['id']) {
|
||||
if (empty($currentUser['role']['perm_admin']) && $mailingList['user_id'] != $currentUser['id']) {
|
||||
if (!$this->MailingLists->isIndividualListed($currentUser['individual_id'], $mailingList)) {
|
||||
throw new NotFoundException(__('Invalid {0}.', Inflector::singularize($this->MailingLists->getAlias())));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,256 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Controller\AppController;
|
||||
use Cake\Utility\Hash;
|
||||
use Cake\Utility\Text;
|
||||
use Cake\Database\Expression\QueryExpression;
|
||||
use Cake\Http\Exception\NotFoundException;
|
||||
use Cake\Http\Exception\MethodNotAllowedException;
|
||||
use Cake\Http\Exception\ForbiddenException;
|
||||
|
||||
class OrgGroupsController extends AppController
|
||||
{
|
||||
|
||||
public $quickFilterFields = [['name' => true], 'uuid'];
|
||||
public $filterFields = ['name', 'uuid'];
|
||||
public $containFields = ['Organisations'];
|
||||
public $statisticsFields = [];
|
||||
|
||||
public function index()
|
||||
{
|
||||
$additionalContainFields = [];
|
||||
|
||||
$this->CRUD->index([
|
||||
'filters' => $this->filterFields,
|
||||
'quickFilters' => $this->quickFilterFields,
|
||||
'quickFilterForMetaField' => ['enabled' => true, 'wildcard_search' => true],
|
||||
'contain' => $this->containFields,
|
||||
'statisticsFields' => $this->statisticsFields,
|
||||
]);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
$this->set('metaGroup', 'ContactDB');
|
||||
}
|
||||
|
||||
public function filtering()
|
||||
{
|
||||
$this->CRUD->filtering();
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
$this->CRUD->add();
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
$this->CRUD->view($id, ['contain' => ['Organisations']]);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
$this->set('canEdit', $this->canEdit($id));
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
if (!$this->canEdit($id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot modify that group.'));
|
||||
}
|
||||
$this->CRUD->edit($id);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
$this->set('metaGroup', 'ContactDB');
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
$this->CRUD->delete($id);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
$this->set('metaGroup', 'ContactDB');
|
||||
}
|
||||
|
||||
public function tag($id)
|
||||
{
|
||||
if (!$this->canEdit($id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot tag that organisation.'));
|
||||
}
|
||||
$this->CRUD->tag($id);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function untag($id)
|
||||
{
|
||||
if (!$this->canEdit($id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot untag that organisation.'));
|
||||
}
|
||||
$this->CRUD->untag($id);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function viewTags($id)
|
||||
{
|
||||
$this->CRUD->viewTags($id);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
private function canEdit($groupId): bool
|
||||
{
|
||||
$currentUser = $this->ACL->getUser();
|
||||
if ($currentUser['role']['perm_admin']) {
|
||||
return true;
|
||||
}
|
||||
if ($currentUser['role']['perm_group_admin']) {
|
||||
$orgGroup = $this->OrgGroups->get($groupId, ['contain' => 'Users']);
|
||||
$found = false;
|
||||
foreach ($orgGroup['users'] as $admin) {
|
||||
if ($admin['id'] == $currentUser['id']) {
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Listing should be available to all, it's purely informational
|
||||
public function listAdmins($groupId)
|
||||
{
|
||||
if (empty($groupId)) {
|
||||
throw new NotFoundException(__('Invalid {0}.', 'OrgGroup'));
|
||||
}
|
||||
$orgGroup = $this->OrgGroups->get($groupId, ['contain' => ['Users' => ['Individuals', 'Organisations']]]);
|
||||
$this->set('data', $orgGroup['users']);
|
||||
$this->set('canEdit', $this->ACL->getUser()['role']['perm_admin']);
|
||||
$this->set('groupId', $groupId);
|
||||
}
|
||||
|
||||
// Listing should be available to all, it's purely informational
|
||||
public function listOrgs($groupId)
|
||||
{
|
||||
if (empty($groupId)) {
|
||||
throw new NotFoundException(__('Invalid {0}.', 'OrgGroup'));
|
||||
}
|
||||
$orgGroup = $this->OrgGroups->get($groupId, ['contain' => 'Organisations']);
|
||||
$this->set('data', $orgGroup['organisations']);
|
||||
$this->set('canEdit', $this->canEdit($groupId));
|
||||
$this->set('groupId', $groupId);
|
||||
}
|
||||
|
||||
public function assignAdmin($groupId)
|
||||
{
|
||||
if (!$this->ACL->getUser()['role']['perm_admin']) {
|
||||
throw new MethodNotAllowedException(__('You do not have permission to edit this group.'));
|
||||
}
|
||||
$this->CRUD->linkObjects(__FUNCTION__, $groupId, 'OrgGroups', 'Users', ['redirect' => '/orgGroups/listAdmins/' . $groupId]);
|
||||
if ($this->request->is('post')) {
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
$orgGroup = $this->OrgGroups->get($groupId, ['contain' => 'Users']);
|
||||
$this->loadModel('Users');
|
||||
$this->loadModel('Roles');
|
||||
$validRoles = $this->Roles->find('list')->disableHydration()->select(
|
||||
['id', 'name']
|
||||
)->where(
|
||||
['OR' => ['perm_admin' => 1, 'perm_group_admin' => 1]]
|
||||
)->toArray();
|
||||
$admins = $this->Users->find('list')->disableHydration()->select(['id', 'username'])->where(['Users.role_id IN' => array_keys($validRoles)])->toArray();
|
||||
asort($admins, SORT_STRING | SORT_FLAG_CASE);
|
||||
if (!empty($orgGroup['users'])) {
|
||||
foreach ($orgGroup['users'] as $admin) {
|
||||
if (isset($admins[$admin['id']])) {
|
||||
unset($admins[$admin['id']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$dropdownData = [
|
||||
'admins' => $admins
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
}
|
||||
|
||||
public function removeAdmin($groupId, $adminId)
|
||||
{
|
||||
if (!$this->ACL->getUser()['role']['perm_admin']) {
|
||||
throw new MethodNotAllowedException(__('You do not have permission to edit this group.'));
|
||||
}
|
||||
$this->CRUD->unlinkObjects(__FUNCTION__, $groupId, $adminId, 'OrgGroups', 'Users');
|
||||
if ($this->request->is('post')) {
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
$this->viewBuilder()->setLayout('ajax');
|
||||
$this->render('/genericTemplates/detach');
|
||||
}
|
||||
|
||||
public function attachOrg($groupId)
|
||||
{
|
||||
if (!$this->OrgGroups->checkIfGroupAdmin($groupId, $this->ACL->getUser())) {
|
||||
throw new MethodNotAllowedException(__('You do not have permission to edit this group.'));
|
||||
}
|
||||
$this->CRUD->linkObjects(__FUNCTION__, $groupId, 'OrgGroups', 'Organisations', ['redirect' => '/orgGroups/listOrgs/' . $groupId]);
|
||||
if ($this->request->is('post')) {
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
$orgGroup = $this->OrgGroups->get($groupId, ['contain' => 'Organisations']);
|
||||
$this->loadModel('Organisations');
|
||||
$orgs = $this->Organisations->find('list')->disableHydration()->select(['id', 'name'])->toArray();
|
||||
asort($orgs, SORT_STRING | SORT_FLAG_CASE);
|
||||
foreach ($orgGroup['organisations'] as $organisation) {
|
||||
if (isset($orgs[$organisation['id']])) {
|
||||
unset($orgs[$organisation['id']]);
|
||||
}
|
||||
}
|
||||
$dropdownData = [
|
||||
'orgs' => $orgs
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
}
|
||||
|
||||
public function detachOrg($groupId, $orgId)
|
||||
{
|
||||
if (!$this->OrgGroups->checkIfGroupAdmin($groupId, $this->ACL->getUser())) {
|
||||
throw new MethodNotAllowedException(__('You do not have permission to edit this group.'));
|
||||
}
|
||||
$this->CRUD->unlinkObjects(__FUNCTION__, $groupId, $orgId, 'OrgGroups', 'Organisations');
|
||||
if ($this->request->is('post')) {
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
$this->viewBuilder()->setLayout('ajax');
|
||||
$this->render('/genericTemplates/detach');
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ class OrganisationsController extends AppController
|
|||
|
||||
public $quickFilterFields = [['name' => true], 'uuid', 'nationality', 'sector', 'type', 'url'];
|
||||
public $filterFields = ['name', 'uuid', 'nationality', 'sector', 'type', 'url', 'Alignments.id', 'MetaFields.field', 'MetaFields.value', 'MetaFields.MetaTemplates.name'];
|
||||
public $containFields = ['Alignments' => 'Individuals'];
|
||||
public $containFields = ['Alignments' => 'Individuals', 'OrgGroups'];
|
||||
public $statisticsFields = ['nationality', 'sector'];
|
||||
|
||||
public function index()
|
||||
|
@ -105,7 +105,7 @@ class OrganisationsController extends AppController
|
|||
|
||||
public function view($id)
|
||||
{
|
||||
$this->CRUD->view($id, ['contain' => ['Alignments' => 'Individuals']]);
|
||||
$this->CRUD->view($id, ['contain' => ['Alignments' => 'Individuals', 'OrgGroups']]);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
|
@ -118,7 +118,15 @@ class OrganisationsController extends AppController
|
|||
if (!$this->canEdit($id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot modify that organisation.'));
|
||||
}
|
||||
$this->CRUD->edit($id);
|
||||
$currentUser = $this->ACL->getUser();
|
||||
$this->CRUD->edit($id, [
|
||||
'beforeSave' => function($data) use ($currentUser) {
|
||||
if (!$currentUser['role']['perm_admin']) {
|
||||
unset($data['uuid']);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
]);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
|
|
|
@ -70,6 +70,12 @@ class UserSettingsController extends AppController
|
|||
$this->CRUD->add([
|
||||
'redirect' => ['action' => 'index', $user_id],
|
||||
'beforeSave' => function ($data) use ($currentUser) {
|
||||
$fakeUser = new \stdClass();
|
||||
$fakeUser->id = $data['user_id'];
|
||||
$existingSetting = $this->UserSettings->getSettingByName($fakeUser, $data['name']);
|
||||
if (!empty($existingSetting)) {
|
||||
throw new MethodNotAllowedException(__('You cannot create a setting that already exists for the given user.'));
|
||||
}
|
||||
if (empty($currentUser['role']['perm_admin'])) {
|
||||
$data['user_id'] = $currentUser->id;
|
||||
}
|
||||
|
@ -112,13 +118,19 @@ class UserSettingsController extends AppController
|
|||
} else {
|
||||
$validUsers = $this->Users->find('list')->select(['id', 'username'])->order(['username' => 'asc'])->all()->toArray();
|
||||
}
|
||||
$dropdownData = [
|
||||
'user' => [$entity->user_id => $validUsers[$entity->user_id]],
|
||||
];
|
||||
|
||||
$entity = $this->CRUD->edit($id, [
|
||||
'redirect' => ['action' => 'index', $entity->user_id],
|
||||
'beforeSave' => function ($data) use ($validUsers) {
|
||||
'beforeSave' => function ($data) use ($validUsers, $entity) {
|
||||
if (!in_array($data['user_id'], array_keys($validUsers))) {
|
||||
throw new MethodNotAllowedException(__('You cannot edit the given user.'));
|
||||
}
|
||||
if ($data['user_id'] != $entity->user_id) {
|
||||
throw new MethodNotAllowedException(__('You cannot assign the setting to a different user.'));
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
]);
|
||||
|
@ -126,11 +138,9 @@ class UserSettingsController extends AppController
|
|||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
$dropdownData = [
|
||||
'user' => $validUsers,
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->set('user_id', $this->entity->user_id);
|
||||
$this->set('is_edit', true);
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ class UsersController extends AppController
|
|||
{
|
||||
public $filterFields = ['Individuals.uuid', 'username', 'Individuals.email', 'Individuals.first_name', 'Individuals.last_name', 'Organisations.name', 'Organisation.nationality'];
|
||||
public $quickFilterFields = ['Individuals.uuid', ['username' => true], ['Individuals.first_name' => true], ['Individuals.last_name' => true], 'Individuals.email'];
|
||||
public $containFields = ['Individuals', 'Roles', 'UserSettings', 'Organisations'];
|
||||
public $containFields = ['Individuals', 'Roles', 'UserSettings', 'Organisations', 'OrgGroups'];
|
||||
|
||||
public function index()
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ class UsersController extends AppController
|
|||
}
|
||||
$this->set(
|
||||
'validRoles',
|
||||
$this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_admin' => 0])->all()->toArray()
|
||||
$this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_admin' => 0, 'perm_org_admin' => 0])->all()->toArray()
|
||||
);
|
||||
$this->set('metaGroup', $this->isAdmin ? 'Administration' : 'Cerebrate');
|
||||
}
|
||||
|
@ -65,8 +65,13 @@ class UsersController extends AppController
|
|||
];
|
||||
$individual_ids = [];
|
||||
if (!$currentUser['role']['perm_admin']) {
|
||||
$validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_admin' => 0, 'perm_org_admin' => 0])->all()->toArray();
|
||||
$individual_ids = $this->Users->Individuals->find('aligned', ['organisation_id' => $currentUser['organisation_id']])->all()->extract('id')->toArray();
|
||||
if (!$currentUser['role']['perm_group_admin']) {
|
||||
$validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_admin' => 0, 'perm_group_admin' => 0])->all()->toArray();
|
||||
$individual_ids = $this->Users->Individuals->find('aligned', ['organisation_id' => $currentUser['organisation_id']])->all()->extract('id')->toArray();
|
||||
} else {
|
||||
$validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_admin' => 0, 'perm_group_admin' => 0, 'perm_org_admin' => 0])->all()->toArray();
|
||||
|
||||
}
|
||||
if (empty($individual_ids)) {
|
||||
$individual_ids = [-1];
|
||||
}
|
||||
|
@ -177,7 +182,7 @@ class UsersController extends AppController
|
|||
}
|
||||
}
|
||||
$this->CRUD->view($id, [
|
||||
'contain' => ['Individuals' => ['Alignments' => 'Organisations'], 'Roles', 'Organisations'],
|
||||
'contain' => ['Individuals' => ['Alignments' => 'Organisations'], 'Roles', 'Organisations', 'OrgGroups'],
|
||||
'afterFind' => function($data) use ($keycloakUsersParsed, $currentUser) {
|
||||
if (empty($currentUser['role']['perm_admin']) && $currentUser['organisation_id'] != $data['organisation_id']) {
|
||||
throw new NotFoundException(__('Invalid User.'));
|
||||
|
@ -259,7 +264,7 @@ class UsersController extends AppController
|
|||
$params['fields'][] = 'disabled';
|
||||
if (!$currentUser['role']['perm_admin']) {
|
||||
$params['afterFind'] = function ($data, &$params) use ($currentUser, $validRoles) {
|
||||
if (!in_array($data['role_id'], array_keys($validRoles))) {
|
||||
if (!in_array($data['role_id'], array_keys($validRoles)) && $this->ACL->getUser()['id'] != $data['id']) {
|
||||
throw new MethodNotAllowedException(__('You cannot edit the given privileged user.'));
|
||||
}
|
||||
if (!$this->ACL->canEditUser($currentUser, $data)) {
|
||||
|
@ -268,7 +273,7 @@ class UsersController extends AppController
|
|||
return $data;
|
||||
};
|
||||
$params['beforeSave'] = function ($data) use ($currentUser, $validRoles) {
|
||||
if (!in_array($data['role_id'], array_keys($validRoles))) {
|
||||
if (!in_array($data['role_id'], array_keys($validRoles)) && $this->ACL->getUser()['id'] != $data['id']) {
|
||||
throw new MethodNotAllowedException(__('You cannot assign the chosen role to a user.'));
|
||||
}
|
||||
return $data;
|
||||
|
@ -284,6 +289,9 @@ class UsersController extends AppController
|
|||
if (empty($currentUser['role']['perm_admin'])) {
|
||||
$org_conditions = ['id' => $currentUser['organisation_id']];
|
||||
}
|
||||
if ($this->ACL->getUser()['id'] == $id) {
|
||||
$validRoles[$this->ACL->getUser()['role']['id']] = $this->ACL->getUser()['role']['name']; // include the current role of the user
|
||||
}
|
||||
$dropdownData = [
|
||||
'role' => $validRoles,
|
||||
'organisation' => $this->Users->Organisations->find('list', [
|
||||
|
@ -329,6 +337,9 @@ class UsersController extends AppController
|
|||
if (empty(Configure::read('user.allow-user-deletion'))) {
|
||||
throw new MethodNotAllowedException(__('User deletion is disabled on this instance.'));
|
||||
}
|
||||
if (!$this->ACL->canEditUser($currentUser, $data)) {
|
||||
throw new MethodNotAllowedException(__('You cannot edit the given user.'));
|
||||
}
|
||||
if (!$currentUser['role']['perm_admin']) {
|
||||
if ($data['organisation_id'] !== $currentUser['organisation_id']) {
|
||||
throw new MethodNotAllowedException(__('You do not have permission to delete the given user.'));
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Entity;
|
||||
|
||||
use App\Model\Entity\AppModel;
|
||||
use Cake\ORM\Entity;
|
||||
|
||||
class OrgGroup extends AppModel
|
||||
{
|
||||
protected $_accessible = [
|
||||
'*' => true,
|
||||
'id' => false,
|
||||
'created' => false
|
||||
];
|
||||
|
||||
protected $_accessibleOnNew = [
|
||||
'created' => true
|
||||
];
|
||||
|
||||
public function rearrangeForAPI(array $options = []): void
|
||||
{
|
||||
if (!empty($this->tags)) {
|
||||
$this->tags = $this->rearrangeTags($this->tags);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -125,7 +125,7 @@ class IndividualsTable extends AppTable
|
|||
public function getValidIndividualsToEdit(object $currentUser): array
|
||||
{
|
||||
$validRoles = $this->Users->Roles->find('list')->select(['id'])->where(['perm_admin' => 0, 'perm_org_admin' => 0])->all()->toArray();
|
||||
$validIndividualIds = $this->Users->find('list')->select(['individual_id'])->where(
|
||||
$validIndividualIds = $this->Users->find()->select(['individual_id'])->where(
|
||||
[
|
||||
'organisation_id' => $currentUser['organisation_id'],
|
||||
'disabled' => 0,
|
||||
|
@ -134,7 +134,7 @@ class IndividualsTable extends AppTable
|
|||
['id' => $currentUser['id']],
|
||||
]
|
||||
]
|
||||
)->all()->toArray();
|
||||
return array_keys($validIndividualIds);
|
||||
)->all()->extract('individual_id')->toArray();
|
||||
return $validIndividualIds;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Table;
|
||||
|
||||
use App\Model\Table\AppTable;
|
||||
use Cake\ORM\Table;
|
||||
use Cake\Validation\Validator;
|
||||
use Cake\Error\Debugger;
|
||||
use App\Model\Entity\User;
|
||||
|
||||
class OrgGroupsTable extends AppTable
|
||||
{
|
||||
public function initialize(array $config): void
|
||||
{
|
||||
parent::initialize($config);
|
||||
$this->addBehavior('UUID');
|
||||
$this->addBehavior('Timestamp');
|
||||
$this->addBehavior('Tags.Tag');
|
||||
$this->addBehavior('AuditLog');
|
||||
$this->belongsToMany('Organisations', [
|
||||
'joinTable' => 'org_groups_organisations',
|
||||
]);
|
||||
$this->belongsToMany('Users', [
|
||||
'joinTable' => 'org_groups_admins',
|
||||
]);
|
||||
$this->setDisplayField('name');
|
||||
}
|
||||
|
||||
public function validationDefault(Validator $validator): Validator
|
||||
{
|
||||
$validator
|
||||
->notEmptyString('name')
|
||||
->notEmptyString('uuid')
|
||||
->requirePresence(['name', 'uuid'], 'create');
|
||||
return $validator;
|
||||
}
|
||||
|
||||
public function checkIfGroupAdmin(int $groupId, mixed $user): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function checkIfUserBelongsToGroupAdminsGroup(User $currentUser, User $userToCheck): bool
|
||||
{
|
||||
$managedGroups = $this->find('list')->where(['Users.id' => $currentUser['id']])->select(['id', 'uuid'])->disableHydration()->toArray();
|
||||
return isset($managedGroups[$userToCheck['org_id']]);
|
||||
}
|
||||
}
|
|
@ -34,6 +34,9 @@ class OrganisationsTable extends AppTable
|
|||
'conditions' => ['owner_model' => 'organisation']
|
||||
]
|
||||
);
|
||||
$this->belongsToMany('OrgGroups', [
|
||||
'joinTable' => 'org_groups_organisations',
|
||||
]);
|
||||
$this->addBehavior('MetaFields');
|
||||
$this->setDisplayField('name');
|
||||
}
|
||||
|
@ -80,4 +83,17 @@ class OrganisationsTable extends AppTable
|
|||
$this->saveMetaFields($id, $org);
|
||||
}
|
||||
}
|
||||
|
||||
public function getEditableOrganisationsForUser($user): array
|
||||
{
|
||||
$query = $this->find();
|
||||
if (empty($user['role']['perm_admin'])) {
|
||||
if (!empty($user['role']['perm_org_admin'])) {
|
||||
$query->where(['Organisations.id' => $user['organisation']['id']]);
|
||||
} else {
|
||||
return []; // User not an org_admin. Cannot edit orgs
|
||||
}
|
||||
}
|
||||
return $query->all()->toList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,9 @@ class UsersTable extends AppTable
|
|||
'strategy' => 'join'
|
||||
]
|
||||
);
|
||||
$this->belongsToMany('OrgGroups', [
|
||||
'joinTable' => 'org_groups_admins',
|
||||
]);
|
||||
$this->hasMany(
|
||||
'UserSettings',
|
||||
[
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"version": "1.15",
|
||||
"version": "1.16",
|
||||
"application": "Cerebrate"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
echo $this->element('genericElements/Form/genericForm', array(
|
||||
'data' => array(
|
||||
'description' => __('OrgGroups are an administrative concept, multiple organisations can belong to a grouping that allows common management by so called "GroupAdmins". This helps grouping organisations by sector, country or other commonalities into co-managed sub-communities.'),
|
||||
'model' => 'OrgGroup',
|
||||
'fields' => array(
|
||||
array(
|
||||
'field' => 'name'
|
||||
),
|
||||
array(
|
||||
'field' => 'uuid',
|
||||
'label' => 'UUID',
|
||||
'type' => 'uuid'
|
||||
),
|
||||
array(
|
||||
'field' => 'description'
|
||||
)
|
||||
),
|
||||
'submit' => array(
|
||||
'action' => $this->request->getParam('action')
|
||||
)
|
||||
)
|
||||
));
|
||||
?>
|
||||
</div>
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
echo $this->element('genericElements/Form/genericForm', array(
|
||||
'data' => array(
|
||||
'description' => __('Assign a user to be an administrator of the group.'),
|
||||
'model' => null,
|
||||
'fields' => array(
|
||||
array(
|
||||
'field' => 'id',
|
||||
'label' => __('User'),
|
||||
'type' => 'dropdown',
|
||||
'options' => $dropdownData['admins']
|
||||
)
|
||||
),
|
||||
'submit' => array(
|
||||
'action' => $this->request->getParam('action')
|
||||
)
|
||||
)
|
||||
));
|
||||
?>
|
||||
</div>
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
echo $this->element('genericElements/Form/genericForm', array(
|
||||
'data' => array(
|
||||
'description' => __('Assign an organisation to the group.'),
|
||||
'model' => null,
|
||||
'fields' => array(
|
||||
array(
|
||||
'field' => 'id',
|
||||
'label' => __('Organisation'),
|
||||
'type' => 'dropdown',
|
||||
'options' => $dropdownData['orgs']
|
||||
)
|
||||
),
|
||||
'submit' => array(
|
||||
'action' => $this->request->getParam('action')
|
||||
)
|
||||
)
|
||||
));
|
||||
?>
|
||||
</div>
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
echo $this->element('genericElements/IndexTable/index_table', [
|
||||
'data' => [
|
||||
'data' => $data,
|
||||
'top_bar' => [
|
||||
'children' => [
|
||||
[
|
||||
'type' => 'simple',
|
||||
'children' => [
|
||||
'data' => [
|
||||
'type' => 'simple',
|
||||
'text' => __('Add group'),
|
||||
'class' => 'btn btn-primary',
|
||||
'popover_url' => '/orgGroups/add'
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'type' => 'search',
|
||||
'button' => __('Search'),
|
||||
'placeholder' => __('Enter value to search'),
|
||||
'data' => '',
|
||||
'searchKey' => 'value',
|
||||
'allowFilering' => true
|
||||
],
|
||||
[
|
||||
'type' => 'table_action',
|
||||
'table_setting_id' => 'org_groups_index',
|
||||
]
|
||||
]
|
||||
],
|
||||
'fields' => [
|
||||
[
|
||||
'name' => '#',
|
||||
'sort' => 'id',
|
||||
'class' => 'short',
|
||||
'data_path' => 'id',
|
||||
],
|
||||
[
|
||||
'name' => __('Name'),
|
||||
'class' => 'short',
|
||||
'data_path' => 'name',
|
||||
'sort' => 'name',
|
||||
],
|
||||
[
|
||||
'name' => __('UUID'),
|
||||
'sort' => 'uuid',
|
||||
'class' => 'short',
|
||||
'data_path' => 'uuid',
|
||||
],
|
||||
[
|
||||
'name' => __('Description'),
|
||||
'data_path' => 'description',
|
||||
'sort' => 'description',
|
||||
],
|
||||
[
|
||||
'name' => __('Tags'),
|
||||
'data_path' => 'tags',
|
||||
'element' => 'tags',
|
||||
],
|
||||
],
|
||||
'title' => __('Organisation Groups Index'),
|
||||
'description' => __('OrgGroups are an administrative concept, multiple organisations can belong to a grouping that allows common management by so called "GroupAdmins". This helps grouping organisations by sector, country or other commonalities into co-managed sub-communities.'),
|
||||
'actions' => [
|
||||
[
|
||||
'url' => '/orgGroups/view',
|
||||
'url_params_data_paths' => ['id'],
|
||||
'icon' => 'eye'
|
||||
],
|
||||
[
|
||||
'open_modal' => '/orgGroups/edit/[onclick_params_data_path]',
|
||||
'modal_params_data_path' => 'id',
|
||||
'icon' => 'edit',
|
||||
'requirement' => $loggedUser['role']['perm_admin']
|
||||
],
|
||||
[
|
||||
'open_modal' => '/orgGroups/delete/[onclick_params_data_path]',
|
||||
'modal_params_data_path' => 'id',
|
||||
'icon' => 'trash',
|
||||
'requirement' => $loggedUser['role']['perm_admin']
|
||||
],
|
||||
]
|
||||
]
|
||||
]);
|
||||
echo '</div>';
|
||||
?>
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
echo $this->element('genericElements/IndexTable/index_table', [
|
||||
'data' => [
|
||||
'skip_pagination' => true,
|
||||
'data' => $data,
|
||||
'top_bar' => [
|
||||
'children' => [
|
||||
[
|
||||
'type' => 'simple',
|
||||
'children' => [
|
||||
'data' => [
|
||||
'type' => 'simple',
|
||||
'text' => __('Add Group Administrator'),
|
||||
'class' => 'btn btn-primary',
|
||||
'popover_url' => '/orgGroups/assignAdmin/' . h($groupId),
|
||||
'reload_url' => '/orgGroups/listAdmins/' . h($groupId),
|
||||
],
|
||||
],
|
||||
'requirement' => $canEdit
|
||||
]
|
||||
]
|
||||
],
|
||||
'fields' => [
|
||||
[
|
||||
'name' => '#',
|
||||
'sort' => 'id',
|
||||
'class' => 'short',
|
||||
'data_path' => 'id',
|
||||
],
|
||||
[
|
||||
'name' => __('Username'),
|
||||
'class' => 'short',
|
||||
'data_path' => 'username',
|
||||
'sort' => 'username',
|
||||
],
|
||||
[
|
||||
'name' => __('Email'),
|
||||
'class' => 'short',
|
||||
'data_path' => 'individual.email',
|
||||
'sort' => 'individual.email',
|
||||
],
|
||||
[
|
||||
'name' => __('Organisation'),
|
||||
'sort' => 'organisation.name',
|
||||
'class' => 'short',
|
||||
'data_path' => 'organisation.name',
|
||||
]
|
||||
],
|
||||
'title' => null,
|
||||
'description' => null,
|
||||
'actions' => [
|
||||
[
|
||||
'open_modal' => '/orgGroups/removeAdmin/' . h($groupId) . '/[onclick_params_data_path]',
|
||||
'reload_url' => '/orgGroups/listAdmins/' . h($groupId),
|
||||
'modal_params_data_path' => 'id',
|
||||
'icon' => 'unlink',
|
||||
'title' => __('Remove the administrator from the group'),
|
||||
'requirement' => $canEdit
|
||||
],
|
||||
]
|
||||
]
|
||||
]);
|
||||
echo '</div>';
|
||||
?>
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
echo $this->element('genericElements/IndexTable/index_table', [
|
||||
'data' => [
|
||||
'skip_pagination' => true,
|
||||
'data' => $data,
|
||||
'top_bar' => [
|
||||
'children' => [
|
||||
[
|
||||
'type' => 'simple',
|
||||
'children' => [
|
||||
'data' => [
|
||||
'type' => 'simple',
|
||||
'text' => __('Add Organisation'),
|
||||
'class' => 'btn btn-primary',
|
||||
'popover_url' => '/orgGroups/attachOrg/' . h($groupId),
|
||||
'reload_url' => '/orgGroups/listOrgs/' . h($groupId),
|
||||
'requirement' => $canEdit
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'type' => 'search',
|
||||
'button' => __('Search'),
|
||||
'placeholder' => __('Enter value to search'),
|
||||
'data' => '',
|
||||
'searchKey' => 'value',
|
||||
'allowFilering' => true
|
||||
]
|
||||
]
|
||||
],
|
||||
'fields' => [
|
||||
[
|
||||
'name' => '#',
|
||||
'sort' => 'id',
|
||||
'class' => 'short',
|
||||
'data_path' => 'id',
|
||||
],
|
||||
[
|
||||
'name' => __('Name'),
|
||||
'class' => 'short',
|
||||
'data_path' => 'name',
|
||||
'sort' => 'name',
|
||||
],
|
||||
[
|
||||
'name' => __('UUID'),
|
||||
'sort' => 'uuid',
|
||||
'class' => 'short',
|
||||
'data_path' => 'uuid',
|
||||
]
|
||||
],
|
||||
'title' => null,
|
||||
'description' => null,
|
||||
'actions' => [
|
||||
[
|
||||
'url' => '/organisations/view',
|
||||
'url_params_data_paths' => ['id'],
|
||||
'icon' => 'eye'
|
||||
],
|
||||
[
|
||||
'open_modal' => '/orgGroups/detachOrg/' . h($groupId) . '/[onclick_params_data_path]',
|
||||
'reload_url' => '/orgGroups/listOrgs/' . h($groupId),
|
||||
'modal_params_data_path' => 'id',
|
||||
'icon' => 'unlink',
|
||||
'title' => __('Remove organisation from the group'),
|
||||
'requirement' => $canEdit
|
||||
],
|
||||
]
|
||||
]
|
||||
]);
|
||||
echo '</div>';
|
||||
?>
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
echo $this->element(
|
||||
'/genericElements/SingleViews/single_view',
|
||||
[
|
||||
'title' => __('Organisation Group View'),
|
||||
'data' => $entity,
|
||||
'fields' => [
|
||||
[
|
||||
'key' => __('ID'),
|
||||
'path' => 'id'
|
||||
],
|
||||
[
|
||||
'key' => __('Name'),
|
||||
'path' => 'name'
|
||||
],
|
||||
[
|
||||
'key' => __('UUID'),
|
||||
'path' => 'uuid'
|
||||
],
|
||||
[
|
||||
'key' => __('Description'),
|
||||
'path' => 'Description'
|
||||
],
|
||||
[
|
||||
'key' => __('Tags'),
|
||||
'type' => 'tags',
|
||||
'editable' => $canEdit,
|
||||
]
|
||||
],
|
||||
'combinedFieldsView' => false,
|
||||
'children' => [
|
||||
[
|
||||
'url' => '/orgGroups/listAdmins/{{0}}',
|
||||
'url_params' => ['id'],
|
||||
'title' => __('Administrators')
|
||||
],
|
||||
[
|
||||
'url' => '/orgGroups/listOrgs/{{0}}',
|
||||
'url_params' => ['id'],
|
||||
'title' => __('Organisations')
|
||||
]
|
||||
]
|
||||
]
|
||||
);
|
|
@ -12,6 +12,7 @@
|
|||
'label' => 'UUID',
|
||||
'type' => 'uuid',
|
||||
'tooltip' => __('If the Organisation already has a known UUID in another application such as MISP or another Cerebrate, please re-use this one.'),
|
||||
'requirements' => $loggedUser['role']['perm_admin']
|
||||
),
|
||||
array(
|
||||
'field' => 'url'
|
||||
|
|
|
@ -59,6 +59,14 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
'url' => '/individuals/index/?Organisations.id={{url_data}}',
|
||||
'url_data_path' => 'id'
|
||||
],
|
||||
[
|
||||
'name' => __('Group memberships'),
|
||||
'data_path' => 'org_groups',
|
||||
'data_id_sub_path' => 'id',
|
||||
'data_value_sub_path' => 'name',
|
||||
'element' => 'link_list',
|
||||
'url_pattern' => '/orgGroups/view/{{data_id}}'
|
||||
],
|
||||
[
|
||||
'name' => __('URL'),
|
||||
'sort' => 'url',
|
||||
|
@ -98,7 +106,14 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
'open_modal' => '/organisations/edit/[onclick_params_data_path]',
|
||||
'modal_params_data_path' => 'id',
|
||||
'icon' => 'edit',
|
||||
'requirement' => $loggedUser['role']['perm_admin']
|
||||
'complex_requirement' => [
|
||||
'function' => function ($row, $options) use ($loggedUser) {
|
||||
if ($loggedUser['role']['perm_admin'] || ($loggedUser['role']['perm_org_admin'] && $row['id'] == $loggedUser['organisation']['id'])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
]
|
||||
],
|
||||
[
|
||||
'open_modal' => '/organisations/delete/[onclick_params_data_path]',
|
||||
|
|
|
@ -1,54 +1,67 @@
|
|||
<?php
|
||||
|
||||
$fields = [
|
||||
[
|
||||
'key' => __('ID'),
|
||||
'path' => 'id'
|
||||
],
|
||||
[
|
||||
'key' => __('Name'),
|
||||
'path' => 'name'
|
||||
],
|
||||
[
|
||||
'key' => __('UUID'),
|
||||
'path' => 'uuid'
|
||||
],
|
||||
[
|
||||
'key' => __('URL'),
|
||||
'path' => 'url'
|
||||
],
|
||||
[
|
||||
'key' => __('Country'),
|
||||
'path' => 'nationality'
|
||||
],
|
||||
[
|
||||
'key' => __('Sector'),
|
||||
'path' => 'sector'
|
||||
],
|
||||
[
|
||||
'key' => __('Type'),
|
||||
'path' => 'type'
|
||||
],
|
||||
[
|
||||
'key' => __('Contacts'),
|
||||
'path' => 'contacts'
|
||||
],
|
||||
[
|
||||
'key' => __('Tags'),
|
||||
'type' => 'tags',
|
||||
'editable' => $canEdit,
|
||||
],
|
||||
[
|
||||
'key' => __('Alignments'),
|
||||
'type' => 'alignment',
|
||||
'path' => '',
|
||||
'scope' => 'organisations'
|
||||
]
|
||||
];
|
||||
|
||||
if (!empty($entity['org_groups'])) {
|
||||
$fields[] = [
|
||||
'type' => 'link_list',
|
||||
'key' => __('Group memberships'),
|
||||
'path' => 'org_groups',
|
||||
'data_id_sub_path' => 'id',
|
||||
'data_value_sub_path' => 'name',
|
||||
'url_pattern' => '/orgGroups/view/{{data_id}}'
|
||||
];
|
||||
}
|
||||
echo $this->element(
|
||||
'/genericElements/SingleViews/single_view',
|
||||
[
|
||||
'title' => __('Organisation View'),
|
||||
'data' => $entity,
|
||||
'fields' => [
|
||||
[
|
||||
'key' => __('ID'),
|
||||
'path' => 'id'
|
||||
],
|
||||
[
|
||||
'key' => __('Name'),
|
||||
'path' => 'name'
|
||||
],
|
||||
[
|
||||
'key' => __('UUID'),
|
||||
'path' => 'uuid'
|
||||
],
|
||||
[
|
||||
'key' => __('URL'),
|
||||
'path' => 'url'
|
||||
],
|
||||
[
|
||||
'key' => __('Country'),
|
||||
'path' => 'nationality'
|
||||
],
|
||||
[
|
||||
'key' => __('Sector'),
|
||||
'path' => 'sector'
|
||||
],
|
||||
[
|
||||
'key' => __('Type'),
|
||||
'path' => 'type'
|
||||
],
|
||||
[
|
||||
'key' => __('Contacts'),
|
||||
'path' => 'contacts'
|
||||
],
|
||||
[
|
||||
'key' => __('Tags'),
|
||||
'type' => 'tags',
|
||||
'editable' => $canEdit,
|
||||
],
|
||||
[
|
||||
'key' => __('Alignments'),
|
||||
'type' => 'alignment',
|
||||
'path' => '',
|
||||
'scope' => 'organisations'
|
||||
]
|
||||
],
|
||||
'fields' => $fields,
|
||||
'combinedFieldsView' => false,
|
||||
'children' => []
|
||||
]
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
'type' => 'checkbox',
|
||||
'label' => 'Full admin privilege'
|
||||
],
|
||||
[
|
||||
'field' => 'perm_group_admin',
|
||||
'type' => 'checkbox',
|
||||
'label' => 'Organisation Group admin privilege'
|
||||
],
|
||||
[
|
||||
'field' => 'perm_org_admin',
|
||||
'type' => 'checkbox',
|
||||
|
|
|
@ -50,6 +50,12 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
'data_path' => 'perm_admin',
|
||||
'element' => 'boolean'
|
||||
],
|
||||
[
|
||||
'name' => __('Group Admin'),
|
||||
'sort' => 'perm_group_admin',
|
||||
'data_path' => 'perm_group_admin',
|
||||
'element' => 'boolean'
|
||||
],
|
||||
[
|
||||
'name' => __('Org Admin'),
|
||||
'sort' => 'perm_org_admin',
|
||||
|
|
|
@ -17,6 +17,11 @@ echo $this->element(
|
|||
'path' => 'perm_admin',
|
||||
'type' => 'boolean'
|
||||
],
|
||||
[
|
||||
'key' => __('Organisation Group admin permission'),
|
||||
'path' => 'perm_group_admin',
|
||||
'type' => 'boolean'
|
||||
],
|
||||
[
|
||||
'key' => __('Organisation admin permission'),
|
||||
'path' => 'perm_org_admin',
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
'label' => __('User'),
|
||||
'options' => $dropdownData['user'],
|
||||
'value' => !is_null($user_id) ? $user_id : '',
|
||||
'disabled' => !empty($is_edit),
|
||||
],
|
||||
[
|
||||
'field' => 'name',
|
||||
|
|
|
@ -63,6 +63,14 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
'url' => '/organisations/view/{{0}}',
|
||||
'url_vars' => ['organisation.id']
|
||||
],
|
||||
[
|
||||
'name' => __('Administered Groups'),
|
||||
'data_path' => 'org_groups',
|
||||
'data_id_sub_path' => 'id',
|
||||
'data_value_sub_path' => 'name',
|
||||
'element' => 'link_list',
|
||||
'url_pattern' => '/orgGroups/view/{{data_id}}'
|
||||
],
|
||||
[
|
||||
'name' => __('Email'),
|
||||
'sort' => 'individual.email',
|
||||
|
@ -108,7 +116,7 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
],
|
||||
'title' => __('User index'),
|
||||
'description' => __('The list of enrolled users in this Cerebrate instance. All of the users have or at one point had access to the system.'),
|
||||
'pull' => 'right',
|
||||
'includeAllPagination' => true,
|
||||
'actions' => [
|
||||
[
|
||||
'url' => '/users/view',
|
||||
|
@ -127,6 +135,9 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
],
|
||||
'function' => function ($row, $options) use ($loggedUser, $validRoles) {
|
||||
if (empty($loggedUser['role']['perm_admin'])) {
|
||||
if ($row['id'] == $loggedUser['id']) {
|
||||
return true;
|
||||
}
|
||||
if (empty($loggedUser['role']['perm_org_admin'])) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -55,6 +55,16 @@ $fields = [
|
|||
'scope' => 'individuals'
|
||||
]
|
||||
];
|
||||
if (!empty($entity['org_groups'])) {
|
||||
$fields[] = [
|
||||
'type' => 'link_list',
|
||||
'key' => __('Administered Groups'),
|
||||
'path' => 'org_groups',
|
||||
'data_id_sub_path' => 'id',
|
||||
'data_value_sub_path' => 'name',
|
||||
'url_pattern' => '/orgGroups/view/{{data_id}}'
|
||||
];
|
||||
}
|
||||
if ($keycloakConfig['enabled']) {
|
||||
$fields[] = [
|
||||
'key' => __('Keycloak status'),
|
||||
|
|
|
@ -4,43 +4,56 @@ $alignments = '';
|
|||
$canRemove = $this->request->getParam('prefix') !== 'Open';
|
||||
if ($field['scope'] === 'individuals') {
|
||||
foreach ($raw_alignments as $alignment) {
|
||||
$alignments .= sprintf(
|
||||
'<div><span class="fw-bold">%s</span> @ %s <a href="#" class="fas fa-trash .text-reset .text-decoration-none" onClick="%s"></a></div>',
|
||||
h($alignment['type']),
|
||||
sprintf(
|
||||
'<a href="%s/organisations/view/%s">%s</a>',
|
||||
$baseurl,
|
||||
h($alignment['organisation']['id']),
|
||||
h($alignment['organisation']['name'])
|
||||
),
|
||||
!$canRemove ? '' : sprintf(
|
||||
"UI.submissionModalForIndex(%s);",
|
||||
sprintf(
|
||||
"'/alignments/delete/%s'",
|
||||
h($alignment['id'])
|
||||
$canEdit = in_array($alignment->individual_id, $editableIds);
|
||||
$alignmentEntryHtml = $this->Bootstrap->node('span', ['class' => ['fw-bold']], h($alignment['type']));
|
||||
$alignmentEntryHtml .= ' @ ' . $this->Bootstrap->node('span', ['class' => ['ms-1']], sprintf(
|
||||
'<a href="%s/organisations/view/%s">%s</a>',
|
||||
$baseurl,
|
||||
h($alignment['organisation']['id']),
|
||||
h($alignment['organisation']['name'])
|
||||
),);
|
||||
if ($canRemove && !empty($canEdit)) {
|
||||
$alignmentEntryHtml .= $this->Bootstrap->button([
|
||||
'icon' => 'trash',
|
||||
'variant' => 'link',
|
||||
'class' => ['ms-1', 'p-0'
|
||||
],
|
||||
'onclick' => sprintf(
|
||||
"UI.submissionModalForSinglePage(%s);",
|
||||
sprintf(
|
||||
"'/alignments/delete/%s'",
|
||||
$alignment['id']
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
]);
|
||||
}
|
||||
$alignments .= sprintf('<div>%s</div>', $alignmentEntryHtml);
|
||||
}
|
||||
} else if ($field['scope'] === 'organisations') {
|
||||
foreach ($raw_alignments as $alignment) {
|
||||
$alignments .= sprintf(
|
||||
'<div>[<span class="fw-bold">%s</span>] %s <a href="#" class="fas fa-trash .text-reset .text-decoration-none" onClick="%s"></a></div>',
|
||||
h($alignment['type']),
|
||||
sprintf(
|
||||
'<a href="%s/individuals/view/%s">%s</a>',
|
||||
$baseurl,
|
||||
h($alignment['individual']['id']),
|
||||
h($alignment['individual']['email'])
|
||||
),
|
||||
!$canRemove ? '' : sprintf(
|
||||
"UI.submissionModalForIndex(%s);",
|
||||
sprintf(
|
||||
"'/alignments/delete/%s'",
|
||||
h($alignment['id'])
|
||||
$canEdit = in_array($alignment->organisation_id, $editableIds);
|
||||
$alignmentEntryHtml = '[' . $this->Bootstrap->node('span', ['class' => ['fw-bold']], h($alignment['type'])) . ']';
|
||||
$alignmentEntryHtml .= $this->Bootstrap->node('span', ['class' => ['ms-1']], sprintf(
|
||||
'<a href="%s/organisations/view/%s">%s</a>',
|
||||
$baseurl,
|
||||
h($alignment['individual']['id']),
|
||||
h($alignment['individual']['email'])
|
||||
),);
|
||||
if ($canRemove && !empty($canEdit)) {
|
||||
$alignmentEntryHtml .= $this->Bootstrap->button([
|
||||
'icon' => 'trash',
|
||||
'variant' => 'link',
|
||||
'class' => ['ms-1', 'p-0'],
|
||||
'onclick' => sprintf(
|
||||
"UI.submissionModalForSinglePage(%s);",
|
||||
sprintf(
|
||||
"'/alignments/delete/%s'",
|
||||
$alignment['id']
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
]);
|
||||
}
|
||||
$alignments .= sprintf('<div>%s</div>', $alignmentEntryHtml);
|
||||
}
|
||||
}
|
||||
echo $alignments;
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
$data = $this->Hash->extract($row, $field['data_path']);
|
||||
$links = [];
|
||||
foreach ($data as $object) {
|
||||
$temp_id = h($this->Hash->extract($object, $field['data_id_sub_path'])[0]);
|
||||
$temp_value = h($this->Hash->extract($object, $field['data_value_sub_path'])[0]);
|
||||
$url = str_replace('{{data_id}}', $temp_id, $field['url_pattern']);
|
||||
$links[] = sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
$url,
|
||||
$temp_value
|
||||
);
|
||||
}
|
||||
echo implode('<br />', $links);
|
||||
?>
|
|
@ -2,7 +2,9 @@
|
|||
if (!isset($data['requirement']) || $data['requirement']) {
|
||||
$elements = '';
|
||||
foreach ($data['children'] as $element) {
|
||||
$elements .= $this->element('/genericElements/ListTopBar/element_' . (empty($element['type']) ? 'simple' : h($element['type'])), array('data' => $element, 'tableRandomValue' => $tableRandomValue));
|
||||
if (!isset($element['requirement']) || $element['requirement']) {
|
||||
$elements .= $this->element('/genericElements/ListTopBar/element_' . (empty($element['type']) ? 'simple' : h($element['type'])), array('data' => $element, 'tableRandomValue' => $tableRandomValue));
|
||||
}
|
||||
}
|
||||
echo sprintf(
|
||||
'<div %s class="btn-group btn-group-sm me-2 flex-wrap" role="group" aria-label="button-group">%s</div>',
|
||||
|
|
|
@ -63,6 +63,7 @@ $numberOfElementHtml = $this->element('/genericElements/ListTopBar/group_table_a
|
|||
'tableSettings' => $tableSettings,
|
||||
'table_setting_id' => $data['table_setting_id'],
|
||||
'numberOfElement' => $numberOfElement,
|
||||
'includeAll' => !empty($table_data['includeAllPagination']),
|
||||
]);
|
||||
?>
|
||||
<?php if (!isset($data['requirement']) || $data['requirement']) : ?>
|
||||
|
|
|
@ -10,7 +10,9 @@ $numberOfElementSelectSeed = 'seed-' . mt_rand();
|
|||
<option value="50" <?= $numberOfElement == 50 ? 'selected' : '' ?>><?= __('50') ?></option>
|
||||
<option value="100" <?= $numberOfElement == 100 ? 'selected' : '' ?>><?= __('100') ?></option>
|
||||
<option value="200" <?= $numberOfElement == 200 ? 'selected' : '' ?>><?= __('200') ?></option>
|
||||
<option value="all" <?= $numberOfElement == 'all' ? 'selected' : '' ?>><?= __('All') ?></option>
|
||||
<?php if (!empty($includeAll)): ?>
|
||||
<option value="all" <?= $numberOfElement == 'all' ? 'selected' : '' ?>><?= __('All') ?></option>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
|
|
|
@ -10,52 +10,64 @@ if (!empty($field['path'])) {
|
|||
}
|
||||
if ($field['scope'] === 'individuals') {
|
||||
foreach ($extracted['alignments'] as $alignment) {
|
||||
$alignments .= sprintf(
|
||||
'<div><span class="fw-bold">%s</span> @ %s <a href="#" class="fas fa-trash" onClick="%s"></a></div>',
|
||||
h($alignment['type']),
|
||||
sprintf(
|
||||
'<a href="%s/organisations/view/%s">%s</a>',
|
||||
$baseurl,
|
||||
h($alignment['organisation']['id']),
|
||||
h($alignment['organisation']['name'])
|
||||
),
|
||||
sprintf(
|
||||
"UI.submissionModalForSinglePage(%s);",
|
||||
sprintf(
|
||||
"'/alignments/delete/%s'",
|
||||
$alignment['id']
|
||||
$alignmentEntryHtml = $this->Bootstrap->node('span', ['class' => ['fw-bold']], h($alignment['type']));
|
||||
$alignmentEntryHtml .= ' @ ' . $this->Bootstrap->node('span', ['class' => ['ms-1']], sprintf(
|
||||
'<a href="%s/organisations/view/%s">%s</a>',
|
||||
$baseurl,
|
||||
h($alignment['organisation']['id']),
|
||||
h($alignment['organisation']['name'])
|
||||
),);
|
||||
if (!empty($canEdit)) {
|
||||
$alignmentEntryHtml .= $this->Bootstrap->button([
|
||||
'icon' => 'trash',
|
||||
'variant' => 'link',
|
||||
'class' => ['ms-1', 'p-0'],
|
||||
'onclick' => sprintf(
|
||||
"UI.submissionModalForSinglePage(%s);",
|
||||
sprintf(
|
||||
"'/alignments/delete/%s'",
|
||||
$alignment['id']
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
]);
|
||||
}
|
||||
$alignments .= sprintf('<div>%s</div>', $alignmentEntryHtml);
|
||||
}
|
||||
} else if ($field['scope'] === 'organisations') {
|
||||
foreach ($extracted['alignments'] as $alignment) {
|
||||
$alignments .= sprintf(
|
||||
'<div>[<span class="fw-bold">%s</span>] %s <a href="#" class="fas fa-trash" onClick="%s"></a></div>',
|
||||
h($alignment['type']),
|
||||
sprintf(
|
||||
'<a href="%s/individuals/view/%s">%s</a>',
|
||||
$baseurl,
|
||||
h($alignment['individual']['id']),
|
||||
h($alignment['individual']['email'])
|
||||
),
|
||||
sprintf(
|
||||
"UI.submissionModalForSinglePage(%s);",
|
||||
sprintf(
|
||||
"'/alignments/delete/%s'",
|
||||
$alignment['id']
|
||||
$alignmentEntryHtml = '[' . $this->Bootstrap->node('span', ['class' => ['fw-bold']], h($alignment['type'])) . ']';
|
||||
$alignmentEntryHtml .= $this->Bootstrap->node('span', ['class' => ['ms-1']], sprintf(
|
||||
'<a href="%s/organisations/view/%s">%s</a>',
|
||||
$baseurl,
|
||||
h($alignment['individual']['id']),
|
||||
h($alignment['individual']['email'])
|
||||
),);
|
||||
if (!empty($canEdit)) {
|
||||
$alignmentEntryHtml .= $this->Bootstrap->button([
|
||||
'icon' => 'trash',
|
||||
'variant' => 'link',
|
||||
'class' => ['ms-1', 'p-0'],
|
||||
'onclick' => sprintf(
|
||||
"UI.submissionModalForSinglePage(%s);",
|
||||
sprintf(
|
||||
"'/alignments/delete/%s'",
|
||||
$alignment['id']
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
]);
|
||||
}
|
||||
$alignments .= sprintf('<div>%s</div>', $alignmentEntryHtml);
|
||||
}
|
||||
}
|
||||
echo sprintf(
|
||||
'<div class="alignments-list">%s</div><div class="alignments-add-container"><button class="alignments-add-button btn btn-primary btn-sm" onclick="%s">%s</button></div>',
|
||||
$alignments,
|
||||
sprintf(
|
||||
"UI.submissionModalForSinglePage('/alignments/add/%s/%s');",
|
||||
h($field['scope']),
|
||||
h($extracted['id'])
|
||||
),
|
||||
$field['scope'] === 'individuals' ? __('Add organisation') : __('Add individual')
|
||||
);
|
||||
echo sprintf('<div class="alignments-list">%s</div>', $alignments);
|
||||
if (!empty($canEdit)) {
|
||||
echo sprintf(
|
||||
'<div class="alignments-add-container"><button class="alignments-add-button btn btn-primary btn-sm" onclick="%s">%s</button></div>',
|
||||
sprintf(
|
||||
"UI.submissionModalForSinglePage('/alignments/add/%s/%s');",
|
||||
h($field['scope']),
|
||||
h($extracted['id'])
|
||||
),
|
||||
$field['scope'] === 'individuals' ? __('Add organisation') : __('Add individual')
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
$data = $this->Hash->extract($data, $field['path']);
|
||||
$links = [];
|
||||
foreach ($data as $object) {
|
||||
$temp_id = h($this->Hash->extract($object, $field['data_id_sub_path'])[0]);
|
||||
$temp_value = h($this->Hash->extract($object, $field['data_value_sub_path'])[0]);
|
||||
$url = str_replace('{{data_id}}', $temp_id, $field['url_pattern']);
|
||||
$links[] = sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
$url,
|
||||
$temp_value
|
||||
);
|
||||
}
|
||||
echo implode('<br />', $links);
|
||||
?>
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
$form = $this->element('genericElements/Form/genericForm', [
|
||||
'entity' => null,
|
||||
'ajax' => false,
|
||||
'raw' => true,
|
||||
'data' => [
|
||||
'submit' => [
|
||||
'action' => $this->request->getParam('action')
|
||||
]
|
||||
]
|
||||
]);
|
||||
$formHTML = sprintf('<div class="d-none">%s</div>', $form);
|
||||
$bodyMessage = !empty($deletionText) ? h($deletionText) : __(
|
||||
'Are you sure you want to detach {2} #{3} from {0} #{1}?',
|
||||
h($data[0]['model']),
|
||||
h($data[0]['id']),
|
||||
h($data[1]['model']),
|
||||
h($data[1]['id'])
|
||||
);
|
||||
|
||||
$bodyHTML = sprintf('%s%s', $formHTML, $bodyMessage);
|
||||
|
||||
echo $this->Bootstrap->modal([
|
||||
'size' => 'lg',
|
||||
'title' => !empty($deletionTitle) ? $deletionTitle : __(
|
||||
'Detach {0} from {1}',
|
||||
h($data[0]['model']),
|
||||
h($data[1]['model']),
|
||||
),
|
||||
'type' => 'confirm',
|
||||
'confirmButton' => [
|
||||
'text' => !empty($deletionConfirm) ? $deletionConfirm : __('Detach'),
|
||||
'variant' => 'danger',
|
||||
],
|
||||
'bodyHtml' => $bodyHTML,
|
||||
]);
|
||||
?>
|
Loading…
Reference in New Issue