diff --git a/libraries/default/RequestProcessors/BroodRequestProcessor.php b/libraries/default/RequestProcessors/BroodRequestProcessor.php new file mode 100644 index 0000000..13a0c33 --- /dev/null +++ b/libraries/default/RequestProcessors/BroodRequestProcessor.php @@ -0,0 +1,108 @@ +description = __('Handle tool interconnection request from other cerebrate instance'); + $this->Broods = TableRegistry::getTableLocator()->get('Broods'); + } + + protected function addValidatorRules($validator) + { + return $validator; + } + + public function create($requestData) { + $this->validateRequestData($requestData); + $requestData['title'] = __('Cerebrate instance {0} requested interconnection for tool {1}', 'Insert brood name', 'Insert tool name'); + return parent::create($requestData); + } + + public function process($id, $requestData) + { + $connectionSuccessfull = false; + $interConnectionResult = []; + if ($connectionSuccessfull) { + $this->discard($id, $requestData); + } + return $this->genActionResult( + $interConnectionResult, + $connectionSuccessfull, + $connectionSuccessfull ? __('Interconnection for `{0}` created', 'Insert tool name') : __('Could interconnect tool `{0}`.', 'Insert tool name'), + [] + ); + } + + public function discard($id, $requestData) + { + return parent::discard($id, $requestData); + } +} + +class OneWaySynchronizationProcessor extends BroodRequestProcessor implements GenericProcessorActionI { + public $action = 'OneWaySynchronization'; + protected $description; + + public function __construct() { + parent::__construct(); + $this->description = __('Handle cerebrate connection request for another cerebrate instance'); + $this->Broods = TableRegistry::getTableLocator()->get('Broods'); + } + + protected function addValidatorRules($validator) + { + return $validator; + } + + public function create($requestData) { + $this->validateRequestData($requestData); + $requestData['title'] = __('Cerebrate instance {0} requested interconnection', 'Insert cerebrate name'); + return parent::create($requestData); + } + + public function process($id, $requestData) + { + $connectionSuccessfull = false; + $interConnectionResult = []; + if ($connectionSuccessfull) { + $this->discard($id, $requestData); + } + return $this->genActionResult( + $interConnectionResult, + $connectionSuccessfull, + $connectionSuccessfull ? __('Interconnection with `{0}` created', 'Insert cerebrate name') : __('Could interconnect with `{0}`.', 'Insert cerebrate name'), + [] + ); + } + + public function discard($id, $requestData) + { + return parent::discard($id, $requestData); + } +} \ No newline at end of file diff --git a/libraries/default/RequestProcessors/GenericRequestProcessor.php b/libraries/default/RequestProcessors/GenericRequestProcessor.php new file mode 100644 index 0000000..b5d2300 --- /dev/null +++ b/libraries/default/RequestProcessors/GenericRequestProcessor.php @@ -0,0 +1,202 @@ +Inbox = TableRegistry::getTableLocator()->get('Inbox'); + if ($registerActions) { + $this->registerActionInProcessor(); + } + $processingTemplatePath = $this->getProcessingTemplatePath(); + $file = new File($this->processingTemplatesDirectory . DS . $processingTemplatePath); + if ($file->exists()) { + $this->processingTemplate = str_replace('.php', '', $processingTemplatePath); + } + $file->close(); + } + + public function getRegisteredActions() + { + return $this->registeredActions; + } + public function getScope() + { + return $this->scope; + } + + private function getProcessingTemplatePath() + { + $class = str_replace('RequestProcessor', '', get_parent_class($this)); + $action = strtolower(str_replace('Processor', '', get_class($this))); + return sprintf('%s/%s.php', + $class, + $action + ); + } + + public function getProcessingTemplate() + { + return $this->processingTemplate; + } + + public function render($request=[]) + { + $processingTemplate = $this->getProcessingTemplate(); + $viewVariables = $this->getViewVariables($request); + $builder = new ViewBuilder(); + $builder->disableAutoLayout() + ->setClassName('Monad') + ->setTemplate($processingTemplate); + $view = $builder->build($viewVariables); + return $view->render(); + } + + protected function generateRequest($requestData) + { + $request = $this->Inbox->newEmptyEntity(); + $request = $this->Inbox->patchEntity($request, $requestData); + if ($request->getErrors()) { + throw new Exception(__('Could not create request.{0}Reason: {1}', PHP_EOL, json_encode($request->getErrors())), 1); + } + return $request; + } + + protected function validateRequestData($requestData) + { + $errors = []; + if (!isset($requestData['data'])) { + $errors[] = __('No request data provided'); + } + $validator = new Validator(); + if (method_exists($this, 'addValidatorRules')) { + $validator = $this->addValidatorRules($validator); + $errors = $validator->validate($requestData['data']); + } + if (!empty($errors)) { + throw new Exception('Error while validating request data. ' . json_encode($errors), 1); + } + } + + protected function registerActionInProcessor() + { + foreach ($this->registeredActions as $i => $action) { + $className = "{$action}Processor"; + $reflection = new ReflectionClass($className); + if ($reflection->isAbstract() || $reflection->isInterface()) { + throw new Exception(__('Cannot create instance of %s, as it is abstract or is an interface')); + } + $this->{$action} = $reflection->newInstance(); + } + } + + protected function getViewVariablesConfirmModal($id, $title='', $question='', $actionName='') + { + return [ + 'title' => !empty($title) ? $title : __('Process request {0}', $id), + 'question' => !empty($question) ? $question : __('Confirm request {0}', $id), + 'actionName' => !empty($actionName) ? $actionName : __('Confirm'), + 'path' => ['controller' => 'inbox', 'action' => 'process', $id] + ]; + } + + public function getViewVariables($request) + { + return $this->getViewVariablesConfirmModal($request->id, '', '', ''); + } + + protected function genActionResult($data, $success, $message, $errors=[]) + { + return [ + 'data' => $data, + 'success' => $success, + 'message' => $message, + 'errors' => $errors, + ]; + } + + public function genHTTPReply($controller, $processResult, $redirect=null) + { + $scope = $this->scope; + $action = $this->action; + if ($processResult['success']) { + $message = !empty($processResult['message']) ? $processResult['message'] : __('Request {0} successfully processed.', $id); + if ($controller->ParamHandler->isRest()) { + $response = $controller->RestResponse->viewData($processResult, 'json'); + } else if ($controller->ParamHandler->isAjax()) { + $response = $controller->RestResponse->ajaxSuccessResponse('RequestProcessor', "{$scope}.{$action}", $processResult['data'], $message); + } else { + $controller->Flash->success($message); + if (!is_null($redirect)) { + $response = $controller->redirect($redirect); + } else { + $response = $controller->redirect(['action' => 'index']); + } + } + } else { + $message = !empty($processResult['message']) ? $processResult['message'] : __('Request {0} could not be processed.', $id); + if ($controller->ParamHandler->isRest()) { + $response = $controller->RestResponse->viewData($processResult, 'json'); + } else if ($controller->ParamHandler->isAjax()) { + $response = $controller->RestResponse->ajaxFailResponse('RequestProcessor', "{$scope}.{$action}", $processResult['data'], $message, $processResult['errors']); + } else { + $controller->Flash->error($message); + if (!is_null($redirect)) { + $response = $controller->redirect($redirect); + } else { + $response = $controller->redirect(['action' => 'index']); + } + } + } + + return $response; + } + + public function checkLoading() + { + return 'Assimilation successful!'; + } + + public function create($requestData) + { + $requestData['scope'] = $this->scope; + $requestData['action'] = $this->action; + $requestData['description'] = $this->description; + $request = $this->generateRequest($requestData); + $savedRequest = $this->Inbox->save($request); + return $this->genActionResult( + $savedRequest, + $savedRequest !== false, + __('{0} request for {1} created', $this->scope, $this->action), + $request->getErrors() + ); + } + + public function discard($id, $requestData) + { + $request = $this->Inbox->get($id); + $this->Inbox->delete($request); + return $this->genActionResult( + [], + true, + __('{0}.{1} request #{2} discarded', $this->scope, $this->action, $id) + ); + } +} diff --git a/libraries/default/RequestProcessors/ProposalRequestProcessor.php b/libraries/default/RequestProcessors/ProposalRequestProcessor.php new file mode 100644 index 0000000..3421edf --- /dev/null +++ b/libraries/default/RequestProcessors/ProposalRequestProcessor.php @@ -0,0 +1,65 @@ +description = __('Handle proposal from users for this cerebrate instance'); + $this->Users = TableRegistry::getTableLocator()->get('Users'); + } + + protected function addValidatorRules($validator) + { + return $validator; + } + + public function create($requestData) { + $this->validateRequestData($requestData); + $requestData['title'] = __('User `{0}` would like to modify record `{0}`', 'username', 'recordname'); + return parent::create($requestData); + } + + public function process($id, $requestData) + { + $proposalAccepted = false; + $saveResult = []; + if ($proposalAccepted) { + $this->discard($id, $requestData); + } + return $this->genActionResult( + $saveResult, + $proposalAccepted, + $proposalAccepted ? __('Record `{0}` modify', 'recordname') : __('Could modify record `{0}`.', 'recordname'), + [] + ); + } + + public function discard($id, $requestData) + { + return parent::discard($id, $requestData); + } +} \ No newline at end of file diff --git a/libraries/default/RequestProcessors/SynchronisationRequestProcessor.php b/libraries/default/RequestProcessors/SynchronisationRequestProcessor.php new file mode 100644 index 0000000..ae63ad5 --- /dev/null +++ b/libraries/default/RequestProcessors/SynchronisationRequestProcessor.php @@ -0,0 +1,65 @@ +description = __('Handle exchange of data between two cerebrate instances'); + $this->Users = TableRegistry::getTableLocator()->get('Users'); + } + + protected function addValidatorRules($validator) + { + return $validator; + } + + public function create($requestData) { + $this->validateRequestData($requestData); + $requestData['title'] = __('Data exchange requested for record `{0}`', 'recordname'); + return parent::create($requestData); + } + + public function process($id, $requestData) + { + $dataExchangeAccepted = false; + $saveResult = []; + if ($dataExchangeAccepted) { + $this->discard($id, $requestData); + } + return $this->genActionResult( + $saveResult, + $dataExchangeAccepted, + $dataExchangeAccepted ? __('Record `{0}` exchanged', 'recordname') : __('Could not exchange record `{0}`.', 'recordname'), + [] + ); + } + + public function discard($id, $requestData) + { + return parent::discard($id, $requestData); + } +} \ No newline at end of file diff --git a/libraries/default/RequestProcessors/TemplateRequestProcessor.php.template b/libraries/default/RequestProcessors/TemplateRequestProcessor.php.template new file mode 100644 index 0000000..6adb4c0 --- /dev/null +++ b/libraries/default/RequestProcessors/TemplateRequestProcessor.php.template @@ -0,0 +1,65 @@ +description = __('~to-be-defined~'); + $this->Users = TableRegistry::getTableLocator()->get('Users'); + } + + protected function addValidatorRules($validator) + { + return $validator; + } + + public function create($requestData) { + $this->validateRequestData($requestData); + $requestData['title'] = __('~to-be-defined~'); + return parent::create($requestData); + } + + public function process($id, $requestData) + { + $proposalAccepted = false; + $saveResult = []; + if ($proposalAccepted) { + $this->discard($id, $requestData); + } + return $this->genActionResult( + $saveResult, + $proposalAccepted, + $proposalAccepted ? __('success') : __('fail'), + [] + ); + } + + public function discard($id, $requestData) + { + return parent::discard($id, $requestData); + } +} \ No newline at end of file diff --git a/libraries/default/RequestProcessors/UserRequestProcessor.php b/libraries/default/RequestProcessors/UserRequestProcessor.php new file mode 100644 index 0000000..3120bde --- /dev/null +++ b/libraries/default/RequestProcessors/UserRequestProcessor.php @@ -0,0 +1,121 @@ +Users = TableRegistry::getTableLocator()->get('Users'); + } + + public function create($requestData) + { + return parent::create($requestData); + } +} + +class RegistrationProcessor extends UserRequestProcessor implements GenericProcessorActionI { + public $action = 'Registration'; + protected $description; + + public function __construct() { + parent::__construct(); + $this->description = __('Handle user account for this cerebrate instance'); + } + + protected function addValidatorRules($validator) + { + return $validator + ->notEmpty('username', 'A username must be provided.') + ->add('email', 'validFormat', [ + 'rule' => 'email', + 'message' => 'E-mail must be valid' + ]) + ->notEmpty('first_name', 'A first name must be provided') + ->notEmpty('last_name', 'A last name must be provided'); + } + + public function create($requestData) { + $this->validateRequestData($requestData); + $requestData['title'] = __('User account creation requested for {0}', $requestData['data']['email']); + return parent::create($requestData); + } + + public function getViewVariables($request) + { + $dropdownData = [ + 'role' => $this->Users->Roles->find('list', [ + 'sort' => ['name' => 'asc'] + ]), + 'individual' => [-1 => __('-- New individual --')] + $this->Users->Individuals->find('list', [ + 'sort' => ['email' => 'asc'] + ])->toArray() + ]; + $individualEntity = $this->Users->Individuals->newEntity([ + 'email' => !empty($request['data']['email']) ? $request['data']['email'] : '', + 'first_name' => !empty($request['data']['first_name']) ? $request['data']['first_name'] : '', + 'last_name' => !empty($request['data']['last_name']) ? $request['data']['last_name'] : '', + 'position' => !empty($request['data']['position']) ? $request['data']['position'] : '', + ]); + $userEntity = $this->Users->newEntity([ + 'individual_id' => -1, + 'username' => !empty($request['data']['username']) ? $request['data']['username'] : '', + 'role_id' => !empty($request['data']['role_id']) ? $request['data']['role_id'] : '', + 'disabled' => !empty($request['data']['disabled']) ? $request['data']['disabled'] : '', + ]); + return [ + 'dropdownData' => $dropdownData, + 'userEntity' => $userEntity, + 'individualEntity' => $individualEntity + ]; + } + + public function process($id, $requestData) + { + if ($requestData['individual_id'] == -1) { + $individual = $this->Users->Individuals->newEntity([ + 'uuid' => $requestData['uuid'], + 'email' => $requestData['email'], + 'first_name' => $requestData['first_name'], + 'last_name' => $requestData['last_name'], + 'position' => $requestData['position'], + ]); + $individual = $this->Users->Individuals->save($individual); + } else { + $individual = $this->Users->Individuals->get($requestData['individual_id']); + } + $user = $this->Users->newEntity([ + 'individual_id' => $individual->id, + 'username' => $requestData['username'], + 'password' => '~PASSWORD_TO_BE_REPLACED~', + 'role_id' => $requestData['role_id'], + 'disabled' => $requestData['disabled'], + ]); + $user = $this->Users->save($user); + + if ($user !== false) { + $this->discard($id, $requestData); + } + return $this->genActionResult( + $user, + $user !== false, + $user !== false ? __('User `{0}` created', $user->username) : __('Could not create user `{0}`.', $user->username), + $user->getErrors() + ); + } + + public function discard($id, $requestData) + { + return parent::discard($id, $requestData); + } +} \ No newline at end of file diff --git a/libraries/default/RequestProcessors/templates/User/registration.php b/libraries/default/RequestProcessors/templates/User/registration.php new file mode 100644 index 0000000..ebcc819 --- /dev/null +++ b/libraries/default/RequestProcessors/templates/User/registration.php @@ -0,0 +1,125 @@ +element('genericElements/Form/genericForm', [ + 'entity' => $userEntity, + 'ajax' => false, + 'raw' => true, + 'data' => [ + 'description' => __('Create user account'), + 'model' => 'User', + 'fields' => [ + [ + 'field' => 'individual_id', + 'type' => 'dropdown', + 'label' => __('Associated individual'), + 'options' => $dropdownData['individual'], + ], + [ + 'field' => 'username', + 'autocomplete' => 'off', + ], + [ + 'field' => 'role_id', + 'type' => 'dropdown', + 'label' => __('Role'), + 'options' => $dropdownData['role'] + ], + [ + 'field' => 'disabled', + 'type' => 'checkbox', + 'label' => 'Disable' + ] + ], + 'submit' => [ + 'action' => $this->request->getParam('action') + ] + ] + ]); + + $formIndividual = $this->element('genericElements/Form/genericForm', [ + 'entity' => $individualEntity, + 'ajax' => false, + 'raw' => true, + 'data' => [ + 'description' => __('Create individual'), + 'model' => 'Individual', + 'fields' => [ + [ + 'field' => 'email', + 'autocomplete' => 'off' + ], + [ + 'field' => 'uuid', + 'label' => 'UUID', + 'type' => 'uuid', + 'autocomplete' => 'off' + ], + [ + 'field' => 'first_name', + 'autocomplete' => 'off' + ], + [ + 'field' => 'last_name', + 'autocomplete' => 'off' + ], + [ + 'field' => 'position', + 'autocomplete' => 'off' + ], + ], + 'submit' => [ + 'action' => $this->request->getParam('action') + ] + ] + ]); + + echo $this->Bootstrap->modal([ + 'title' => __('Register user'), + 'size' => 'lg', + 'type' => 'confirm', + 'bodyHtml' => sprintf('
%s
%s
', + $formUser, + $formIndividual + ), + 'confirmText' => __('Create user'), + 'confirmFunction' => 'submitRegistration' + ]); +?> + + + + + \ No newline at end of file diff --git a/src/Controller/AppController.php b/src/Controller/AppController.php index 87f968d..70116d9 100644 --- a/src/Controller/AppController.php +++ b/src/Controller/AppController.php @@ -107,6 +107,12 @@ class AppController extends Controller } else if ($this->ParamHandler->isRest()) { throw new MethodNotAllowedException(__('Invalid user credentials.')); } + + if ($this->request->getParam('action') === 'index') { + $this->Security->setConfig('validatePost', false); + } + $this->Security->setConfig('unlockedActions', ['index']); + $this->ACL->checkAccess(); $this->set('menu', $this->ACL->getMenu()); $this->set('ajax', $this->request->is('ajax')); diff --git a/src/Controller/BroodsController.php b/src/Controller/BroodsController.php index 7796324..d954f77 100644 --- a/src/Controller/BroodsController.php +++ b/src/Controller/BroodsController.php @@ -6,6 +6,7 @@ use App\Controller\AppController; use Cake\Utility\Hash; use Cake\Utility\Text; use \Cake\Database\Expression\QueryExpression; +use Cake\ORM\TableRegistry; class BroodsController extends AppController { @@ -154,4 +155,22 @@ class BroodsController extends AppController $this->redirect($this->referer()); } } + + public function interconnectTools() + { + $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); + $processor = $this->requestProcessor->getProcessor('Brood', 'ToolInterconnection'); + $data = [ + 'origin' => '127.0.0.1', + 'comment' => 'Test comment', + 'user_id' => $this->ACL->getUser()->id, + 'data' => [ + 'foo' => 'foo', + 'bar' => 'bar', + 'baz' => 'baz', + ], + ]; + $processorResult = $processor->create($data); + return $processor->genHTTPReply($this, $processorResult, ['controller' => 'Broods', 'action' => 'index']); + } } diff --git a/src/Controller/Component/ACLComponent.php b/src/Controller/Component/ACLComponent.php index 2a5a648..eec94ce 100644 --- a/src/Controller/Component/ACLComponent.php +++ b/src/Controller/Component/ACLComponent.php @@ -642,6 +642,29 @@ class ACLComponent extends Component ] ] ], + 'Inbox' => [ + 'label' => __('Inbox'), + 'url' => '/inbox/index', + 'children' => [ + 'index' => [ + 'url' => '/inbox/index', + 'label' => __('Inbox') + ], + 'view' => [ + 'url' => '/inbox/view/{{id}}', + 'label' => __('View Meta Template'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1 + ], + 'delete' => [ + 'url' => '/inbox/delete/{{id}}', + 'label' => __('Delete Meta Template'), + 'actions' => ['delete', 'edit', 'view'], + 'skipTopMenu' => 1, + 'popup' => 1 + ] + ] + ], 'MetaTemplates' => [ 'label' => __('Meta Field Templates'), 'url' => '/metaTemplates/index', diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index 32d3717..31de16c 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -165,7 +165,7 @@ class CRUDComponent extends Component $message = __( '{0} could not be added.{1}', $this->ObjectAlias, - empty($validationMessage) ? '' : ' ' . __('Reason:{0}', $validationMessage) + empty($validationMessage) ? '' : PHP_EOL . __('Reason:{0}', $validationMessage) ); if ($this->Controller->ParamHandler->isRest()) { } else if ($this->Controller->ParamHandler->isAjax()) { @@ -508,7 +508,7 @@ class CRUDComponent extends Component if (is_bool($contextFromField)) { $contextFromFieldText = sprintf('%s: %s', $field, $contextFromField ? 'true' : 'false'); } else { - $contextFromFieldText = $contextFromField; + $contextFromFieldText = sprintf('%s: %s', $field, $contextFromField); } $filteringContexts[] = [ 'label' => Inflector::humanize($contextFromFieldText), diff --git a/src/Controller/Component/RestResponseComponent.php b/src/Controller/Component/RestResponseComponent.php index 96b3725..4a7346f 100644 --- a/src/Controller/Component/RestResponseComponent.php +++ b/src/Controller/Component/RestResponseComponent.php @@ -423,11 +423,12 @@ class RestResponseComponent extends Component public function ajaxSuccessResponse($ObjectAlias, $action, $entity, $message, $additionalData=[]) { $action = $this->__dissectAdminRouting($action); + $entity = is_array($entity) ? $entity : $entity->toArray(); $response = [ 'success' => true, 'message' => $message, - 'data' => $entity->toArray() - //'url' => $this->__generateURL($action, $ObjectAlias, $entity->id) + 'data' => $entity, + 'url' => !empty($entity['id']) ? $this->__generateURL($action, $ObjectAlias, $entity['id']) : '' ]; if (!empty($additionalData)) { $response['additionalData'] = $additionalData; @@ -438,11 +439,12 @@ class RestResponseComponent extends Component public function ajaxFailResponse($ObjectAlias, $action, $entity, $message, $errors = []) { $action = $this->__dissectAdminRouting($action); + $entity = is_array($entity) ? $entity : $entity->toArray(); $response = [ 'success' => false, 'message' => $message, 'errors' => $errors, - 'url' => $this->__generateURL($action, $ObjectAlias, $entity->id) + 'url' => !empty($entity['id']) ? $this->__generateURL($action, $ObjectAlias, $entity['id']) : '' ]; return $this->viewData($response); } diff --git a/src/Controller/EncryptionKeysController.php b/src/Controller/EncryptionKeysController.php index afcc611..a9bf699 100644 --- a/src/Controller/EncryptionKeysController.php +++ b/src/Controller/EncryptionKeysController.php @@ -18,7 +18,7 @@ class EncryptionKeysController extends AppController { $this->CRUD->index([ 'quickFilters' => ['encryption_key'], - 'filters' => ['owner_type', 'organisation_id', 'individual_id', 'encryption_key'], + 'filters' => ['owner_model', 'organisation_id', 'individual_id', 'encryption_key'], 'contextFilters' => [ 'fields' => [ 'type' diff --git a/src/Controller/InboxController.php b/src/Controller/InboxController.php new file mode 100644 index 0000000..6c0f978 --- /dev/null +++ b/src/Controller/InboxController.php @@ -0,0 +1,131 @@ +set('metaGroup', 'Administration'); + } + + + public function index() + { + $this->CRUD->index([ + 'filters' => $this->filters, + 'quickFilters' => ['scope', 'action', ['title' => true], ['comment' => true]], + 'contextFilters' => [ + 'fields' => [ + 'scope', + 'action', + ] + ], + 'contain' => ['Users'] + ]); + $responsePayload = $this->CRUD->getResponsePayload(); + if (!empty($responsePayload)) { + return $responsePayload; + } + } + + public function filtering() + { + $this->CRUD->filtering(); + } + + public function view($id) + { + $this->CRUD->view($id); + $responsePayload = $this->CRUD->getResponsePayload(); + if (!empty($responsePayload)) { + return $responsePayload; + } + } + + public function delete($id) + { + if ($this->request->is('post')) { + $request = $this->Inbox->get($id); + $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); + $processor = $this->requestProcessor->getProcessor($request->scope, $request->action); + $discardResult = $processor->discard($id, $request); + return $processor->genHTTPReply($this, $discardResult); + } + $this->set('deletionTitle', __('Discard request')); + $this->set('deletionText', __('Are you sure you want to discard request #{0}?', $id)); + $this->set('deletionConfirm', __('Discard')); + $this->CRUD->delete($id); + $responsePayload = $this->CRUD->getResponsePayload(); + if (!empty($responsePayload)) { + return $responsePayload; + } + } + + public function process($id) + { + $request = $this->Inbox->get($id); + $scope = $request->scope; + $action = $request->action; + $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); + $processor = $this->requestProcessor->getProcessor($request->scope, $request->action); + if ($this->request->is('post')) { + $processResult = $processor->process($id, $this->request->getData()); + return $processor->genHTTPReply($this, $processResult); + } else { + $renderedView = $processor->render($request); + return $this->response->withStringBody($renderedView); + } + } + + public function listProcessors() + { + $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); + $requestProcessors = $this->requestProcessor->listProcessors(); + if ($this->ParamHandler->isRest()) { + return $this->RestResponse->viewData($requestProcessors, 'json'); + } + $data = []; + foreach ($requestProcessors as $scope => $processors) { + foreach ($processors as $processor) { + $data[] = [ + 'enabled' => $processor->enabled, + 'scope' => $scope, + 'action' => $processor->action + ]; + } + } + $this->set('title', 'Available request processors'); + $this->set('fields', [ + [ + 'name' => 'Enabled', + 'data_path' => 'enabled', + 'element' => 'boolean' + ], + [ + 'name' => 'Processor scope', + 'data_path' => 'scope', + ], + [ + 'name' => 'Processor action', + 'data_path' => 'action', + ] + ]); + $this->set('data', $data); + $this->render('/genericTemplates/index_simple'); + } +} diff --git a/src/Controller/UsersController.php b/src/Controller/UsersController.php index dadca75..8a40151 100644 --- a/src/Controller/UsersController.php +++ b/src/Controller/UsersController.php @@ -4,6 +4,7 @@ namespace App\Controller; use App\Controller\AppController; use Cake\Utility\Hash; use Cake\Utility\Text; +use Cake\ORM\TableRegistry; use \Cake\Database\Expression\QueryExpression; class UsersController extends AppController @@ -134,4 +135,22 @@ class UsersController extends AppController return $this->redirect(\Cake\Routing\Router::url('/users/login')); } } + + public function register() + { + $this->requestProcessor = TableRegistry::getTableLocator()->get('RequestProcessor'); + $processor = $this->requestProcessor->getProcessor('User', 'Registration'); + $data = [ + 'origin' => '127.0.0.1', + 'comment' => 'Hi there!, please create an account', + 'data' => [ + 'username' => 'foobar', + 'email' => 'foobar@admin.test', + 'first_name' => 'foo', + 'last_name' => 'bar', + ], + ]; + $processorResult = $processor->create($data); + return $processor->genHTTPReply($this, $processorResult, ['controller' => 'Inbox', 'action' => 'index']); + } } diff --git a/src/Model/Entity/Inbox.php b/src/Model/Entity/Inbox.php new file mode 100644 index 0000000..a8f546e --- /dev/null +++ b/src/Model/Entity/Inbox.php @@ -0,0 +1,11 @@ + 'owner_id', - 'conditions' => ['owner_type' => 'individual'] + 'conditions' => ['owner_model' => 'individual'] ] ); $this->belongsTo( 'Organisations', [ 'foreignKey' => 'owner_id', - 'conditions' => ['owner_type' => 'organisation'] + 'conditions' => ['owner_model' => 'organisation'] ] ); $this->setDisplayField('encryption_key'); @@ -34,13 +34,13 @@ class EncryptionKeysTable extends AppTable public function beforeMarshal(EventInterface $event, ArrayObject $data, ArrayObject $options) { if (empty($data['owner_id'])) { - if (empty($data['owner_type'])) { + if (empty($data['owner_model'])) { return false; } - if (empty($data[$data['owner_type'] . '_id'])) { + if (empty($data[$data['owner_model'] . '_id'])) { return false; } - $data['owner_id'] = $data[$data['owner_type'] . '_id']; + $data['owner_id'] = $data[$data['owner_model'] . '_id']; } } @@ -50,8 +50,8 @@ class EncryptionKeysTable extends AppTable ->notEmptyString('type') ->notEmptyString('encryption_key') ->notEmptyString('owner_id') - ->notEmptyString('owner_type') - ->requirePresence(['type', 'encryption_key', 'owner_id', 'owner_type'], 'create'); + ->notEmptyString('owner_model') + ->requirePresence(['type', 'encryption_key', 'owner_id', 'owner_model'], 'create'); return $validator; } } diff --git a/src/Model/Table/InboxTable.php b/src/Model/Table/InboxTable.php new file mode 100644 index 0000000..93a7f22 --- /dev/null +++ b/src/Model/Table/InboxTable.php @@ -0,0 +1,65 @@ +addBehavior('UUID'); + $this->addBehavior('Timestamp', [ + 'events' => [ + 'Model.beforeSave' => [ + 'created' => 'new' + ] + ] + ]); + + $this->belongsTo('Users'); + $this->setDisplayField('title'); + } + + protected function _initializeSchema(TableSchemaInterface $schema): TableSchemaInterface + { + $schema->setColumnType('data', 'json'); + + return $schema; + } + + public function validationDefault(Validator $validator): Validator + { + $validator + ->notEmptyString('scope') + ->notEmptyString('action') + ->notEmptyString('title') + ->notEmptyString('origin') + ->datetime('created') + + ->requirePresence([ + 'scope' => ['message' => __('The field `scope` is required')], + 'action' => ['message' => __('The field `action` is required')], + 'title' => ['message' => __('The field `title` is required')], + 'origin' => ['message' => __('The field `origin` is required')], + ], 'create'); + return $validator; + } + + public function buildRules(RulesChecker $rules): RulesChecker + { + $rules->add($rules->existsIn('user_id', 'Users'), [ + 'message' => 'The provided `user_id` does not exist' + ]); + + return $rules; + } +} diff --git a/src/Model/Table/IndividualsTable.php b/src/Model/Table/IndividualsTable.php index 1611a5f..d0df577 100644 --- a/src/Model/Table/IndividualsTable.php +++ b/src/Model/Table/IndividualsTable.php @@ -25,7 +25,7 @@ class IndividualsTable extends AppTable 'EncryptionKeys', [ 'foreignKey' => 'owner_id', - 'conditions' => ['owner_type' => 'individual'] + 'conditions' => ['owner_model' => 'individual'] ] ); $this->hasOne( diff --git a/src/Model/Table/OrganisationsTable.php b/src/Model/Table/OrganisationsTable.php index 446d3fa..c23ee7b 100644 --- a/src/Model/Table/OrganisationsTable.php +++ b/src/Model/Table/OrganisationsTable.php @@ -30,7 +30,7 @@ class OrganisationsTable extends AppTable [ 'dependent' => true, 'foreignKey' => 'owner_id', - 'conditions' => ['owner_type' => 'organisation'] + 'conditions' => ['owner_model' => 'organisation'] ] ); $this->hasMany( diff --git a/src/Model/Table/RequestProcessorTable.php b/src/Model/Table/RequestProcessorTable.php new file mode 100644 index 0000000..e76fdab --- /dev/null +++ b/src/Model/Table/RequestProcessorTable.php @@ -0,0 +1,95 @@ + [ + 'ToolInterconnection' => false, + 'OneWaySynchronization' => false, + ], + 'Proposal' => [ + 'ProposalEdit' => false, + ], + 'Synchronisation' => [ + 'DataExchange' => false, + ], + 'User' => [ + 'Registration' => true, + ], + ]; + + public function initialize(array $config): void + { + parent::initialize($config); + $this->loadProcessors(); + } + + public function getProcessor($scope, $action=null) + { + if (isset($this->requestProcessors[$scope])) { + if (is_null($action)) { + return $this->requestProcessors[$scope]; + } else if (!empty($this->requestProcessors[$scope]->{$action})) { + return $this->requestProcessors[$scope]->{$action}; + } else { + throw new \Exception(__('Processor {0}.{1} not found', $scope, $action)); + } + } + throw new \Exception(__('Processor not found'), 1); + } + + public function listProcessors($scope=null) + { + if (is_null($scope)) { + return $this->requestProcessors; + } else { + if (isset($this->requestProcessors[$scope])) { + return $this->requestProcessors[$scope]; + } else { + throw new \Exception(__('Processors for {0} not found', $scope)); + } + } + } + + private function loadProcessors() + { + $processorDir = new Folder($this->processorsDirectory); + $processorFiles = $processorDir->find('.*RequestProcessor\.php', true); + foreach ($processorFiles as $processorFile) { + if ($processorFile == 'GenericRequestProcessor.php') { + continue; + } + $processorMainClassName = str_replace('.php', '', $processorFile); + $processorMainClassNameShort = str_replace('RequestProcessor.php', '', $processorFile); + $processorMainClass = $this->getProcessorClass($processorDir->pwd() . DS . $processorFile, $processorMainClassName); + if ($processorMainClass !== false) { + $this->requestProcessors[$processorMainClassNameShort] = $processorMainClass; + foreach ($this->requestProcessors[$processorMainClassNameShort]->getRegisteredActions() as $registeredAction) { + $scope = $this->requestProcessors[$processorMainClassNameShort]->getScope(); + if (!empty($this->enabledProcessors[$scope][$registeredAction])) { + $this->requestProcessors[$processorMainClassNameShort]->{$registeredAction}->enabled = true; + } else { + $this->requestProcessors[$processorMainClassNameShort]->{$registeredAction}->enabled = false; + } + } + } + } + } + + private function getProcessorClass($filePath, $processorMainClassName) + { + require_once($filePath); + $reflection = new \ReflectionClass($processorMainClassName); + $processorMainClass = $reflection->newInstance(true); + if ($processorMainClass->checkLoading() === 'Assimilation successful!') { + return $processorMainClass; + } + } +} diff --git a/src/View/Helper/BootstrapHelper.php b/src/View/Helper/BootstrapHelper.php index 2309d87..b63e5f6 100644 --- a/src/View/Helper/BootstrapHelper.php +++ b/src/View/Helper/BootstrapHelper.php @@ -721,7 +721,7 @@ class BoostrapModal extends BootstrapGeneric { 'footerClass' => [''], 'type' => 'ok-only', 'variant' => '', - 'confirmFunction' => '', + 'confirmFunction' => '', // Will be called with the following arguments confirmFunction(modalObject, tmpApi) 'cancelFunction' => '' ]; @@ -822,7 +822,7 @@ class BoostrapModal extends BootstrapGeneric { 'variant' => 'primary', 'text' => __('Ok'), 'params' => [ - 'data-dismiss' => 'modal', + 'data-dismiss' => $this->options['confirmFunction'] ? '' : 'modal', 'onclick' => $this->options['confirmFunction'] ] ]))->button(); @@ -847,9 +847,10 @@ class BoostrapModal extends BootstrapGeneric { $buttonConfirm = (new BoostrapButton([ 'variant' => $variant, 'text' => h($this->options['confirmText']), + 'class' => 'modal-confirm-button', 'params' => [ - 'data-dismiss' => 'modal', - 'onclick' => $this->options['confirmFunction'] + 'data-dismiss' => $this->options['confirmFunction'] ? '' : 'modal', + 'data-confirmFunction' => sprintf('%s', $this->options['confirmFunction']) ] ]))->button(); return $buttonCancel . $buttonConfirm; diff --git a/src/View/MonadView.php b/src/View/MonadView.php new file mode 100644 index 0000000..6c72ef3 --- /dev/null +++ b/src/View/MonadView.php @@ -0,0 +1,34 @@ +additionalTemplatePaths); + return $paths; + } +} diff --git a/templates/EncryptionKeys/add.php b/templates/EncryptionKeys/add.php index 6a80c39..3f1800e 100644 --- a/templates/EncryptionKeys/add.php +++ b/templates/EncryptionKeys/add.php @@ -6,7 +6,7 @@ echo $this->element('genericElements/Form/genericForm', [ 'model' => 'Organisations', 'fields' => [ [ - 'field' => 'owner_type', + 'field' => 'owner_model', 'label' => __('Owner type'), 'options' => array_combine(array_keys($dropdownData), array_keys($dropdownData)), 'type' => 'dropdown' @@ -17,7 +17,7 @@ echo $this->element('genericElements/Form/genericForm', [ 'options' => $dropdownData['organisation'] ?? [], 'type' => 'dropdown', 'stateDependence' => [ - 'source' => '#owner_type-field', + 'source' => '#owner_model-field', 'option' => 'organisation' ] ], @@ -27,7 +27,7 @@ echo $this->element('genericElements/Form/genericForm', [ 'options' => $dropdownData['individual'] ?? [], 'type' => 'dropdown', 'stateDependence' => [ - 'source' => '#owner_type-field', + 'source' => '#owner_model-field', 'option' => 'individual' ] ], diff --git a/templates/EncryptionKeys/index.php b/templates/EncryptionKeys/index.php index fed2abd..5017319 100644 --- a/templates/EncryptionKeys/index.php +++ b/templates/EncryptionKeys/index.php @@ -45,7 +45,7 @@ echo $this->element('genericElements/IndexTable/index_table', [ [ 'name' => __('Owner'), 'data_path' => 'owner_id', - 'owner_type_path' => 'owner_type', + 'owner_model_path' => 'owner_model', 'element' => 'owner' ], [ diff --git a/templates/Inbox/index.php b/templates/Inbox/index.php new file mode 100644 index 0000000..55eabd3 --- /dev/null +++ b/templates/Inbox/index.php @@ -0,0 +1,99 @@ +Html->scriptBlock(sprintf( + 'var csrfToken = %s;', + json_encode($this->request->getAttribute('csrfToken')) +)); +echo $this->element('genericElements/IndexTable/index_table', [ + 'data' => [ + 'data' => $data, + 'top_bar' => [ + 'children' => [ + [ + 'type' => 'context_filters', + 'context_filters' => !empty($filteringContexts) ? $filteringContexts : [] + ], + [ + 'type' => 'search', + 'button' => __('Filter'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value', + 'allowFilering' => true + ] + ] + ], + 'fields' => [ + [ + 'name' => '#', + 'sort' => 'id', + 'data_path' => 'id', + ], + [ + 'name' => 'created', + 'sort' => 'created', + 'data_path' => 'created', + 'element' => 'datetime' + ], + [ + 'name' => 'scope', + 'sort' => 'scope', + 'data_path' => 'scope', + ], + [ + 'name' => 'action', + 'sort' => 'action', + 'data_path' => 'action', + ], + [ + 'name' => 'title', + 'sort' => 'title', + 'data_path' => 'title', + ], + [ + 'name' => 'origin', + 'sort' => 'origin', + 'data_path' => 'origin', + ], + [ + 'name' => 'user', + 'sort' => 'user_id', + 'data_path' => 'user', + 'element' => 'user' + ], + [ + 'name' => 'description', + 'sort' => 'description', + 'data_path' => 'description', + ], + [ + 'name' => 'comment', + 'sort' => 'comment', + 'data_path' => 'comment', + ], + ], + 'title' => __('Inbox'), + 'description' => __('A list of requests to be manually processed'), + 'actions' => [ + [ + 'url' => '/inbox/view', + 'url_params_data_paths' => ['id'], + 'icon' => 'eye', + 'title' => __('View request') + ], + [ + 'open_modal' => '/inbox/process/[onclick_params_data_path]', + 'modal_params_data_path' => 'id', + 'icon' => 'cogs', + 'title' => __('Process request') + ], + [ + 'open_modal' => '/inbox/delete/[onclick_params_data_path]', + 'modal_params_data_path' => 'id', + 'icon' => 'trash', + 'title' => __('Discard request') + ], + ] + ] +]); +echo ''; +?> diff --git a/templates/Inbox/view.php b/templates/Inbox/view.php new file mode 100644 index 0000000..36e0dfc --- /dev/null +++ b/templates/Inbox/view.php @@ -0,0 +1,51 @@ +element( + '/genericElements/SingleViews/single_view', + [ + 'data' => $entity, + 'fields' => [ + [ + 'key' => __('ID'), + 'path' => 'id' + ], + [ + 'key' => 'created', + 'path' => 'created', + ], + [ + 'key' => 'scope', + 'path' => 'scope', + ], + [ + 'key' => 'action', + 'path' => 'action', + ], + [ + 'key' => 'title', + 'path' => 'title', + ], + [ + 'key' => 'origin', + 'path' => 'origin', + ], + [ + 'key' => 'user_id', + 'path' => 'user_id', + ], + [ + 'key' => 'description', + 'path' => 'description', + ], + [ + 'key' => 'comment', + 'path' => 'comment', + ], + [ + 'key' => 'data', + 'path' => 'data', + 'type' => 'json' + ], + ], + 'children' => [] + ] +); diff --git a/templates/Instance/migration_index.php b/templates/Instance/migration_index.php index b554dd1..f2bef2e 100644 --- a/templates/Instance/migration_index.php +++ b/templates/Instance/migration_index.php @@ -51,7 +51,7 @@ function runAllUpdate() { type: 'confirm-success', confirmText: '', APIConfirm: (tmpApi) => { - tmpApi.fetchAndPostForm(url, {}).then(() => { + return tmpApi.fetchAndPostForm(url, {}).then(() => { location.reload() }) }, diff --git a/templates/element/genericElements/Form/genericForm.php b/templates/element/genericElements/Form/genericForm.php index 6e642ff..9e3ba2e 100644 --- a/templates/element/genericElements/Form/genericForm.php +++ b/templates/element/genericElements/Form/genericForm.php @@ -115,6 +115,24 @@ 'actionButton' => $this->element('genericElements/Form/submitButton', $submitButtonData), 'class' => 'modal-lg' ]); + } else if (!empty($raw)) { + echo sprintf( + '%s%s%s%s%s%s', + empty($data['description']) ? '' : sprintf( + '
%s
', + $data['description'] + ), + $ajaxFlashMessage, + $formCreate, + $fieldsString, + empty($metaTemplateString) ? '' : $this->element( + 'genericElements/accordion_scaffold', [ + 'body' => $metaTemplateString, + 'title' => 'Meta fields' + ] + ), + $formEnd + ); } else { echo sprintf( '%s

%s

%s%s%s%s%s%s%s%s%s', diff --git a/templates/element/genericElements/Form/submitButton.php b/templates/element/genericElements/Form/submitButton.php index 9a2a82c..134e857 100644 --- a/templates/element/genericElements/Form/submitButton.php +++ b/templates/element/genericElements/Form/submitButton.php @@ -3,7 +3,7 @@ echo sprintf( '%s', sprintf( - '', + '', '#form-' . h($formRandomValue), __('Submit') ) diff --git a/templates/element/genericElements/IndexTable/Fields/actions.php b/templates/element/genericElements/IndexTable/Fields/actions.php index 4e4bf61..e0d275b 100644 --- a/templates/element/genericElements/IndexTable/Fields/actions.php +++ b/templates/element/genericElements/IndexTable/Fields/actions.php @@ -97,7 +97,7 @@ ); } $reload_url = !empty($action['reload_url']) ? $action['reload_url'] : $this->Url->build(['action' => 'index']); - $action['onclick'] = sprintf('UI.openModalFromURL(\'%s\', \'%s\', \'%s\')', $modal_url, $reload_url, $tableRandomValue); + $action['onclick'] = sprintf('UI.submissionModalForIndex(\'%s\', \'%s\', \'%s\')', $modal_url, $reload_url, $tableRandomValue); } echo sprintf( ' ', diff --git a/templates/element/genericElements/IndexTable/Fields/alignments.php b/templates/element/genericElements/IndexTable/Fields/alignments.php index 611f990..f761c2a 100644 --- a/templates/element/genericElements/IndexTable/Fields/alignments.php +++ b/templates/element/genericElements/IndexTable/Fields/alignments.php @@ -14,7 +14,7 @@ if ($field['scope'] === 'individuals') { h($alignment['organisation']['name']) ), !$canRemove ? '' : sprintf( - "UI.openModalFromURL(%s);", + "UI.submissionModalForIndex(%s);", sprintf( "'/alignments/delete/%s'", h($alignment['id']) @@ -34,7 +34,7 @@ if ($field['scope'] === 'individuals') { h($alignment['individual']['email']) ), !$canRemove ? '' : sprintf( - "UI.openModalFromURL(%s);", + "UI.submissionModalForIndex(%s);", sprintf( "'/alignments/delete/%s'", h($alignment['id']) diff --git a/templates/element/genericElements/IndexTable/Fields/owner.php b/templates/element/genericElements/IndexTable/Fields/owner.php index 17d491f..aa16e41 100644 --- a/templates/element/genericElements/IndexTable/Fields/owner.php +++ b/templates/element/genericElements/IndexTable/Fields/owner.php @@ -1,5 +1,5 @@ Hash->extract($row, $field['owner_type_path'])[0]; + $type = $this->Hash->extract($row, $field['owner_model_path'])[0]; $owner = $row[$type]; $types = [ 'individual' => [ diff --git a/templates/element/genericElements/IndexTable/Fields/user.php b/templates/element/genericElements/IndexTable/Fields/user.php new file mode 100644 index 0000000..25504fc --- /dev/null +++ b/templates/element/genericElements/IndexTable/Fields/user.php @@ -0,0 +1,11 @@ +Hash->extract($row, 'user.id')[0]; + $userName = $this->Hash->extract($row, 'user.username')[0]; + echo $this->Html->link( + h($userName), + ['controller' => 'users', 'action' => 'view', $userId] + ); + } + +?> diff --git a/templates/element/genericElements/ListTopBar/element_simple.php b/templates/element/genericElements/ListTopBar/element_simple.php index 6747eb1..a14976f 100644 --- a/templates/element/genericElements/ListTopBar/element_simple.php +++ b/templates/element/genericElements/ListTopBar/element_simple.php @@ -71,12 +71,8 @@ \ No newline at end of file diff --git a/templates/element/genericElements/ListTopBar/group_search.php b/templates/element/genericElements/ListTopBar/group_search.php index 8e85f39..7822e07 100644 --- a/templates/element/genericElements/ListTopBar/group_search.php +++ b/templates/element/genericElements/ListTopBar/group_search.php @@ -12,6 +12,7 @@ * - id: element ID for the input field - defaults to quickFilterField */ if (!isset($data['requirement']) || $data['requirement']) { + $filterEffective = !empty($quickFilter); // No filters will be picked up, thus rendering the filtering useless $filteringButton = ''; if (!empty($data['allowFilering'])) { $activeFilters = !empty($activeFilters) ? $activeFilters : []; @@ -32,9 +33,10 @@ $filteringButton = $this->Bootstrap->button($buttonConfig); } $button = empty($data['button']) && empty($data['fa-icon']) ? '' : sprintf( - '
%s
', + '
%s
', empty($data['data']) ? '' : h($data['data']), h($tableRandomValue), + $filterEffective ? '' : 'disabled="disabled"', empty($data['fa-icon']) ? '' : sprintf('', h($data['fa-icon'])), empty($data['button']) ? '' : h($data['button']), $filteringButton @@ -43,13 +45,14 @@ $button .= $this->element('/genericElements/ListTopBar/element_simple', array('data' => $data['cancel'])); } $input = sprintf( - '', + '', h($tableRandomValue), empty($data['placeholder']) ? '' : h($data['placeholder']), empty($data['placeholder']) ? '' : h($data['placeholder']), empty($data['id']) ? 'quickFilterField' : h($data['id']), empty($data['searchKey']) ? 'searchall' : h($data['searchKey']), - empty($data['value']) ? (!empty($quickFilterValue) ? h($quickFilterValue) : '') : h($data['value']) + empty($data['value']) ? (!empty($quickFilterValue) ? h($quickFilterValue) : '') : h($data['value']), + $filterEffective ? '' : 'disabled="disabled"' ); echo sprintf( '
%s%s
', @@ -127,7 +130,8 @@ } $searchType = $('') .text(searchContain ? '' : '') - .attr('title', searchContain ? '' : '') + .attr('title', searchContain ? '' : '') + .attr('style', 'cursor: help;') tableData.push([fieldName, $searchType]) }); tableData.sort((a, b) => a[0] < b[0] ? -1 : 1) @@ -144,11 +148,7 @@ } function openFilteringModal(clicked, url, reloadUrl, tableId) { - const loadingOverlay = new OverlayFactory(clicked); - loadingOverlay.show() - UI.openModalFromURL(url, reloadUrl, tableId).finally(() => { - loadingOverlay.hide() - }) + UI.overlayUntilResolve(clicked, UI.submissionModalForIndex(url, reloadUrl, tableId)) } }); diff --git a/templates/element/genericElements/ListTopBar/group_simple.php b/templates/element/genericElements/ListTopBar/group_simple.php index 2c79909..187b616 100644 --- a/templates/element/genericElements/ListTopBar/group_simple.php +++ b/templates/element/genericElements/ListTopBar/group_simple.php @@ -5,7 +5,7 @@ $elements .= $this->element('/genericElements/ListTopBar/element_' . (empty($element['type']) ? 'simple' : h($element['type'])), array('data' => $element, 'tableRandomValue' => $tableRandomValue)); } echo sprintf( - '
%s
', + '
%s
', (!empty($data['id'])) ? 'id="' . h($data['id']) . '"' : '', $elements ); diff --git a/templates/element/genericElements/SingleViews/Fields/alignmentField.php b/templates/element/genericElements/SingleViews/Fields/alignmentField.php index eb922b9..50e9162 100644 --- a/templates/element/genericElements/SingleViews/Fields/alignmentField.php +++ b/templates/element/genericElements/SingleViews/Fields/alignmentField.php @@ -20,7 +20,7 @@ if ($field['scope'] === 'individuals') { h($alignment['organisation']['name']) ), sprintf( - "UI.openModalFromURL(%s);", + "UI.submissionModalForSinglePage(%s);", sprintf( "'/alignments/delete/%s'", $alignment['id'] @@ -40,7 +40,7 @@ if ($field['scope'] === 'individuals') { h($alignment['individual']['email']) ), sprintf( - "UI.openModalFromURL(%s);", + "UI.submissionModalForSinglePage(%s);", sprintf( "'/alignments/delete/%s'", $alignment['id'] @@ -53,7 +53,7 @@ echo sprintf( '
%s
', $alignments, sprintf( - "UI.openModalFromURL('/alignments/add/%s/%s');", + "UI.submissionModalForSinglePage('/alignments/add/%s/%s');", h($field['scope']), h($extracted['id']) ), diff --git a/templates/element/genericElements/side_menu_scaffold.php b/templates/element/genericElements/side_menu_scaffold.php index bd6ab03..9e8eab5 100644 --- a/templates/element/genericElements/side_menu_scaffold.php +++ b/templates/element/genericElements/side_menu_scaffold.php @@ -35,7 +35,7 @@ if (isset($menu[$metaGroup])) { } $active = ($scope === $this->request->getParam('controller') && $action === $this->request->getParam('action')); if (!empty($data['popup'])) { - $link_template = '%s'; + $link_template = '%s'; } else { $link_template = '%s'; } diff --git a/templates/genericTemplates/confirm.php b/templates/genericTemplates/confirm.php index ea4c3b2..0d2fbd9 100644 --- a/templates/genericTemplates/confirm.php +++ b/templates/genericTemplates/confirm.php @@ -13,7 +13,7 @@ Form->postLink( h($actionName), $path, - ['class' => 'btn btn-primary button-execute'] + ['class' => 'btn btn-primary button-execute', 'id' => 'submitButton'] ) ?> diff --git a/templates/genericTemplates/delete.php b/templates/genericTemplates/delete.php index 7da0be7..6b07d6a 100644 --- a/templates/genericTemplates/delete.php +++ b/templates/genericTemplates/delete.php @@ -1,7 +1,13 @@