1 // https://civicrm.org/licensing
2 (function(angular
, $, _
) {
5 angular
.module('afGuiEditor').component('afGuiEditor', {
6 templateUrl
: '~/afGuiEditor/afGuiEditor.html',
10 controllerAs
: 'editor',
11 controller: function($scope
, crmApi4
, afAdmin
, $parse
, $timeout
, $location
) {
12 var ts
= $scope
.ts
= CRM
.ts('afform');
14 $scope
.saving
= false;
15 $scope
.selectedEntityName
= null;
16 this.meta
= afAdmin
.meta
;
20 permission
: 'access CiviCRM',
28 this.$onInit = function() {
29 // Fetch the current form plus all blocks
30 afAdmin
.initialize(editor
.name
)
31 .then(initializeForm
);
34 // Initialize the current form
35 function initializeForm(afforms
) {
36 $scope
.afform
= _
.findWhere(afforms
, {name
: editor
.name
});
38 $scope
.afform
= _
.cloneDeep(newForm
);
39 if (editor
.name
!= '0') {
40 alert('Error: unknown form "' + editor
.name
+ '"');
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');
48 if (editor
.name
== '0') {
49 editor
.addEntity('Individual');
50 editor
.layout
['#children'].push(afAdmin
.meta
.elements
.submit
.element
);
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;
60 $scope
.updateLayoutHtml = function() {
61 $scope
.layoutHtml
= '...Loading...';
62 crmApi4('Afform', 'convert', {layout
: [editor
.layout
], from: 'deep', to
: 'html', formatWhitespace
: true})
64 $scope
.layoutHtml
= r
[0].layout
|| '(Error)';
67 $scope
.layoutHtml
= '(Error)';
71 this.addEntity = function(type
) {
72 var meta
= afAdmin
.meta
.entities
[type
],
74 // Give this new entity a unique name
75 while (!!$scope
.entities
[type
+ num
]) {
78 $scope
.entities
[type
+ num
] = _
.assign($parse(meta
.defaults
)($scope
), {
82 label
: meta
.label
+ ' ' + num
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()});
95 // Attempt to place the new af-fieldset after the last one on the form
96 pos
= 1 + _
.findLastIndex(editor
.layout
['#children'], 'af-fieldset');
98 editor
.layout
['#children'].splice(pos
, 0, fieldset
);
100 editor
.layout
['#children'].push(fieldset
);
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);
112 this.selectEntity = function(entityName
) {
113 $scope
.selectedEntityName
= entityName
;
116 this.getEntity = function(entityName
) {
117 return $scope
.entities
[entityName
];
120 this.getSelectedEntityName = function() {
121 return $scope
.selectedEntityName
;
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();
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();
146 $scope
.addEntity = function(entityType
) {
147 var entityName
= editor
.addEntity(entityType
);
148 editor
.selectEntity(entityName
);
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
);
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
;
174 })(angular
, CRM
.$, CRM
._
);