Uses a searchDisplay in an Afform to display form submissions.
Takes advantage of the new 'unmodified' update mode.
->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'),
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;
<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>
--- /dev/null
+<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>
--- /dev/null
+{
+ "type": "search",
+ "title": "Form Submissions",
+ "server_route": "civicrm/admin/afform/submissions",
+ "permission": "administer CiviCRM"
+}
<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>
--- /dev/null
+<?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',
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+];
'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',
],
];
-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;