Merge pull request #21228 from colemanw/afformFixTitle
[civicrm-core.git] / ext / afform / admin / ang / afGuiEditor / afGuiEntity.component.js
CommitLineData
b844d2ca
CW
1// https://civicrm.org/licensing
2(function(angular, $, _) {
3 "use strict";
4
5 angular.module('afGuiEditor').component('afGuiEntity', {
6 templateUrl: '~/afGuiEditor/afGuiEntity.html',
7 bindings: {
8 entity: '<'
9 },
10 require: {editor: '^^afGuiEditor'},
23fd8685 11 controller: function ($scope, $timeout, afGui) {
67d666c6 12 var ts = $scope.ts = CRM.ts('org.civicrm.afform_admin');
b844d2ca
CW
13 var ctrl = this;
14 $scope.controls = {};
15 $scope.fieldList = [];
16 $scope.blockList = [];
17 $scope.blockTitles = [];
18 $scope.elementList = [];
19 $scope.elementTitles = [];
20
e73f06cd 21 this.getEntityType = function() {
de9fdf9f 22 return (ctrl.entity.type === 'Contact' && ctrl.entity.data) ? ctrl.entity.data.contact_type || 'Contact' : ctrl.entity.type;
e73f06cd 23 };
b844d2ca
CW
24
25 $scope.getMeta = function() {
e73f06cd 26 return afGui.meta.entities[ctrl.getEntityType()];
b844d2ca
CW
27 };
28
229a3e39
CW
29 $scope.getAdminTpl = function() {
30 return $scope.getMeta().admin_tpl || '~/afGuiEditor/entityConfig/Generic.html';
31 };
32
23fd8685 33 $scope.getField = afGui.getField;
b844d2ca
CW
34
35 $scope.valuesFields = function() {
36 var fields = _.transform($scope.getMeta().fields, function(fields, field) {
37 fields.push({id: field.name, text: field.label, disabled: $scope.fieldInUse(field.name)});
38 }, []);
39 return {results: fields};
40 };
41
42 $scope.removeValue = function(entity, fieldName) {
43 delete entity.data[fieldName];
44 };
45
46730a23 46 this.buildPaletteLists = function() {
b844d2ca
CW
47 var search = $scope.controls.fieldSearch ? $scope.controls.fieldSearch.toLowerCase() : null;
48 buildFieldList(search);
49 buildBlockList(search);
50 buildElementList(search);
46730a23 51 };
b844d2ca
CW
52
53 function buildFieldList(search) {
54 $scope.fieldList.length = 0;
55 $scope.fieldList.push({
56 entityName: ctrl.entity.name,
e73f06cd 57 entityType: ctrl.getEntityType(),
b844d2ca
CW
58 label: ts('%1 Fields', {1: $scope.getMeta().label}),
59 fields: filterFields($scope.getMeta().fields)
60 });
d4e2c9ff 61 // Add fields for af-join blocks
23fd8685 62 _.each(afGui.meta.entities, function(entity, entityName) {
b844d2ca
CW
63 if (check(ctrl.editor.layout['#children'], {'af-join': entityName})) {
64 $scope.fieldList.push({
65 entityName: ctrl.entity.name + '-join-' + entityName,
66 entityType: entityName,
67 label: ts('%1 Fields', {1: entity.label}),
68 fields: filterFields(entity.fields)
69 });
70 }
71 });
72
73 function filterFields(fields) {
74 return _.transform(fields, function(fieldList, field) {
da5e820b
CW
75 if (!field.readonly &&
76 (!search || _.contains(field.name, search) || _.contains(field.label.toLowerCase(), search))
77 ) {
d4e2c9ff 78 fieldList.push(fieldDefaults(field));
b844d2ca
CW
79 }
80 }, []);
81 }
d4e2c9ff
CW
82
83 function fieldDefaults(field) {
84 var tag = {
85 "#tag": "af-field",
86 name: field.name
87 };
88 return tag;
89 }
b844d2ca
CW
90 }
91
92 function buildBlockList(search) {
93 $scope.blockList.length = 0;
94 $scope.blockTitles.length = 0;
23fd8685 95 _.each(afGui.meta.blocks, function(block, directive) {
b844d2ca 96 if ((!search || _.contains(directive, search) || _.contains(block.name.toLowerCase(), search) || _.contains(block.title.toLowerCase(), search)) &&
fa8dc3f2 97 (block.entity_type === '*' || block.entity_type === ctrl.entity.type || (ctrl.entity.type === 'Contact' && block.entity_type === ctrl.entity.data.contact_type)) &&
2652d11b 98 block.name !== ctrl.editor.getAfform().name
b844d2ca 99 ) {
fa8dc3f2
CW
100 var item = {"#tag": block.join_entity ? "div" : directive};
101 if (block.join_entity) {
102 item['af-join'] = block.join_entity;
b844d2ca
CW
103 item['#children'] = [{"#tag": directive}];
104 }
105 if (block.repeat) {
106 item['af-repeat'] = ts('Add');
107 item.min = '1';
108 if (typeof block.repeat === 'number') {
109 item.max = '' + block.repeat;
110 }
111 }
112 $scope.blockList.push(item);
113 $scope.blockTitles.push(block.title);
114 }
115 });
116 }
117
118 function buildElementList(search) {
119 $scope.elementList.length = 0;
120 $scope.elementTitles.length = 0;
23fd8685 121 _.each(afGui.meta.elements, function(element, name) {
b844d2ca
CW
122 if (!search || _.contains(name, search) || _.contains(element.title.toLowerCase(), search)) {
123 var node = _.cloneDeep(element.element);
124 if (name === 'fieldset') {
fbcd8c17
CW
125 if (!ctrl.editor.allowEntityConfig) {
126 return;
127 }
b844d2ca
CW
128 node['af-fieldset'] = ctrl.entity.name;
129 }
130 $scope.elementList.push(node);
131 $scope.elementTitles.push(name === 'fieldset' ? ts('Fieldset for %1', {1: ctrl.entity.label}) : element.title);
132 }
133 });
134 }
135
b844d2ca
CW
136 // This gets called from jquery-ui so we have to manually apply changes to scope
137 $scope.buildPaletteLists = function() {
138 $timeout(function() {
139 $scope.$apply(function() {
46730a23 140 ctrl.buildPaletteLists();
b844d2ca
CW
141 });
142 });
143 };
144
145 // Checks if a field is on the form or set as a value
146 $scope.fieldInUse = function(fieldName) {
147 var data = ctrl.entity.data || {};
148 if (fieldName in data) {
149 return true;
150 }
151 return check(ctrl.editor.layout['#children'], {'#tag': 'af-field', name: fieldName});
152 };
153
46730a23
CW
154 // Checks if fields in a block are already in use on the form.
155 // Note that if a block contains no fields it can be used repeatedly, so this will always return false for those.
b844d2ca
CW
156 $scope.blockInUse = function(block) {
157 if (block['af-join']) {
158 return check(ctrl.editor.layout['#children'], {'af-join': block['af-join']});
159 }
23fd8685 160 var fieldsInBlock = _.pluck(afGui.findRecursive(afGui.meta.blocks[block['#tag']].layout, {'#tag': 'af-field'}), 'name');
b844d2ca
CW
161 return check(ctrl.editor.layout['#children'], function(item) {
162 return item['#tag'] === 'af-field' && _.includes(fieldsInBlock, item.name);
163 });
164 };
165
166 // Check for a matching item for this entity
167 // Recursively checks the form layout, including block directives
168 function check(group, criteria, found) {
169 if (!found) {
170 found = {};
171 }
172 if (_.find(group, criteria)) {
173 found.match = true;
174 return true;
175 }
176 _.each(group, function(item) {
177 if (found.match) {
178 return false;
179 }
180 if (_.isPlainObject(item)) {
181 // Recurse through everything but skip fieldsets for other entities
182 if ((!item['af-fieldset'] || (item['af-fieldset'] === ctrl.entity.name)) && item['#children']) {
183 check(item['#children'], criteria, found);
184 }
185 // Recurse into block directives
23fd8685
CW
186 else if (item['#tag'] && item['#tag'] in afGui.meta.blocks) {
187 check(afGui.meta.blocks[item['#tag']].layout, criteria, found);
b844d2ca
CW
188 }
189 }
190 });
191 return found.match;
192 }
193
46730a23
CW
194 this.$onInit = function() {
195 // When a new block is saved, update the list
196 this.meta = afGui.meta;
197 $scope.$watchCollection('$ctrl.meta.blocks', function() {
198 $scope.controls.fieldSearch = '';
199 ctrl.buildPaletteLists();
200 });
b844d2ca 201
46730a23
CW
202 $scope.$watch('controls.addValue', function(fieldName) {
203 if (fieldName) {
204 if (!ctrl.entity.data) {
205 ctrl.entity.data = {};
206 }
207 ctrl.entity.data[fieldName] = '';
208 $scope.controls.addValue = '';
209 }
210 });
211 };
b844d2ca
CW
212 }
213 });
214
215})(angular, CRM.$, CRM._);