Afform - move max-repeat to entity definition not afform block definition
authorColeman Watts <coleman@civicrm.org>
Tue, 24 Aug 2021 17:36:11 +0000 (13:36 -0400)
committerColeman Watts <coleman@civicrm.org>
Fri, 27 Aug 2021 12:43:40 +0000 (08:43 -0400)
Adds entity definition files for joins like Email, Address, etc,
and pre-loads all joined entities including custom on the afform gui screen.

22 files changed:
ext/afform/admin/Civi/AfformAdmin/AfformAdminMeta.php
ext/afform/admin/Civi/Api4/Action/Afform/LoadAdminData.php
ext/afform/admin/afformEntities/Activity.php
ext/afform/admin/afformEntities/Address.php [new file with mode: 0644]
ext/afform/admin/afformEntities/Email.php [new file with mode: 0644]
ext/afform/admin/afformEntities/Household.php
ext/afform/admin/afformEntities/IM.php [new file with mode: 0644]
ext/afform/admin/afformEntities/Individual.php
ext/afform/admin/afformEntities/Organization.php
ext/afform/admin/afformEntities/Phone.php [new file with mode: 0644]
ext/afform/admin/afformEntities/Website.php [new file with mode: 0644]
ext/afform/admin/ang/afAdmin/afAdminList.controller.js
ext/afform/admin/ang/afGuiEditor/afGuiEntity.component.js
ext/afform/admin/ang/afGuiEditor/elements/afGuiContainer-menu.html
ext/afform/admin/ang/afGuiEditor/elements/afGuiContainer.component.js
ext/afform/core/Civi/Api4/Action/Afform/Get.php
ext/afform/core/Civi/Api4/Afform.php
ext/afform/core/ang/afblockContactAddress.aff.json
ext/afform/core/ang/afblockContactEmail.aff.json
ext/afform/core/ang/afblockContactIM.aff.json
ext/afform/core/ang/afblockContactPhone.aff.json
ext/afform/core/ang/afblockContactWebsite.aff.json

index fa8554171b9126abf0f5a0589199064bff5ed941..e34943a99a85b79f929f29cc2c1605a8eec4012f 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace Civi\AfformAdmin;
 
+use Civi\Api4\Entity;
 use Civi\Api4\Utils\CoreUtil;
 use CRM_AfformAdmin_ExtensionUtil as E;
 
@@ -67,17 +68,32 @@ class AfformAdminMeta {
     }
     $info = \Civi\Api4\Entity::get(FALSE)
       ->addWhere('name', '=', $entityName)
-      ->addSelect('title', 'icon')
       ->execute()->first();
     if (!$info) {
       // Disabled contact type or nonexistent api entity
       return NULL;
     }
