Afform - Add table of submission results
authorColeman Watts <coleman@civicrm.org>
Mon, 8 Nov 2021 15:00:31 +0000 (10:00 -0500)
committerColeman Watts <coleman@civicrm.org>
Wed, 10 Nov 2021 22:00:45 +0000 (17:00 -0500)
Uses a searchDisplay in an Afform to display form submissions.
Takes advantage of the new 'unmodified' update mode.

ext/afform/admin/Civi/AfformAdmin/AfformAdminMeta.php
ext/afform/admin/ang/afAdmin/afAdminList.controller.js
ext/afform/admin/ang/afAdmin/afAdminList.html
ext/afform/admin/ang/afAdminFormSubmissionList.aff.html [new file with mode: 0644]
ext/afform/admin/ang/afAdminFormSubmissionList.aff.json [new file with mode: 0644]
ext/afform/admin/info.xml
ext/afform/admin/managed/FormSubmissionSavedSearch.mgd.php [new file with mode: 0644]
ext/afform/core/managed/AfformType.mgd.php

index bd72efb865b4fcd6f51ff98269d51f5d81967e5a..6ac1d0350339d2e588b2705cf4f34453a0c7c921 100644 (file)
@@ -20,7 +20,7 @@ class AfformAdminMeta {
       ->execute();
     // Pluralize tabs (too bad option groups only store a single label)
     $plurals = [
-      'form' => E::ts('Custom Forms'),
+      'form' => E::ts('Submission Forms'),
       'search' => E::ts('Search Forms'),
       'block' => E::ts('Field Blocks'),
       'system' => E::ts('System Forms'),
index f7a00701020ae33e5a65d518c888d0b8f1597239..2b1ee33b946409b0096333c69f2ed0b8a6385463 100644 (file)
       afforms[afform.type].push(afform);
     }, {});
 
