Merge pull request #19382 from totten/master-maxfile
[civicrm-core.git] / ext / afform / admin / ang / afGuiEditor / afGuiEditor.component.js
CommitLineData
b844d2ca
CW
1// https://civicrm.org/licensing
2(function(angular, $, _) {
3 "use strict";
4
5 angular.module('afGuiEditor').component('afGuiEditor', {
6 templateUrl: '~/afGuiEditor/afGuiEditor.html',
7 bindings: {
8 name: '<'
9 },
10 controllerAs: 'editor',
11 controller: function($scope, crmApi4, afAdmin, $parse, $timeout, $location) {
12 var ts = $scope.ts = CRM.ts('afform');
13 $scope.afform = null;
14 $scope.saving = false;
15 $scope.selectedEntityName = null;
16 this.meta = afAdmin.meta;
17 var editor = this;
18 var newForm = {
19 title: '',
20 permission: 'access CiviCRM',
21 layout: [{
22 '#tag': 'af-form',
23 ctrl: 'afform',
24 '#children': []
25 }]
26 };
27
28 this.$onInit = function() {
29 // Fetch the current form plus all blocks
30 afAdmin.initialize(editor.name)
31 .then(initializeForm);
32 };
33
34 // Initialize the current form
35 function initializeForm(afforms) {
36 $scope.afform = _.findWhere(afforms, {name: editor.name});
37 if (!$scope.afform) {
38 $scope.afform = _.cloneDeep(newForm);
39 if (editor.name != '0') {
40 alert('Error: unknown form "' + editor.name + '"');
41 }
42 }
43 $scope.canvasTab = 'layout';
44 $scope.layoutHtml = '';
45 editor.layout = afAdmin.findRecursive($scope.afform.layout, {'#tag': 'af-form'})[0];
46 $scope.entities = afAdmin.findRecursive(editor.layout['#children'], {'#tag': 'af-entity'}, 'name');
47
48 if (editor.name == '0') {
49 editor.addEntity('Individual');
50 editor.layout['#children'].push(afAdmin.meta.elements.submit.element);
51 }
52
53 // Set changesSaved to true on initial load, false thereafter whenever changes are made to the model
54 $scope.changesSaved = editor.name == '0' ? false : 1;
55 $scope.$watch('afform', function () {
56 $scope.changesSaved = $scope.changesSaved === 1;
57 }, true);
58 }
59
60 $scope.updateLayoutHtml = function() {
61 $scope.layoutHtml = '...Loading...';
62 crmApi4('Afform', 'convert', {layout: [editor.layout], from: 'deep', to: 'html', formatWhitespace: true})
63 .then(function(r){
64 $scope.layoutHtml = r[0].layout || '(Error)';
65 })
66 .catch(function(r){
67 $scope.layoutHtml = '(Error)';
68 });
69 };
70
71 this.addEntity = function(type) {
72 var meta = afAdmin.meta.entities[type],
73 num = 1;
74 // Give this new entity a unique name
75 while (!!$scope.entities[type + num]) {
76 num++;
77 }
78 $scope.entities[type + num] = _.assign($parse(meta.defaults)($scope), {
79 '#tag': 'af-entity',
80 type: meta.entity,
81 name: type + num,
82 label: meta.label + ' ' + num
83 });
84 // Add this af-entity tag after the last existing one
85 var pos = 1 + _.findLastIndex(editor.layout['#children'], {'#tag': 'af-entity'});
86 editor.layout['#children'].splice(pos, 0, $scope.entities[type + num]);
87 // Create a new af-fieldset container for the entity
88 var fieldset = _.cloneDeep(afAdmin.meta.elements.fieldset.element);
89 fieldset['af-fieldset'] = type + num;
90 fieldset['#children'][0]['#children'][0]['#text'] = meta.label + ' ' + num;
91 // Add default contact name block
92 if (meta.entity === 'Contact') {
93 fieldset['#children'].push({'#tag': 'afblock-name-' + type.toLowerCase()});
94 }
95 // Attempt to place the new af-fieldset after the last one on the form
96 pos = 1 + _.findLastIndex(editor.layout['#children'], 'af-fieldset');
97 if (pos) {
98 editor.layout['#children'].splice(pos, 0, fieldset);
99 } else {
100 editor.layout['#children'].push(fieldset);
101 }
102 return type + num;
103 };
104
105 this.removeEntity = function(entityName) {
106 delete $scope.entities[entityName];
107 afAdmin.removeRecursive(editor.layout['#children'], {'#tag': 'af-entity', name: entityName});
108 afAdmin.removeRecursive(editor.layout['#children'], {'af-fieldset': entityName});
109 this.selectEntity(null);
110 };
111
112 this.selectEntity = function(entityName) {
113 $scope.selectedEntityName = entityName;
114 };
115
116 this.getEntity = function(entityName) {
117 return $scope.entities[entityName];
118 };
119
120 this.getSelectedEntityName = function() {
121 return $scope.selectedEntityName;
122 };
123
124 // Validates that a drag-n-drop action is allowed
125 this.onDrop = function(event, ui) {
126 var sort = ui.item.sortable;
127 // Check if this is a callback for an item dropped into a different container
128 // @see https://github.com/angular-ui/ui-sortable notes on canceling
129 if (!sort.received && sort.source[0] !== sort.droptarget[0]) {
130 var $source = $(sort.source[0]),
131 $target = $(sort.droptarget[0]),
132 $item = $(ui.item[0]);
133 // Fields cannot be dropped outside their own entity
134 if ($item.is('[af-gui-field]') || $item.has('[af-gui-field]').length) {
135 if ($source.closest('[data-entity]').attr('data-entity') !== $target.closest('[data-entity]').attr('data-entity')) {
136 return sort.cancel();
137 }
138 }
139 // Entity-fieldsets cannot be dropped into other entity-fieldsets
140 if ((sort.model['af-fieldset'] || $item.has('.af-gui-fieldset').length) && $target.closest('.af-gui-fieldset').length) {
141 return sort.cancel();
142 }
143 }
144 };
145
146 $scope.addEntity = function(entityType) {
147 var entityName = editor.addEntity(entityType);
148 editor.selectEntity(entityName);
149 };
150
151 $scope.save = function() {
152 $scope.saving = $scope.changesSaved = true;
153 crmApi4('Afform', 'save', {formatWhitespace: true, records: [JSON.parse(angular.toJson($scope.afform))]})
154 .then(function (data) {
155 $scope.saving = false;
156 $scope.afform.name = data[0].name;
157 // FIXME: This causes an unnecessary reload when saving a new form
158 $location.search('name', data[0].name);
159 });
160 };
161
162 $scope.$watch('afform.title', function(newTitle, oldTitle) {
163 if (typeof oldTitle === 'string') {
164 _.each($scope.entities, function(entity) {
165 if (entity.data && entity.data.source === oldTitle) {
166 entity.data.source = newTitle;
167 }
168 });
169 }
170 });
171 }
172 });
173
174})(angular, CRM.$, CRM._);