1 // https://civicrm.org/licensing
2 (function(angular
, $, _
) {
5 angular
.module('afGuiEditor').component('afGuiContainer', {
6 templateUrl
: '~/afGuiEditor/elements/afGuiContainer.html',
13 require
: {editor
: '^^afGuiEditor'},
14 controller: function($scope
, crmApi4
, dialogService
, afGui
) {
15 var ts
= $scope
.ts
= CRM
.ts('org.civicrm.afform_admin'),
18 this.$onInit = function() {
19 if (ctrl
.node
['#tag'] && ((ctrl
.node
['#tag'] in afGui
.meta
.blocks
) || ctrl
.join
)) {
20 var blockNode
= getBlockNode(),
21 blockTag
= blockNode
? blockNode
['#tag'] : null;
22 if (blockTag
&& (blockTag
in afGui
.meta
.blocks
) && !afGui
.meta
.blocks
[blockTag
].layout
) {
24 crmApi4('Afform', 'loadAdminData', {
25 definition
: {name
: afGui
.meta
.blocks
[blockTag
].name
},
26 skipEntities
: _
.transform(afGui
.meta
.entities
, function(result
, entity
, entityName
) {
28 result
.push(entityName
);
31 }, 0).then(function(data
) {
33 initializeBlockContainer();
37 initializeBlockContainer();
41 $scope
.isSelectedFieldset = function(entityName
) {
42 return entityName
=== ctrl
.editor
.getSelectedEntityName();
45 $scope
.selectEntity = function() {
46 if (ctrl
.node
['af-fieldset']) {
47 ctrl
.editor
.selectEntity(ctrl
.node
['af-fieldset']);
53 fieldset
: ts('Fieldset')
60 $scope
.getSetChildren = function(val
) {
61 var collection
= block
.layout
|| (ctrl
.node
&& ctrl
.node
['#children']);
62 return arguments
.length
? (collection
= val
) : collection
;
65 $scope
.isRepeatable = function() {
66 return ctrl
.node
['af-fieldset'] || (block
.directive
&& afGui
.meta
.blocks
[block
.directive
].repeat
) || ctrl
.join
;
69 $scope
.toggleRepeat = function() {
70 if ('af-repeat' in ctrl
.node
) {
73 delete ctrl
.node
['af-repeat'];
74 delete ctrl
.node
['add-icon'];
77 ctrl
.node
['af-repeat'] = ts('Add');
81 $scope
.getSetMin = function(val
) {
82 if (arguments
.length
) {
83 if (ctrl
.node
.max
&& val
> parseInt(ctrl
.node
.max
, 10)) {
84 ctrl
.node
.max
= '' + val
;
90 ctrl
.node
.min
= '' + val
;
93 return ctrl
.node
.min
? parseInt(ctrl
.node
.min
, 10) : null;
96 $scope
.getSetMax = function(val
) {
97 if (arguments
.length
) {
98 if (ctrl
.node
.min
&& val
&& val
< parseInt(ctrl
.node
.min
, 10)) {
99 ctrl
.node
.min
= '' + val
;
101 if (typeof val
!== 'number') {
102 delete ctrl
.node
.max
;
105 ctrl
.node
.max
= '' + val
;
108 return ctrl
.node
.max
? parseInt(ctrl
.node
.max
, 10) : null;
111 $scope
.pickAddIcon = function() {
112 afGui
.pickIcon().then(function(val
) {
113 ctrl
.node
['add-icon'] = val
;
117 function getBlockNode() {
118 return !ctrl
.join
? ctrl
.node
: (ctrl
.node
['#children'] && ctrl
.node
['#children'].length
=== 1 ? ctrl
.node
['#children'][0] : null);
121 function setBlockDirective(directive
) {
123 ctrl
.node
['#children'] = [{'#tag': directive
}];
125 delete ctrl
.node
['#children'];
126 delete ctrl
.node
['class'];
127 ctrl
.node
['#tag'] = directive
;
131 function overrideBlockContents(layout
) {
132 ctrl
.node
['#children'] = layout
|| [];
134 ctrl
.node
['#tag'] = 'div';
135 ctrl
.node
['class'] = 'af-container';
137 block
.layout
= block
.directive
= null;
141 'af-layout-rows': ts('Contents display as rows'),
142 'af-layout-cols': ts('Contents are evenly-spaced columns'),
143 'af-layout-inline': ts('Contents are arranged inline')
146 $scope
.getLayout = function() {
150 return _
.intersection(afGui
.splitClass(ctrl
.node
['class']), _
.keys($scope
.layouts
))[0] || 'af-layout-rows';
153 $scope
.setLayout = function(val
) {
154 var classes
= ['af-container'];
155 if (val
!== 'af-layout-rows') {
158 afGui
.modifyClasses(ctrl
.node
, _
.keys($scope
.layouts
), classes
);
161 $scope
.selectBlockDirective = function() {
162 if (block
.directive
) {
163 block
.layout
= _
.cloneDeep(afGui
.meta
.blocks
[block
.directive
].layout
);
164 block
.original
= block
.directive
;
165 setBlockDirective(block
.directive
);
168 overrideBlockContents(block
.layout
);
172 function initializeBlockContainer() {
174 // Cancel the below $watch expressions if already set
175 _
.each(block
.listeners
, function(deregister
) {
179 block
= $scope
.block
= {
187 _
.each(afGui
.meta
.blocks
, function(blockInfo
, directive
) {
188 if (directive
=== ctrl
.node
['#tag'] || (blockInfo
.join
&& blockInfo
.join
=== ctrl
.getFieldEntityType())) {
191 text
: blockInfo
.title
196 if (getBlockNode() && getBlockNode()['#tag'] in afGui
.meta
.blocks
) {
197 block
.directive
= block
.original
= getBlockNode()['#tag'];
198 block
.layout
= _
.cloneDeep(afGui
.meta
.blocks
[block
.directive
].layout
);
201 block
.listeners
.push($scope
.$watch('block.layout', function (layout
, oldVal
) {
202 if (block
.directive
&& layout
&& layout
!== oldVal
&& !angular
.equals(layout
, afGui
.meta
.blocks
[block
.directive
].layout
)) {
203 overrideBlockContents(block
.layout
);
208 $scope
.saveBlock = function() {
209 var options
= CRM
.utils
.adjustDialogDefaults({
213 title
: ts('Save block')
219 layout
: ctrl
.node
['#children']
222 model
.join
= ctrl
.join
;
224 if ($scope
.block
&& $scope
.block
.original
) {
225 model
.title
= afGui
.meta
.blocks
[$scope
.block
.original
].title
;
226 model
.name
= afGui
.meta
.blocks
[$scope
.block
.original
].name
;
227 model
.block
= afGui
.meta
.blocks
[$scope
.block
.original
].block
;
230 model
.block
= ctrl
.getFieldEntityType();
232 dialogService
.open('saveBlockDialog', '~/afGuiEditor/saveBlock.html', model
, options
)
233 .then(function(block
) {
234 afGui
.meta
.blocks
[block
.directive_name
] = block
;
235 setBlockDirective(block
.directive_name
);
236 initializeBlockContainer();
240 this.node
= ctrl
.node
;
242 this.getNodeType = function(node
) {
243 if (!node
|| !node
['#tag']) {
246 if (node
['#tag'] === 'af-field') {
249 if ('af-fieldset' in node
) {
252 if (node
['af-join']) {
255 if (node
['#tag'] && node
['#tag'] in afGui
.meta
.blocks
) {
258 if (node
['#tag'] && (node
['#tag'].slice(0, 19) === 'crm-search-display-')) {
259 return 'searchDisplay';
261 var classes
= afGui
.splitClass(node
['class']),
262 types
= ['af-container', 'af-text', 'af-button', 'af-markup'],
263 type
= _
.intersection(types
, classes
);
264 return type
.length
? type
[0].replace('af-', '') : null;
267 this.removeElement = function(element
) {
268 afGui
.removeRecursive($scope
.getSetChildren(), {$$hashKey
: element
.$$hashKey
});
271 this.getEntityName = function() {
272 return ctrl
.entityName
? ctrl
.entityName
.split('-join-')[0] : null;
275 // Returns the primary entity type for this container e.g. "Contact"
276 this.getMainEntityType = function() {
277 return ctrl
.editor
&& ctrl
.editor
.getEntity(ctrl
.getEntityName()).type
;
280 // Returns the entity type for fields within this conainer (join entity type if this is a join, else the primary entity type)
281 this.getFieldEntityType = function(fieldName
) {
282 // If entityName is declared for this fieldset, return entity-type or join-type
283 if (ctrl
.entityName
) {
284 var joinType
= ctrl
.entityName
.split('-join-');
285 return joinType
[1] || (ctrl
.editor
&& ctrl
.editor
.getEntity(joinType
[0]).type
);
287 // If entityName is not declared, this field belongs to a search
289 prefix
= _
.includes(fieldName
, '.') ? fieldName
.split('.')[0] : null;
290 _
.each(afGui
.meta
.searchDisplays
, function(searchDisplay
) {
292 _
.each(searchDisplay
['saved_search.api_params'].join
, function(join
) {
293 var joinInfo
= join
[0].split(' AS ');
294 if (prefix
=== joinInfo
[1]) {
295 entityType
= joinInfo
[0];
300 if (!entityType
&& fieldName
&& afGui
.getField(searchDisplay
['saved_search.api_entity'], fieldName
)) {
301 entityType
= searchDisplay
['saved_search.api_entity'];
307 return entityType
|| _
.map(afGui
.meta
.searchDisplays
, 'saved_search.api_entity')[0];
313 })(angular
, CRM
.$, CRM
._
);