Skip to content

Commit

Permalink
Merge pull request #19395 from colemanw/afformTypes
Browse files Browse the repository at this point in the history
Tabbed interface for organizing afforms by type
  • Loading branch information
colemanw authored Jan 20, 2021
2 parents d467c0d + a92d81a commit e8eab34
Show file tree
Hide file tree
Showing 62 changed files with 775 additions and 434 deletions.
32 changes: 32 additions & 0 deletions ext/afform/admin/CRM/AfformAdmin/Page/Base.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/*
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC. All rights reserved. |
| |
| This work is published under the GNU AGPLv3 license with some |
| permitted exceptions and without any warranty. For full license |
| and copyright information, see https://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/

/**
* Base page for Afform admin
*/
class CRM_AfformAdmin_Page_Base extends CRM_Core_Page {

public function run() {
$breadCrumb = [
'title' => ts('Forms'),
'url' => CRM_Utils_System::url('civicrm/admin/afform', NULL, FALSE, '/'),
];
CRM_Utils_System::appendBreadCrumb([$breadCrumb]);

// Load angular module
$loader = new Civi\Angular\AngularLoader();
$loader->setPageName('civicrm/admin/afform');
$loader->useApp();
$loader->load();
parent::run();
}

}
32 changes: 17 additions & 15 deletions ext/afform/admin/CRM/AfformAdmin/Utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,31 @@