-    return [
-      'entity' => $entityName,
+    return self::entityToAfformMeta($info);
+  }
+
+  /**
+   * Converts info from API.Entity.get to an array of afform entity metadata
+   * @param array $info
+   * @return array
+   */
+  private static function entityToAfformMeta(array $info): array {
+    $meta = [
+      'entity' => $info['name'],
       'label' => $info['title'],
       'icon' => $info['icon'],
     ];
+    // Custom entities are always type 'join'
+    if (in_array('CustomValue', $info['type'], TRUE)) {
+      $meta['type'] = 'join';
+      $max = (int) \CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', substr($info['name'], 7), 'max_multiple', 'name');
+      $meta['repeat_max'] = $max ?: NULL;
+    }
+    return $meta;
   }
 
   /**
@@ -140,10 +156,17 @@ class AfformAdminMeta {
           'icon' => 'fa-pencil-square-o',
           'fields' => [],
         ],
-        'Contact' => self::getApiEntity('Contact'),
       ],
     ];
 
+    // Explicitly load Contact and Custom entities because they do not have afformEntity files
+    $entities = Entity::get(TRUE)
+      ->addClause('OR', ['name', '=', 'Contact'], ['type', 'CONTAINS', 'CustomValue'])
+      ->execute()->indexBy('name');
+    foreach ($entities as $name => $entity) {
+      $data['entities'][$name] = self::entityToAfformMeta($entity);
+    }
+
     $contactTypes = \CRM_Contact_BAO_ContactType::basicTypeInfo();
 
     // Call getFields on getFields to get input type labels
index fa2c2f535a8c7e0d050c303b743ac3d4b9631c3c..289206b08fc118b098a0fe68f0ba82b06976fa6d 100644 (file)
@@ -237,7 +237,7 @@ class LoadAdminData extends \Civi\Api4\Generic\AbstractAction {
     }
     if ($entities) {
       $blockInfo = Afform::get($this->checkPermissions)
-        ->addSelect('name', 'title', 'entity_type', 'join_entity', 'directive_name', 'repeat')
+        ->addSelect('name', 'title', 'entity_type', 'join_entity', 'directive_name')
         ->setWhere($where)
         ->addWhere('type', '=', 'block')
         ->addWhere('entity_type', 'IN', $entities)
index 860d68fd1d7b3ab310d7ce04d1b1feb9bac3c3ec..cff72ead286fba2f755b2dc9c97d9916ff71d80c 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 return [
+  'type' => 'primary',
   'defaults' => "{
     data: {
       source_contact_id: 'user_contact_id',
diff --git a/ext/afform/admin/afformEntities/Address.php b/ext/afform/admin/afformEntities/Address.php
new file mode 100644 (file)
index 0000000..a8a3554
--- /dev/null
@@ -0,0 +1,5 @@
+<?php
+return [
+  'type' => 'join',
+  'repeat_max' => NULL,
+];
diff --git a/ext/afform/admin/afformEntities/Email.php b/ext/afform/admin/afformEntities/Email.php
new file mode 100644 (file)
index 0000000..a8a3554
--- /dev/null
@@ -0,0 +1,5 @@
+<?php
+return [
+  'type' => 'join',
+  'repeat_max' => NULL,
+];
index 20cbab0c6d0ca961501fdfafe06928b060c43599..808565deab9781f6250f6d55614f5d371afe17a5 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 return [
+  'type' => 'primary',
   'defaults' => "{
     data: {
       contact_type: 'Household',
diff --git a/ext/afform/admin/afformEntities/IM.php b/ext/afform/admin/afformEntities/IM.php
new file mode 100644 (file)
index 0000000..a8a3554
--- /dev/null
@@ -0,0 +1,5 @@
+<?php
+return [
+  'type' => 'join',
+  'repeat_max' => NULL,
+];
index 31e5ace42502c16b49145ed7a66263907261e145..3d124377d893504472cb52b4b70e7a0c3c63a152 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 return [
+  'type' => 'primary',
   'defaults' => "{
     data: {
       contact_type: 'Individual',
index ecbf7fdd70d200d3a5a322eda478e004e4814704..b36455be1bba0e913530c4d57ea4a6a48374afc1 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 return [
+  'type' => 'primary',
   'defaults' => "{
     data: {
       contact_type: 'Organization',
diff --git a/ext/afform/admin/afformEntities/Phone.php b/ext/afform/admin/afformEntities/Phone.php
new file mode 100644 (file)
index 0000000..a8a3554
--- /dev/null
@@ -0,0 +1,5 @@
+<?php
+return [
+  'type' => 'join',
+  'repeat_max' => NULL,
+];
diff --git a/ext/afform/admin/afformEntities/Website.php b/ext/afform/admin/afformEntities/Website.php
new file mode 100644 (file)
index 0000000..a8a3554
--- /dev/null
@@ -0,0 +1,5 @@
+<?php
+return [
+  'type' => 'join',
+  'repeat_max' => NULL,
+];
index df46006d1ba083d37536d4eb87fa318371608025..c48d9c55863d35a9632c293fdeaf894e6f6e5f77 100644 (file)
@@ -58,7 +58,7 @@
 
       if (ctrl.tab === 'form') {
         _.each(CRM.afGuiEditor.entities, function(entity, name) {
-          if (entity.defaults) {
+          if (entity.type === 'primary') {
             links.push({
               url: '#create/form/' + name,
               label: entity.label,
index 2e6b72c3c5e8de183b6f34fb258b11360b467596..de487e19a7e1f588681dec0fdf02d4398f5feb7c 100644 (file)
           ) {
             var item = {"#tag": block.join_entity ? "div" : directive};
             if (block.join_entity) {
+              var joinEntity = afGui.getEntity(block.join_entity);
+              // Skip adding block if entity does not exist
+              if (!joinEntity) {
+                return;
+              }
               item['af-join'] = block.join_entity;
               item['#children'] = [{"#tag": directive}];
-            }
-            if (block.repeat) {
               item['af-repeat'] = ts('Add');
               item.min = '1';
-              if (typeof block.repeat === 'number') {
-                item.max = '' + block.repeat;
+              if (typeof joinEntity.repeat_max === 'number') {
+                item.max = '' + joinEntity.repeat_max;
               }
             }
             $scope.blockList.push(item);
index 9121bfeb7da758428191f2551e777f62dc440aaf..4a9b6d6fb7d4664ab9faf7f66125e3f0a382e8d8 100644 (file)
@@ -16,7 +16,7 @@
     </label>
     <span ng-style="{visibility: $ctrl.node['af-repeat'] || $ctrl.node['af-repeat'] === '' ? 'visible' : 'hidden'}">
       <input type="number" class="form-control" ng-model="getSetMin" ng-model-options="{getterSetter: true}" placeholder="{{:: ts('min') }}" min="0" step="1" />
-      - <input type="number" class="form-control" ng-model="getSetMax" ng-model-options="{getterSetter: true}" placeholder="{{:: ts('max') }}" min="2" step="1" />
+      - <input type="number" class="form-control" ng-model="getSetMax" ng-model-options="{getterSetter: true}" placeholder="{{:: ts('max') }}" min="2" max="{{:: getRepeatMax() }}" step="1" />
     </span>
   </div>
 </li>
index 539d6b96c2e8165a0d0e61af8c2ca69e9472bcb2..097782c795e7c20956476df95f4044e24721f8c9 100644 (file)
@@ -88,6 +88,7 @@
         }
       };
 
+      // Sets min value for af-repeat as a string, returns it as an int
       $scope.getSetMin = function(val) {
         if (arguments.length) {
           if (ctrl.node.max && val > parseInt(ctrl.node.max, 10)) {
         return ctrl.node.min ? parseInt(ctrl.node.min, 10) : null;
       };
 
+      // Sets max value for af-repeat as a string, returns it as an int
       $scope.getSetMax = function(val) {
         if (arguments.length) {
           if (ctrl.node.min && val && val < parseInt(ctrl.node.min, 10)) {
         return ctrl.node.max ? parseInt(ctrl.node.max, 10) : null;
       };
 
+      // Returns the maximum number of repeats allowed if this is a joined entity with a limit
+      // Value comes from civicrm_custom_group.max_multiple for custom entities,
+      // or from afformEntity php file for core entities.
+      $scope.getRepeatMax = function() {
+        if (ctrl.join) {
+          return afGui.getEntity(ctrl.join).repeat_max || '';
+        }
+        return '';
+      };
+
       $scope.pickAddIcon = function() {
         afGui.pickIcon().then(function(val) {
           ctrl.node['add-icon'] = val;
index 2d4fa6e86a1ddc9b7672881f94390dc08634ac09..10e035b2dc32b45268a60c9c4100a919eeb37afc 100644 (file)
@@ -135,7 +135,6 @@ class Get extends \Civi\Api4\Generic\BasicGetAction {
         'permission' => 'access CiviCRM',
         'join_entity' => 'Custom_' . $custom['name'],
         'entity_type' => $custom['extends'],
-        'repeat' => $custom['max_multiple'] ?: TRUE,
         'has_base' => TRUE,
       ];
       if ($getLayout) {
index 378b874e31c6cd46e7ee975f5b27f5ec1f590d42..ec76fd3df2f29c2059483c2e9e844b32a879b163 100644 (file)
@@ -178,10 +178,6 @@ class Afform extends Generic\AbstractEntity {
             'tab' => ts('Contact Summary Tab'),
           ],
         ],
-        [
-          'name' => 'repeat',
-          'data_type' => 'Mixed',
-        ],
         [
           'name' => 'server_route',
         ],
index 41a28494a18252afacb92f7a6cc11fbe4a7e51e3..0a0e209345f744cfc3cf3cc52c8bad9fadff2e52 100644 (file)
@@ -2,6 +2,5 @@
   "title": "Contact Address(es)",
   "type": "block",
   "entity_type": "Contact",
-  "join_entity": "Address",
-  "repeat": true
+  "join_entity": "Address"
 }
index 0b8a749a4c21d137523992706192abcb569ecbdf..6ddb66f259498c578b17ce3d69059f7705619b34 100644 (file)
@@ -2,6 +2,5 @@
   "title": "Contact Email(s)",
   "type": "block",
   "entity_type": "Contact",
-  "join_entity": "Email",
-  "repeat": true
+  "join_entity": "Email"
 }
index 0d7ea0ff46b74b74a5024359ffdfc1cfbb3de62f..953d1a09d13d6a84350536cd53a8d8e2f656a008 100644 (file)
@@ -2,6 +2,5 @@
   "title": "Contact IM(s)",
   "type": "block",
   "entity_type": "Contact",
-  "join_entity": "IM",
-  "repeat": true
+  "join_entity": "IM"
 }
index fa7cad216868d14e7791d758f09099c1ebf14e9e..c66eae8b24f24e1c907a526855a0e34d456f6bf0 100644 (file)
@@ -2,6 +2,5 @@
   "title": "Contact Phone(s)",
   "type": "block",
   "entity_type": "Contact",
-  "join_entity": "Phone",
-  "repeat": true
+  "join_entity": "Phone"
 }
index 3cea837cca6eb4e7d34756028769196f4c3262c8..89288fcfd09db84ee6080dc667cec936092b942a 100644 (file)
@@ -2,6 +2,5 @@
   "title": "Contact Website(s)",
   "type": "block",
   "entity_type": "Contact",
-  "join_entity": "Website",
-  "repeat": true
+  "join_entity": "Website"
 }