Add api for afform prefill
authorColeman Watts <coleman@civicrm.org>
Sun, 25 Aug 2019 23:44:55 +0000 (19:44 -0400)
committerCiviCRM <info@civicrm.org>
Wed, 16 Sep 2020 02:13:19 +0000 (19:13 -0700)
ext/afform/core/CRM/Afform/Page/AfformBase.php
ext/afform/core/Civi/Afform/Utils.php [new file with mode: 0644]
ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php [new file with mode: 0644]
ext/afform/core/Civi/Api4/Action/Afform/Prefill.php [new file with mode: 0644]
ext/afform/core/Civi/Api4/Afform.php
ext/afform/core/ang/af/ModelList.js
ext/afform/core/ang/af/ModelProp.js

index 91bec9509c522a152978e427ce42d4f9ac461a1c..3d389528b605092d1f3961d6682536952c11ebfe 100644 (file)
@@ -18,7 +18,7 @@ class CRM_Afform_Page_AfformBase extends CRM_Core_Page {
     ]);
     $loader->load();
 
-    $afform = civicrm_api4('Afform', 'get', ['where' => [['name', '=', $pageArgs['afform']]], 'select' => ['title']]);
+    $afform = civicrm_api4('Afform', 'get', ['checkPermissions' => FALSE, 'where' => [['name', '=', $pageArgs['afform']]], 'select' => ['title']]);
 
     if (!empty($afform[0]['title'])) {
       CRM_Utils_System::setTitle(strip_tags($afform[0]['title']));
diff --git a/ext/afform/core/Civi/Afform/Utils.php b/ext/afform/core/Civi/Afform/Utils.php
new file mode 100644 (file)
index 0000000..4a89307
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+
+namespace Civi\Afform;
+
+class Utils {
+
+  /**
+   * Gets entity metadata and all fields from the form
+   *
+   * @param array $layout
+   * @return array
+   */
+  public static function getEntities($layout) {
+    $entities = array_column(self::getTags($layout, 'af-model-prop'), NULL, 'af-name');
+    self::getFields($layout, $entities);
+    return $entities;
+  }
+
+  /**
+   * Returns all tags with a certain tag name, e.g. 'af-model-prop'
+   *
+   * @param array $collection
+   * @param string $tagName
+   * @return array
+   */
+  public static function getTags($collection, $tagName) {
+    $results = [];
+    if ($collection['#tag'] == $tagName) {
+      $results[] = self::getProps($collection);
+    }
+    foreach ($collection['#children'] ?? [] as $child) {
+      $results = array_merge($results, self::getTags($child, $tagName));
+    }
+    return $results;
+  }
+
+  /**
+   * @param array $layout
+   * @param array $entities
+   */
+  protected static function getFields($layout, &$entities) {
+    foreach ($layout['#children'] as $child) {
+      if ($child['#tag'] == 'af-model' && !empty($child['#children'])) {
+        $entities[$child['af-name']]['fields'] = array_merge($entities[$child['af-name']]['fields'] ?? [], self::getTags($child, 'af-field'));
+      }
+      elseif (!empty($child['#children'])) {
+        self::getFields($child['#children'], $entities);
+      }
+    }
+  }
+
+  /**
+   * Returns all the real properties of a collection,
+   * filtering out any array keys that start with a hashtag
+   *
+   * @param array $collection
+   * @return array
+   */
+  public static function getProps($collection) {
+    return array_filter($collection, function($key) {
+      return substr($key, 0, 1) !== '#';
+    }, ARRAY_FILTER_USE_KEY);
+  }
+
+}
diff --git a/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php b/ext/afform/core/Civi/Api4/Action/Afform/AbstractProcessor.php
new file mode 100644 (file)
index 0000000..3c5085c
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+
+namespace Civi\Api4\Action\Afform;
+
+use Civi\Afform\Utils;
+use Civi\Api4\Generic\Result;
+
+/**
+ * Class Prefill
+ * @package Civi\Api4\Action\Afform
+ */
+abstract class AbstractProcessor extends \Civi\Api4\Generic\AbstractAction {
+
+  /**
+   * Form name
+   * @var string
+   * @required
+   */
+  protected $name;
+
+  /**
+   * Arguments present when loading the form
+   * @var array
+   */
+  protected $args;
+
+  protected $_afform;
+  
+  protected $_afformEntities;
+
+  public function _run(Result $result) {
+    // This will throw an exception if the form doesn't exist
+    $this->_afform = (array) civicrm_api4('Afform', 'get', ['checkPermissions' => FALSE, 'where' => [['name', '=', $this->name]]], 0);
+    $this->_afformEntities = Utils::getEntities($this->_afform['layout']);
+    $this->validateArgs();
+    $result->exchangeArray($this->processForm());
+  }
+
+  /**
+   * Strip out arguments that are not allowed on this form
+   */
+  protected function validateArgs() {
+    $rawArgs = $this->args;
+    $this->args = [];
+    foreach ($rawArgs as $arg => $val) {
+      if (!empty($this->_afformEntities[$arg]['af-url-autofill'])) {
+        $this->args[$arg] = $val;
+      }
+    }
+  }
+
+  /**
+   * @return array
+   */
+  abstract protected function processForm();
+
+}
diff --git a/ext/afform/core/Civi/Api4/Action/Afform/Prefill.php b/ext/afform/core/Civi/Api4/Action/Afform/Prefill.php
new file mode 100644 (file)
index 0000000..64ff218
--- /dev/null
@@ -0,0 +1,75 @@
+<?php
+
+namespace Civi\Api4\Action\Afform;
+
+/**
+ * Class Prefill
+ * @package Civi\Api4\Action\Afform
+ */
+class Prefill extends AbstractProcessor {
+
+  protected $_data = [];
+
+  protected function processForm() {
+    foreach ($this->_afformEntities as $entityName => $entity) {
+      // Load entities from args
+      if (!empty($this->args[$entityName])) {
+        $this->loadEntity($entity, $this->args[$entityName]);
+      }
+      // Load entities from autofill settings
+      elseif (!empty($entity['af-autofill'])) {
+        $this->autofillEntity($entity, $entity['af-autofill']);
+      }
+    }
+    $data = [];
+    foreach ($this->_data as $name => $values) {
+      $data[] = ['name' => $name, 'values' => $values];
+    }
+    return $data;
+  }
+
+  /**
+   * Fetch all fields needed to display a given entity on this form
+   *
+   * @param $entity
+   * @param $id
+   * @throws \API_Exception
+   */
+  private function loadEntity($entity, $id) {
+    $checkPermissions = TRUE;
+    if ($entity['af-type'] == 'Contact' && !empty($this->args[$entity['af-name'] . '-cs'])) {
+      $checkSum = civicrm_api4('Contact', 'validateChecksum', [
+        'checksum' => $this->args[$entity['af-name'] . '-cs'],
+        'contactId' => $id,
+      ]);
+      $checkPermissions = empty($checkSum[0]['valid']);
+    }
+    $result = civicrm_api4($entity['af-type'], 'get', [
+      'where' => [['id', '=', $id]],
+      'select' => array_column($entity['fields'], 'field-name'),
+      'checkPermissions' => $checkPermissions,
+    ]);
+    if ($result->first()) {
+      $this->_data[$entity['af-name']] = $result->first();
+    }
+  }
+
+  /**
+   * Fetch an entity based on its autofill settings
+   *
+   * @param $entity
+   * @param $mode
+   */
+  private function autoFillEntity($entity, $mode) {
+    $id = NULL;
+    if ($entity['af-type'] == 'Contact') {
+      if ($mode == 'user') {
+        $id = \CRM_Core_Session::getLoggedInContactID();
+      }
+    }
+    if ($id) {
+      $this->loadEntity($entity, $id);
+    }
+  }
+
+}
index ecfffa0e80975d5cca0e2ab9eab321c82b97cfed..75b2095d87e2d8e5389247c124b5600538cc0594 100644 (file)
@@ -56,6 +56,20 @@ class Afform extends AbstractEntity {
     return new \Civi\Api4\Action\Afform\Update('Afform', __FUNCTION__, 'name');
   }
 
+  /**
+   * @return \Civi\Api4\Action\Afform\Prefill
+   */
+  public static function prefill() {
+    return new \Civi\Api4\Action\Afform\Prefill('Afform', __FUNCTION__);
+  }
+
+  /**
+   * @return \Civi\Api4\Action\Afform\Submit
+   */
+  public static function submit() {
+    return new \Civi\Api4\Action\Afform\Submit('Afform', __FUNCTION__);
+  }
+
   public static function getFields() {
     return new BasicGetFieldsAction('Afform', __FUNCTION__, function() {
       return [
@@ -92,6 +106,8 @@ class Afform extends AbstractEntity {
     return [
       "meta" => ["access CiviCRM"],
       "default" => ["administer CiviCRM"],
+      'prefill' => [],
+      'submit' => [],
     ];
   }
 
index 7e28e0e6080f32bdad93a29513e39168ef0060a1..b4465a4515dff6bc68cd88ef7b276fec785a47e7 100644 (file)
@@ -30,6 +30,7 @@
         this.getEntity = function getEntity(name) {
           return schema[name];
         };
+        // Returns field values for a given entity
         this.getData = function getData(name) {
           return data[name];
         };
           return schema[name];
         };
         this.loadData = function() {
-          var apiCalls = {};
+          var toLoad = 0;
           _.each(schema, function(entity, entityName) {
-            if ($routeParams[entityName]) {
-              var id = $routeParams[entityName];
-              apiCalls[entityName] = [entity.type, 'get', {select: entity.fields, where: [['id', '=', id]]}, 0];
+            if ($routeParams[entityName] || entity.autofill) {
+              toLoad++;
             }
           });
-          if (!_.isEmpty(apiCalls)) {
-            crmApi4(apiCalls).then(function(resp) {
-              data = resp;
-            });
+          if (toLoad) {
+            crmApi4('Afform', 'prefill', {name: CRM.afform.open, args: $routeParams})
+              .then(function(result) {
+                _.each(result, function(item) {
+                  data[item.name] = item.values;
+                });
+              });
           }
         };
 
index 399c6a60bf0bf81938ffe0556e6973ab43312326..ce597661ed5d6446dd8e7a75642ed6001c47f4a2 100644 (file)
@@ -8,7 +8,8 @@
       scope: {
         afType: '@',
         afName: '@',
-        afLabel: '@'
+        afLabel: '@',
+        afAutofill: '@'
       },
       link: function($scope, $el, $attr, afModelListCtrl) {
         var ts = $scope.ts = CRM.ts('afform');
@@ -17,7 +18,8 @@
           type: $scope.afType,
           name: $scope.afName,
           label: $scope.afLabel,
-          fields: [],
+          autofill: $scope.afAutofill,
+          fields: []
         });
         // $scope.$watch('afModelProp', function(newValue){$scope.myOptions = newValue;});
       }