class CRM_AfformAdmin_Utils {

/**
* @return array
*/
public static function getAdminSettings() {
return [
'afform_type' => \Civi\Api4\OptionValue::get(FALSE)
->addSelect('name', 'label', 'icon')
->addWhere('is_active', '=', TRUE)
->addWhere('option_group_id:name', '=', 'afform_type')
->addOrderBy('weight', 'ASC')
->execute(),
];
}

/**
* Loads metadata for the gui editor.
*
* FIXME: This is a prototype and should get broken out into separate callbacks with hooks, events, etc.
* @return array
*/
public static function getAngularSettings() {
public static function getGuiSettings() {
$getFieldParams = [
'checkPermissions' => FALSE,
'includeCustom' => TRUE,
'loadOptions' => TRUE,
'loadOptions' => ['id', 'label'],
'action' => 'create',
'select' => ['name', 'label', 'input_type', 'input_attrs', 'required', 'options', 'help_pre', 'help_post', 'serialize', 'data_type'],
'where' => [['input_type', 'IS NOT NULL']],
Expand Down Expand Up @@ -135,19 +150,6 @@ public static function getAngularSettings() {
],
];

// Reformat options
// TODO: Teach the api to return options in this format
foreach ($data['entities'] as $entityName => $entity) {
foreach ($entity['fields'] as $name => $field) {
if (!empty($field['options'])) {
$data['entities'][$entityName]['fields'][$name]['options'] = CRM_Utils_Array::makeNonAssociative($field['options'], 'key', 'label');
}
else {
unset($data['entities'][$entityName]['fields'][$name]['options']);
}
}
}

$data['styles'] = [
'default' => E::ts('Default'),
'primary' => E::ts('Primary'),
Expand Down
7 changes: 0 additions & 7 deletions ext/afform/admin/ang/afAdmin.aff.html

This file was deleted.

8 changes: 0 additions & 8 deletions ext/afform/admin/ang/afAdmin.aff.json

This file was deleted.

15 changes: 15 additions & 0 deletions ext/afform/admin/ang/afAdmin.ang.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
// Angular module for afform gui editor
return [
'js' => [
'ang/afAdmin.js',
'ang/afAdmin/*.js',
'ang/afAdmin/*/*.js',
],
'css' => [],
'partials' => ['ang/afAdmin'],
'requires' => ['api4', 'afGuiEditor', 'crmRouteBinder'],
'settingsFactory' => ['CRM_AfformAdmin_Utils', 'getAdminSettings'],
'basePages' => ['civicrm/admin/afform'],
'bundles' => ['bootstrap3'],
];
30 changes: 30 additions & 0 deletions ext/afform/admin/ang/afAdmin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
(function(angular, $, _) {
"use strict";
angular.module('afAdmin', CRM.angRequires('afAdmin'))

.config(function($routeProvider) {
$routeProvider.when('/', {
controller: 'afAdminList',
reloadOnSearch: false,
templateUrl: '~/afAdmin/afAdminList.html',
resolve: {
// Load data for lists
afforms: function(crmApi4) {
return crmApi4('Afform', 'get', {
select: ['name', 'title', 'type', 'is_public', 'server_route', 'has_local', 'has_base'],
orderBy: {title: 'ASC'}
});
}
}
});
$routeProvider.when('/create/:type', {
controller: 'afAdminGui',
template: '<af-gui-editor type="$ctrl.type"></af-gui-editor>',
});
$routeProvider.when('/edit/:name', {
controller: 'afAdminGui',
template: '<af-gui-editor name="$ctrl.name"></af-gui-editor>',
});
});

})(angular, CRM.$, CRM._);
15 changes: 15 additions & 0 deletions ext/afform/admin/ang/afAdmin/afAdminGui.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(function(angular, $, _) {
"use strict";

angular.module('afAdmin').controller('afAdminGui', function($scope, $routeParams) {
var ts = $scope.ts = CRM.ts(),
ctrl = $scope.$ctrl = this;

// Edit mode
this.name = $routeParams.name;
// Create mode
this.type = $routeParams.type;

});

})(angular, CRM.$, CRM._);
51 changes: 51 additions & 0 deletions ext/afform/admin/ang/afAdmin/afAdminList.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
(function(angular, $, _) {
"use strict";

angular.module('afAdmin').controller('afAdminList', function($scope, afforms, crmApi4, crmStatus) {
var ts = $scope.ts = CRM.ts(),
ctrl = $scope.$ctrl = this;

$scope.crmUrl = CRM.url;

this.tabs = CRM.afAdmin.afform_type;

this.afforms = _.transform(afforms, function(afforms, afform) {
var type = afform.type || 'system';
afforms[type] = afforms[type] || [];
afforms[type].push(afform);
}, {});

$scope.$bindToRoute({
expr: '$ctrl.tab',
param: 'tab',
format: 'raw',
default: ctrl.tabs[0].name
});

this.revert = function(afform) {
var index = _.findIndex(ctrl.afforms[ctrl.tab], {name: afform.name});
if (index > -1) {
var apiOps = [['Afform', 'revert', {where: [['name', '=', afform.name]]}]];
if (afform.has_base) {
apiOps.push(['Afform', 'get', {
where: [['name', '=', afform.name]],
select: ['name', 'title', 'type', 'is_public', 'server_route', 'has_local', 'has_base']
}, 0]);
}
var apiCall = crmStatus(
afform.has_base ? {start: ts('Reverting...')} : {start: ts('Deleting...'), success: ts('Deleted')},
crmApi4(apiOps)
);
if (afform.has_base) {
afform.has_local = false;
apiCall.then(function(result) {
ctrl.afforms[ctrl.tab][index] = result[1];
});
} else {
ctrl.afforms[ctrl.tab].splice(index, 1);
}
}
};
});

})(angular, CRM.$, CRM._);
59 changes: 59 additions & 0 deletions ext/afform/admin/ang/afAdmin/afAdminList.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<div id="bootstrap-theme" class="afadmin-list">
<h1 crm-page-title>{{:: ts('Configurable Forms') }}</h1>

<div class="form-inline text-right">
<a class="btn btn-primary" href="#/create/form">
<i class="crm-i fa-plus"></i>
{{:: ts('New Form') }}
</a>
</div>

<ul class="nav nav-tabs">
<li role="presentation" ng-repeat="tab in $ctrl.tabs" ng-class="{active: tab.name === $ctrl.tab}">
<a href ng-click="$ctrl.tab = tab.name"><i class="crm-i {{ tab.icon }}"></i> {{:: tab.label }}</a>
</li>
</ul>

<div class="form-inline">
<input class="form-control" type="search" ng-model="$ctrl.search" placeholder="{{:: ts('Filter') }}">
</div>

<table>
<thead>
<tr>
<th>{{:: ts('Title') }}</th>
<th>{{:: ts('Name') }}</th>
<th>{{:: ts('Server Route') }}</th>
<th>{{:: ts('Frontend?') }}</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="afform in $ctrl.afforms[$ctrl.tab] | filter:$ctrl.search">
<td>{{afform.title}}</td>
<td>
<code>{{afform.name}}</code>
</td>
<td>
<a ng-if="afform.server_route" ng-href="{{ crmUrl(afform.server_route) }}" target="_blank">
<code>{{afform.server_route}}</code>
</a>
</td>
<td>{{afform.is_public ? ts('Frontend') : ts('Backend')}}</td>
<td>
<a href="#/edit/{{ afform.name }}" class="btn btn-xs btn-primary">{{ ts('Edit') }}</a>
<a href ng-if="afform.has_local" class="btn btn-xs btn-danger" crm-confirm="{type: afform.has_base ? 'revert' : 'delete', obj: afform}" on-yes="$ctrl.revert(afform)">
{{ afform.has_base ? ts('Revert') : ts('Delete') }}
</a>
</td>
</tr>
<tr ng-if="!$ctrl.afforms[$ctrl.tab] || $ctrl.afforms[$ctrl.tab].length === 0">
<td colspan="9">
<p class="messages status no-popup text-center">
{{:: ts('None Found')}}
</p>
</td>
</tr>
</tbody>
</table>
</div>
55 changes: 0 additions & 55 deletions ext/afform/admin/ang/afAdminList.aff.html

This file was deleted.

2 changes: 1 addition & 1 deletion ext/afform/admin/ang/afGuiEditor.ang.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
'css' => ['ang/afGuiEditor.css'],
'partials' => ['ang/afGuiEditor'],
'requires' => ['crmUi', 'crmUtil', 'dialogService', 'api4', 'crmMonaco', 'ui.sortable'],
'settingsFactory' => ['CRM_AfformAdmin_Utils', 'getAngularSettings'],
'settingsFactory' => ['CRM_AfformAdmin_Utils', 'getGuiSettings'],
'basePages' => [],
'exports' => [
'af-gui-editor' => 'E',
Expand Down
2 changes: 1 addition & 1 deletion ext/afform/admin/ang/afGuiEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"use strict";
angular.module('afGuiEditor', CRM.angRequires('afGuiEditor'))

.service('afAdmin', function(crmApi4, $parse, $q) {
.service('afGui', function(crmApi4, $parse, $q) {

// Parse strings of javascript that php couldn't interpret
function evaluate(collection) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@

this.$onInit = function() {
$scope.options = JSON.parse(angular.toJson(ctrl.field.getOptions()));
var optionKeys = _.map($scope.options, 'key');
var optionKeys = _.map($scope.options, 'id');
$scope.deletedOptions = _.filter(JSON.parse(angular.toJson(ctrl.field.getDefn().options || [])), function (item) {
return !_.contains(optionKeys, item.key);
return !_.contains(optionKeys, item.id);
});
$scope.originalLabels = _.transform(ctrl.field.getDefn().options || [], function (originalLabels, item) {
originalLabels[item.key] = item.label;
originalLabels[item.id] = item.label;
}, {});
};

Expand Down
2 changes: 1 addition & 1 deletion ext/afform/admin/ang/afGuiEditor/afGuiEditOptions.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ <h4 class="pull-left">{{:: ts('Customize options') }}</h4>
</div>
<ul ui-sortable="{connectWith: '.af-gui-edit-options-deleted', cancel: 'input,textarea,button,select,option,a,[contenteditable]'}" ng-model="options" class="af-gui-edit-options-enabled">
<li ng-repeat="option in options">
<div af-gui-editable ng-model="option.label" default-value="originalLabels[option.key]" >{{ option.label }}</div>
<div af-gui-editable ng-model="option.label" default-value="originalLabels[option.id]" >{{ option.label }}</div>
<button type="button" class="btn btn-danger-outline btn-xs pull-right" ng-click="deleteOption(option, $index)" title="{{:: ts('Remove option') }}">
<i class="crm-i fa-trash"></i>
</button>
Expand Down
Loading

0 comments on commit e8eab34

Please sign in to comment.