+    // Load aggregated submission stats for each form
+    crmApi4('AfformSubmission', 'get', {
+      select: ['afform_name', 'MAX(submission_date) AS last_submission', 'COUNT(id) AS submission_count'],
+      groupBy: ['afform_name']
+    }).then(function(submissions) {
+      _.each(submissions, function(submission) {
+        var afform = _.findWhere(afforms, {name: submission.afform_name}) || {};
+        afform.last_submission = CRM.utils.formatDate(submission.last_submission);
+        afform.submission_count = submission.submission_count;
+      });
+    });
+
     // Change sort field/direction when clicking a column header
     this.sortBy = function(col) {
       ctrl.sortDir = ctrl.sortField === col ? !ctrl.sortDir : false;
index bfa5c19eb7be3d4c7e9983a9b106f27788435269..0f119d02d575703108f823f23efb0c89533c6913 100644 (file)
@@ -50,7 +50,7 @@
         <i class="crm-i fa-sort-{{ $ctrl.sortDir ? 'asc' : 'desc' }}" ng-if="$ctrl.sortField === 'name'"></i>
         {{:: ts('Name') }}
       </th>
-      <th title="{{:: ts('Click to sort') }}" ng-click="$ctrl.sortBy('server_route')">
+      <th  ng-if="$ctrl.tab !== 'block'" title="{{:: ts('Click to sort') }}" ng-click="$ctrl.sortBy('server_route')">
         <i class="crm-i fa-sort disabled" ng-if="$ctrl.sortField !== 'server_route'"></i>
         <i class="crm-i fa-sort-{{ $ctrl.sortDir ? 'asc' : 'desc' }}" ng-if="$ctrl.sortField === 'server_route'"></i>
         {{:: ts('Page') }}
         <i class="crm-i fa-sort-{{ $ctrl.sortDir ? 'asc' : 'desc' }}" ng-if="$ctrl.sortField === 'placement.length'"></i>
         {{:: ts('Placement') }}
       </th>
+      <th ng-if="$ctrl.tab === 'form'" title="{{:: ts('Click to sort') }}" ng-click="$ctrl.sortBy('submission_count')">
+        <i class="crm-i fa-sort disabled" ng-if="$ctrl.sortField !== 'submission_count'"></i>
+        <i class="crm-i fa-sort-{{ $ctrl.sortDir ? 'asc' : 'desc' }}" ng-if="$ctrl.sortField === 'submission_count'"></i>
+        {{:: ts('Submissions') }}
+      </th>
       <th title="{{:: ts('Click to sort') }}" ng-click="$ctrl.sortBy('base_module')">
         <i class="crm-i fa-sort disabled" ng-if="$ctrl.sortField !== 'base_module'"></i>
         <i class="crm-i fa-sort-{{ $ctrl.sortDir ? 'asc' : 'desc' }}" ng-if="$ctrl.sortField === 'base_module'"></i>
       <td>
         <code>{{:: afform.name }}</code>
       </td>
-      <td>
+      <td ng-if="$ctrl.tab !== 'block'">
         <a ng-if=":: afform.server_route" ng-href="{{:: crmUrl(afform.server_route, null, afform.is_public ? 'front' : 'back') }}" target="_blank">
           <i class="crm-i fa-external-link"></i>
           {{:: afform.server_route }}
         </a>
       </td>
       <td>{{:: afform.placement.join(', ') }}</td>
+      <td ng-if="$ctrl.tab === 'form'">
+        <a ng-if="afform.submission_count" ng-href="{{:: crmUrl('civicrm/admin/afform/submissions#/?name=' + afform.name) }}">
+          {{:: afform.submission_count === 1 ? ts('1 Submission') : ts('%1 Submissions', {1: afform.submission_count}) }}
+        </a>
+        <div ng-if="afform.last_submission">
+          {{:: ts('Last submitted: %1', {1: afform.last_submission}) }}
+        </div>
+      </td>
       <td>
         {{:: afform['base_module:label'] }}
       </td>
diff --git a/ext/afform/admin/ang/afAdminFormSubmissionList.aff.html b/ext/afform/admin/ang/afAdminFormSubmissionList.aff.html
new file mode 100644 (file)
index 0000000..f26eb38
--- /dev/null
@@ -0,0 +1,10 @@
+<div
+  ng-if="routeParams.name"
+  af-api4="['Afform', 'get', {select: ['title'], where: [['name', '=', routeParams.name]]}, 0]"
+  af-api4-ctrl="api4">
+  <h2>{{ ts('%1 Submissions', {1: api4.result.title || ts('Loading')}) }}</h2>
+</div>
+<div af-fieldset="">
+  <crm-search-display-table search-name="AfAdmin_Submission_List" display-name="AfAdmin_Submission_List_Display" filters="{afform_name: routeParams.name}">
+  </crm-search-display-table>
+</div>
diff --git a/ext/afform/admin/ang/afAdminFormSubmissionList.aff.json b/ext/afform/admin/ang/afAdminFormSubmissionList.aff.json
new file mode 100644 (file)
index 0000000..3e4fa21
--- /dev/null
@@ -0,0 +1,6 @@
+{
+    "type": "search",
+    "title": "Form Submissions",
+    "server_route": "civicrm/admin/afform/submissions",
+    "permission": "administer CiviCRM"
+}
index 41ab945bee475b5732f22b2c1ae8712b037128fc..4c6ca916c7a58163e24c18af9ceb70680a3411a6 100644 (file)
@@ -23,6 +23,7 @@
   <comments>Administer, edit and compose CiviCRM Afforms.</comments>
   <requires>
     <ext>org.civicrm.afform</ext>
+    <ext>org.civicrm.search_kit</ext>
   </requires>
   <civix>
     <namespace>CRM/AfformAdmin</namespace>
diff --git a/ext/afform/admin/managed/FormSubmissionSavedSearch.mgd.php b/ext/afform/admin/managed/FormSubmissionSavedSearch.mgd.php
new file mode 100644 (file)
index 0000000..bd6634a
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+
+use CRM_AfformAdmin_ExtensionUtil as E;
+
+// This file declares a SavedSearch and SearchDisplay for viewing form submissions.
+return [
+  [
+    'name' => 'AfAdmin_Submission_List',
+    'entity' => 'SavedSearch',
+    'update' => 'unmodified',
+    'cleanup' => 'unused',
+    'params' => [
+      'version' => 4,
+      'values' => [
+        'name' => 'AfAdmin_Submission_List',
+        'label' => E::ts('Form Submissions'),
+        'form_values' => NULL,
+        'mapping_id' => NULL,
+        'search_custom_id' => NULL,
+        'api_entity' => 'AfformSubmission',
+        'api_params' => [
+          'version' => 4,
+          'select' => [
+            'id',
+            'contact_id.display_name',
+            'submission_date',
+          ],
+        ],
+      ],
+    ],
+  ],
+  [
+    'name' => 'AfAdmin_Submission_List_Display',
+    'entity' => 'SearchDisplay',
+    'update' => 'unmodified',
+    'cleanup' => 'unused',
+    'params' => [
+      'version' => 4,
+      'values' => [
+        'name' => 'AfAdmin_Submission_List_Display',
+        'label' => E::ts('Form Submissions Table'),
+        'saved_search_id.name' => 'AfAdmin_Submission_List',
+        'type' => 'table',
+        'actions' => TRUE,
+        'acl_bypass' => FALSE,
+        'settings' => [
+          'actions' => TRUE,
+          'limit' => 50,
+          'classes' => [
+            'table',
+            'table-striped',
+          ],
+          'pager' => [
+            'show_count' => TRUE,
+            'expose_limit' => TRUE,
+          ],
+          'columns' => [
+            [
+              'type' => 'field',
+              'key' => 'id',
+              'dataType' => 'Integer',
+              'label' => E::ts('Id'),
+              'sortable' => TRUE,
+            ],
+            [
+              'type' => 'field',
+              'key' => 'contact_id.display_name',
+              'dataType' => 'String',
+              'label' => E::ts('Submitted by'),
+              'sortable' => TRUE,
+              'link' => [
+                'entity' => 'Contact',
+                'action' => 'view',
+                'join' => 'contact_id',
+                'target' => '_blank',
+              ],
+              'empty_value' => E::ts('Anonymous'),
+              'cssRules' => [
+                [
+                  'disabled',
+                  'contact_id.display_name',
+                  '=',
+                ],
+              ],
+            ],
+            [
+              'type' => 'field',
+              'key' => 'submission_date',
+              'dataType' => 'Timestamp',
+              'label' => E::ts('Submission Date/Time'),
+              'sortable' => TRUE,
+            ],
+          ],
+          'sort' => [
+            [
+              'submission_date',
+              'ASC',
+            ],
+          ],
+        ],
+      ],
+    ],
+  ],
+];
index baeaccb8c6c4daf6ac9cabd3007e42141861e667..25da5682b7a8af7b867b73c6d6e3afa9ddf82918 100644 (file)
@@ -17,11 +17,23 @@ $mgd = [
       'option_group_id' => 'afform_type',
       'name' => 'form',
       'value' => 'form',
-      'label' => 'Custom Form',
+      'label' => 'Submission Form',
       'weight' => 0,
       'icon' => 'fa-list-alt',
     ],
   ],
+  [
+    'name' => 'AfformType:search',
+    'entity' => 'OptionValue',
+    'params' => [
+      'option_group_id' => 'afform_type',
+      'name' => 'search',
+      'value' => 'search',
+      'label' => 'Search Form',
+      'weight' => 10,
+      'icon' => 'fa-search',
+    ],
+  ],
   [
     'name' => 'AfformType:block',
     'entity' => 'OptionValue',
@@ -48,27 +60,4 @@ $mgd = [
   ],
 ];
 
-try {
-  $search = civicrm_api3('Extension', 'getsingle', [
-    'full_name' => 'org.civicrm.search_kit',
-  ]);
-  if ($search['status'] === 'installed') {
-    $mgd[] = [
-      'name' => 'AfformType:search',
-      'entity' => 'OptionValue',
-      'params' => [
-        'option_group_id' => 'afform_type',
-        'name' => 'search',
-        'value' => 'search',
-        'label' => 'Search Form',
-        'weight' => 10,
-        'icon' => 'fa-search',
-      ],
-    ];
-  }
-}
-catch (Exception $e) {
-  // ¯\_(ツ)_/¯
-}
-
 return $mgd;