From cd459c653e113d0c6fb24b8ed8051b4b75e96ef9 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Fri, 5 Aug 2022 18:02:19 -0400 Subject: [PATCH] Afform - fill entity based on existing entity selection --- .../afGuiEditor/elements/afGuiField-menu.html | 6 ---- .../elements/afGuiField.component.js | 4 --- .../Civi/Afform/AfformMetadataInjector.php | 2 +- .../Api4/Action/Afform/AbstractProcessor.php | 34 +++++++++++++++++-- .../Subscriber/AutocompleteSubscriber.php | 2 +- ext/afform/core/ang/af/afEntity.component.js | 2 ++ ext/afform/core/ang/af/afField.component.js | 7 ++++ .../core/ang/af/afFieldset.directive.js | 3 ++ ext/afform/core/ang/af/afForm.component.js | 33 ++++++++++++------ ext/afform/core/ang/af/fields/Existing.html | 10 +++++- 10 files changed, 77 insertions(+), 26 deletions(-) diff --git a/ext/afform/admin/ang/afGuiEditor/elements/afGuiField-menu.html b/ext/afform/admin/ang/afGuiEditor/elements/afGuiField-menu.html index fa12d3f843..6896abf1b2 100644 --- a/ext/afform/admin/ang/afGuiEditor/elements/afGuiField-menu.html +++ b/ext/afform/admin/ang/afGuiEditor/elements/afGuiField-menu.html @@ -18,12 +18,6 @@ {{:: ts('Required') }} -
  • - - - {{:: ts('Disable Permission Checks') }} - -
  • diff --git a/ext/afform/admin/ang/afGuiEditor/elements/afGuiField.component.js b/ext/afform/admin/ang/afGuiEditor/elements/afGuiField.component.js index b17c029442..177e9b1700 100644 --- a/ext/afform/admin/ang/afGuiEditor/elements/afGuiField.component.js +++ b/ext/afform/admin/ang/afGuiEditor/elements/afGuiField.component.js @@ -224,10 +224,6 @@ getSet('required', !getSet('required')); }; - $scope.toggleSkipPermissions = function() { - getSet('skip_permissions', !getSet('skip_permissions')); - }; - $scope.toggleHelp = function(position) { getSet('help_' + position, $scope.propIsset('help_' + position) ? null : (ctrl.getDefn()['help_' + position] || ts('Enter text'))); }; diff --git a/ext/afform/core/Civi/Afform/AfformMetadataInjector.php b/ext/afform/core/Civi/Afform/AfformMetadataInjector.php index be6cc1a16c..b84bdfb324 100644 --- a/ext/afform/core/Civi/Afform/AfformMetadataInjector.php +++ b/ext/afform/core/Civi/Afform/AfformMetadataInjector.php @@ -180,7 +180,7 @@ class AfformMetadataInjector { } } // Id field for selecting existing entity - if ($field['name'] === CoreUtil::getIdFieldName($entityName)) { + if ($action === 'update' && $field['name'] === CoreUtil::getIdFieldName($entityName)) { $entityTitle = CoreUtil::getInfoItem($entityName, 'title'); $field['input_type'] = 'Existing'; $field['entity'] = $entityName; diff --git a/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php b/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php index 870254d1c1..17c25b78f3 100644 --- a/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php +++ b/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php @@ -68,9 +68,13 @@ abstract class AbstractProcessor extends \Civi\Api4\Generic\AbstractAction { protected function loadEntities() { foreach ($this->_formDataModel->getEntities() as $entityName => $entity) { $this->_entityIds[$entityName] = []; + $idField = CoreUtil::getIdFieldName($entity['type']); if (!empty($entity['actions']['update'])) { - if (!empty($this->args[$entityName]) && !empty($entity['url-autofill'])) { - $ids = array_map('trim', explode(',', $this->args[$entityName])); + if ( + !empty($this->args[$entityName]) && + (!empty($entity['url-autofill']) || isset($entity['fields'][$idField])) + ) { + $ids = (array) $this->args[$entityName]; // Limit number of records to 1 unless using af-repeat $ids = array_slice($ids, 0, !empty($entity['af-repeat']) ? $entity['max'] ?? NULL : 1); $this->loadEntity($entity, $ids); @@ -90,10 +94,17 @@ abstract class AbstractProcessor extends \Civi\Api4\Generic\AbstractAction { */ private function loadEntity(array $entity, array $ids) { $api4 = $this->_formDataModel->getSecureApi4($entity['name']); + $idField = CoreUtil::getIdFieldName($entity['type']); + if (!empty($entity['fields'][$idField]['saved_search'])) { + $ids = $this->validateBySavedSearch($entity, $idField, $ids); + } + if (!$ids) { + return; + } $result = $api4($entity['type'], 'get', [ 'where' => [['id', 'IN', $ids]], 'select' => array_keys($entity['fields']), - ])->indexBy('id'); + ])->indexBy($idField); foreach ($ids as $index => $id) { $this->_entityIds[$entity['name']][$index] = [ 'id' => isset($result[$id]) ? $id : NULL, @@ -133,6 +144,23 @@ abstract class AbstractProcessor extends \Civi\Api4\Generic\AbstractAction { } } + private function validateBySavedSearch($entity, array $ids) { + $idField = CoreUtil::getIdFieldName($entity['type']); + $fetched = civicrm_api4($entity['type'], 'autocomplete', [ + 'ids' => $ids, + 'formName' => 'afform:' . $this->name, + 'fieldName' => $entity['name'] . ':' . $idField, + ])->indexBy($idField); + $validIds = []; + // Preserve keys + foreach ($ids as $index => $id) { + if (isset($fetched[$id])) { + $validIds[$index] = $id; + } + } + return $validIds; + } + /** * @return array */ diff --git a/ext/afform/core/Civi/Api4/Subscriber/AutocompleteSubscriber.php b/ext/afform/core/Civi/Api4/Subscriber/AutocompleteSubscriber.php index 2b5335f475..ece48d0932 100644 --- a/ext/afform/core/Civi/Api4/Subscriber/AutocompleteSubscriber.php +++ b/ext/afform/core/Civi/Api4/Subscriber/AutocompleteSubscriber.php @@ -57,7 +57,7 @@ class AutocompleteSubscriber implements EventSubscriberInterface { if (!empty($entity['fields'][$fieldName]['defn'])) { $defn = \CRM_Utils_JS::decode($entity['fields'][$fieldName]['defn']); } - $apiRequest->setCheckPermissions(empty($defn['skip_permissions'])); + $apiRequest->setCheckPermissions($entity['security'] !== 'FBAC'); $apiRequest->setSavedSearch($defn['saved_search'] ?? NULL); } } diff --git a/ext/afform/core/ang/af/afEntity.component.js b/ext/afform/core/ang/af/afEntity.component.js index 6f0fef14d2..c778856ff2 100644 --- a/ext/afform/core/ang/af/afEntity.component.js +++ b/ext/afform/core/ang/af/afEntity.component.js @@ -4,6 +4,7 @@ var modelProps = { type: '@', data: '=', + actions: '=', modelName: '@name', label: '@', autofill: '@' @@ -16,6 +17,7 @@ this.$onInit = function() { var entity = _.pick(this, _.keys(modelProps)); + entity.actions = entity.actions || {update: true, create: true}; entity.id = null; this.afForm.registerEntity(entity); }; diff --git a/ext/afform/core/ang/af/afField.component.js b/ext/afform/core/ang/af/afField.component.js index aa83eac8fb..1e0cda0dbc 100644 --- a/ext/afform/core/ang/af/afField.component.js +++ b/ext/afform/core/ang/af/afField.component.js @@ -169,6 +169,13 @@ } }; + ctrl.onSelectExisting = function() { + var val = $scope.getSetSelect(); + var entity = ctrl.afFieldset.modelName; + var index = ctrl.getEntityIndex(); + ctrl.afFieldset.afFormCtrl.loadData(entity, index, val); + }; + // Params for the Afform.submitFile API when uploading a file field ctrl.getFileUploadParams = function() { return { diff --git a/ext/afform/core/ang/af/afFieldset.directive.js b/ext/afform/core/ang/af/afFieldset.directive.js index 384fad9b90..d67216de05 100644 --- a/ext/afform/core/ang/af/afFieldset.directive.js +++ b/ext/afform/core/ang/af/afFieldset.directive.js @@ -24,6 +24,9 @@ // If there is no Afform entity, get the name of embedded search display $element.find('[search-name][display-name]').attr('display-name'); }; + this.getEntity = function() { + return this.afFormCtrl.getEntity(this.modelName); + }; this.getEntityType = function() { return this.afFormCtrl.getEntity(this.modelName).type; }; diff --git a/ext/afform/core/ang/af/afForm.component.js b/ext/afform/core/ang/af/afForm.component.js index 79514948a4..314d710cc5 100644 --- a/ext/afform/core/ang/af/afForm.component.js +++ b/ext/afform/core/ang/af/afForm.component.js @@ -37,16 +37,30 @@ this.getFormMeta = function getFormMeta() { return $scope.$parent.meta; }; - this.loadData = function() { - var toLoad = 0; - args = _.assign({}, $scope.$parent.routeParams || {}, $scope.$parent.options || {}); - _.each(schema, function(entity, entityName) { - if (args[entityName] || entity.autofill) { - toLoad++; - } - }); + // With no arguments this will prefill the entire form based on url args + // With selectedEntity, selectedIndex & selectedId provided this will prefill a single entity + this.loadData = function(selectedEntity, selectedIndex, selectedId) { + var toLoad = 0, + params = {name: ctrl.getFormMeta().name, args: {}}; + // Load single entity + if (selectedEntity) { + toLoad = selectedId; + params.args[selectedEntity] = {}; + params.args[selectedEntity][selectedIndex] = selectedId; + } else { + args = _.assign({}, $scope.$parent.routeParams || {}, $scope.$parent.options || {}); + _.each(schema, function (entity, entityName) { + if (args[entityName] || entity.autofill) { + toLoad++; + } + if (args[entityName] && typeof args[entityName] === 'string') { + args[entityName] = args[entityName].split(','); + } + }); + params.args = args; + } if (toLoad) { - crmApi4('Afform', 'prefill', {name: ctrl.getFormMeta().name, args: args}) + crmApi4('Afform', 'prefill', params) .then(function(result) { _.each(result, function(item) { data[item.name] = data[item.name] || {}; @@ -98,7 +112,6 @@ function replaceTokens(str, vars) { function recurse(stack, values) { _.each(values, function(value, key) { - console.log('value:' + value, stack); if (_.isArray(value) || _.isPlainObject(value)) { recurse(stack.concat([key]), value); } else { diff --git a/ext/afform/core/ang/af/fields/Existing.html b/ext/afform/core/ang/af/fields/Existing.html index 10855d4927..7824c6dfdc 100644 --- a/ext/afform/core/ang/af/fields/Existing.html +++ b/ext/afform/core/ang/af/fields/Existing.html @@ -1 +1,9 @@ - + -- 2.25.1