Commit | Line | Data |
---|---|---|
4b513f23 | 1 | (function($, _) { |
6a488035 TO |
2 | if (!CRM.Designer) CRM.Designer = {}; |
3 | ||
4 | /** | |
5 | * When rendering a template with Marionette.ItemView, the list of variables is determined by | |
6 | * serializeData(). The normal behavior is to map each property of this.model to a template | |
7 | * variable. | |
8 | * | |
9 | * This function extends that practice by exporting variables "_view", "_model", "_collection", | |
10 | * and "_options". This makes it easier for the template to, e.g., access computed properties of | |
11 | * a model (by calling "_model.getComputedProperty"), or to access constructor options (by | |
12 | * calling "_options.myoption"). | |
13 | * | |
14 | * @return {*} | |
15 | */ | |
16 | var extendedSerializeData = function() { | |
17 | var result = Marionette.ItemView.prototype.serializeData.apply(this); | |
18 | result._view = this; | |
19 | result._model = this.model; | |
20 | result._collection = this.collection; | |
21 | result._options = this.options; | |
22 | return result; | |
abc8b55b | 23 | }; |
6a488035 TO |
24 | |
25 | /** | |
26 | * Display a dialog window with an editable form for a UFGroupModel | |
27 | * | |
28 | * The implementation here is very "jQuery-style" and not "Backbone-style"; | |
29 | * it's been extracted | |
30 | * | |
31 | * options: | |
32 | * - model: CRM.UF.UFGroupModel | |
33 | */ | |
34 | CRM.Designer.DesignerDialog = Backbone.Marionette.Layout.extend({ | |
35 | serializeData: extendedSerializeData, | |
36 | template: '#designer_dialog_template', | |
37 | className: 'crm-designer-dialog', | |
38 | regions: { | |
39 | designerRegion: '.crm-designer' | |
40 | }, | |
41 | /** @var bool whether this dialog is currently open */ | |
42 | isDialogOpen: false, | |
43 | /** @var bool whether any changes have been made */ | |
44 | isUfUnsaved: false, | |
45 | /** @var obj handle for the CRM.alert containing undo link */ | |
46 | undoAlert: null, | |
47 | /** @var bool whether this dialog is being re-opened by the undo link */ | |
48 | undoState: false, | |
49 | ||
50 | initialize: function(options) { | |
51 | CRM.designerApp.vent.on('ufUnsaved', this.onUfChanged, this); | |
23716b45 | 52 | CRM.designerApp.vent.on('ufSaved', this.onUfSaved, this); |
6a488035 TO |
53 | }, |
54 | onClose: function() { | |
3e64165a | 55 | if (this.undoAlert && this.undoAlert.close) this.undoAlert.close(); |
6a488035 TO |
56 | CRM.designerApp.vent.off('ufUnsaved', this.onUfChanged, this); |
57 | }, | |
58 | onUfChanged: function(isUfUnsaved) { | |
59 | this.isUfUnsaved = isUfUnsaved; | |
60 | }, | |
23716b45 CW |
61 | onUfSaved: function() { |
62 | CRM.designerApp.vent.off('ufUnsaved', this.onUfChanged, this); | |
63 | this.isUfUnsaved = false; | |
64 | }, | |
6a488035 TO |
65 | onRender: function() { |
66 | var designerDialog = this; | |
67 | designerDialog.$el.dialog({ | |
68 | autoOpen: true, // note: affects accordion height | |
fb0aac1e CW |
69 | title: ts('Edit Profile'), |
70 | modal: true, | |
6a488035 | 71 | width: '75%', |
3e64165a | 72 | height: parseInt($(window).height() * 0.8, 10), |
6a488035 TO |
73 | minWidth: 500, |
74 | minHeight: 600, // to allow dropping in big whitespace, coordinate with min-height of .crm-designer-fields | |
75 | open: function() { | |
76 | // Prevent conflicts with other onbeforeunload handlers | |
77 | designerDialog.oldOnBeforeUnload = window.onbeforeunload; | |
78 | // Warn of unsaved changes when navigating away from the page | |
79 | window.onbeforeunload = function() { | |
80 | if (designerDialog.isDialogOpen && designerDialog.isUfUnsaved) { | |
81 | return ts("Your profile has not been saved."); | |
82 | } | |
83 | if (designerDialog.oldOnBeforeUnload) { | |
84 | return designerDialog.oldOnBeforeUnload.apply(arguments); | |
85 | } | |
86 | }; | |
3e64165a | 87 | if (designerDialog.undoAlert && designerDialog.undoAlert.close) designerDialog.undoAlert.close(); |
6a488035 TO |
88 | designerDialog.isDialogOpen = true; |
89 | // Initialize new dialog if we are not re-opening unsaved changes | |
90 | if (designerDialog.undoState === false) { | |
3e64165a | 91 | if (designerDialog.designerRegion && designerDialog.designerRegion.close) designerDialog.designerRegion.close(); |
fb0aac1e | 92 | designerDialog.$el.block(); |
6a488035 TO |
93 | designerDialog.options.findCreateUfGroupModel({ |
94 | onLoad: function(ufGroupModel) { | |
95 | designerDialog.model = ufGroupModel; | |
96 | var designerLayout = new CRM.Designer.DesignerLayout({ | |
97 | model: ufGroupModel, | |
98 | el: '<div class="full-height"></div>' | |
99 | }); | |
100 | designerDialog.$el.unblock(); | |
101 | designerDialog.designerRegion.show(designerLayout); | |
102 | CRM.designerApp.vent.trigger('resize'); | |
103 | designerDialog.isUfUnsaved = false; | |
104 | } | |
105 | }); | |
106 | } | |
107 | designerDialog.undoState = false; | |
4151fff2 CW |
108 | // CRM-12188 |
109 | CRM.designerApp.DetachedProfiles = []; | |
6a488035 TO |
110 | }, |
111 | close: function() { | |
112 | window.onbeforeunload = designerDialog.oldOnBeforeUnload; | |
113 | designerDialog.isDialogOpen = false; | |
114 | ||
3e64165a | 115 | if (designerDialog.undoAlert && designerDialog.undoAlert.close) designerDialog.undoAlert.close(); |
6a488035 | 116 | if (designerDialog.isUfUnsaved) { |
8537b332 | 117 | designerDialog.undoAlert = CRM.alert('<p>' + ts('%1 has not been saved.', {1: designerDialog.model.get('title')}) + '</p><a href="#" class="crm-undo">' + ts('Restore') + '</a>', ts('Unsaved Changes'), 'alert', {expires: 60000}); |
d2f7ffaa | 118 | $('.ui-notify-message a.crm-undo').button({icons: {primary: 'fa-undo'}}).click(function(e) { |
fb0aac1e | 119 | e.preventDefault(); |
6a488035 TO |
120 | designerDialog.undoState = true; |
121 | designerDialog.$el.dialog('open'); | |
6a488035 TO |
122 | }); |
123 | } | |
4151fff2 CW |
124 | // CRM-12188 |
125 | CRM.designerApp.restorePreviewArea(); | |
6a488035 TO |
126 | }, |
127 | resize: function() { | |
128 | CRM.designerApp.vent.trigger('resize'); | |
129 | } | |
130 | }); | |
131 | } | |
132 | }); | |
133 | ||
134 | /** | |
135 | * Display a complete form-editing UI, including canvas, palette, and | |
136 | * buttons. | |
137 | * | |
138 | * options: | |
139 | * - model: CRM.UF.UFGroupModel | |
140 | */ | |
141 | CRM.Designer.DesignerLayout = Backbone.Marionette.Layout.extend({ | |
142 | serializeData: extendedSerializeData, | |
143 | template: '#designer_template', | |
144 | regions: { | |
145 | buttons: '.crm-designer-buttonset-region', | |
146 | palette: '.crm-designer-palette-region', | |
147 | form: '.crm-designer-form-region', | |
148 | fields: '.crm-designer-fields-region' | |
149 | }, | |
150 | initialize: function() { | |
151 | CRM.designerApp.vent.on('resize', this.onResize, this); | |
152 | }, | |
153 | onClose: function() { | |
154 | CRM.designerApp.vent.off('resize', this.onResize, this); | |
155 | }, | |
156 | onRender: function() { | |
157 | this.buttons.show(new CRM.Designer.ToolbarView({ | |
158 | model: this.model | |
159 | })); | |
160 | this.palette.show(new CRM.Designer.PaletteView({ | |
161 | model: this.model | |
162 | })); | |
163 | this.form.show(new CRM.Designer.UFGroupView({ | |
164 | model: this.model | |
165 | })); | |
166 | this.fields.show(new CRM.Designer.UFFieldCanvasView({ | |
167 | model: this.model | |
168 | })); | |
169 | }, | |
170 | onResize: function() { | |
171 | if (! this.hasResizedBefore) { | |
172 | this.hasResizedBefore = true; | |
173 | this.$('.crm-designer-toolbar').resizable({ | |
174 | handles: 'w', | |
175 | maxWidth: 400, | |
176 | minWidth: 150, | |
177 | resize: function(event, ui) { | |
178 | $('.crm-designer-canvas').css('margin-right', (ui.size.width + 10) + 'px'); | |
179 | $(this).css({left: '', height: ''}); | |
180 | } | |
181 | }).css({left: '', height: ''}); | |
182 | } | |
183 | } | |
184 | }); | |
185 | ||
186 | /** | |
187 | * Display toolbar with working button | |
188 | * | |
189 | * options: | |
190 | * - model: CRM.UF.UFGroupModel | |
191 | */ | |
192 | CRM.Designer.ToolbarView = Backbone.Marionette.ItemView.extend({ | |
193 | serializeData: extendedSerializeData, | |
194 | template: '#designer_buttons_template', | |
195 | previewMode: false, | |
196 | events: { | |
197 | 'click .crm-designer-save': 'doSave', | |
198 | 'click .crm-designer-preview': 'doPreview' | |
199 | }, | |
200 | onRender: function() { | |
d2f7ffaa | 201 | this.$('.crm-designer-save').button({icons: {primary: 'fa-check'}}).attr({ |
95269eab | 202 | disabled: 'disabled', |
496a23ac | 203 | style: 'opacity:.5; cursor:default;' |
95269eab | 204 | }); |
d2f7ffaa | 205 | this.$('.crm-designer-preview').button({icons: {primary: 'fa-television'}}); |
6a488035 | 206 | }, |
95269eab CW |
207 | initialize: function(options) { |
208 | CRM.designerApp.vent.on('ufUnsaved', this.onUfChanged, this); | |
209 | }, | |
210 | onUfChanged: function(isUfUnsaved) { | |
211 | if (isUfUnsaved) { | |
6f9cd76f | 212 | this.$('.crm-designer-save').removeAttr('style').prop('disabled', false); |
95269eab CW |
213 | } |
214 | }, | |
fb0aac1e | 215 | doSave: function(e) { |
378ea9fc | 216 | e.preventDefault(); |
6a488035 TO |
217 | var ufGroupModel = this.model; |
218 | if (ufGroupModel.getRel('ufFieldCollection').hasDuplicates()) { | |
219 | CRM.alert(ts('Please correct errors before saving.'), '', 'alert'); | |
220 | return; | |
221 | } | |
222 | var $dialog = this.$el.closest('.crm-designer-dialog'); // FIXME use events | |
c31942fe | 223 | $dialog.block(); |
6a488035 TO |
224 | var profile = ufGroupModel.toStrictJSON(); |
225 | profile["api.UFField.replace"] = {values: ufGroupModel.getRel('ufFieldCollection').toSortedJSON(), 'option.autoweight': 0}; | |
226 | CRM.api('UFGroup', 'create', profile, { | |
227 | success: function(data) { | |
228 | $dialog.unblock(); | |
229 | var error = false; | |
230 | if (data.is_error) { | |
231 | CRM.alert(data.error_message); | |
232 | error = true; | |
233 | } | |
234 | _.each(data.values, function(ufGroupResponse) { | |
235 | if (ufGroupResponse['api.UFField.replace'].is_error) { | |
236 | CRM.alert(ufGroupResponse['api.UFField.replace'].error_message); | |
237 | error = true; | |
238 | } | |
239 | }); | |
240 | if (!error) { | |
241 | if (!ufGroupModel.get('id')) { | |
242 | ufGroupModel.set('id', data.id); | |
243 | } | |
244 | CRM.designerApp.vent.trigger('ufUnsaved', false); | |
245 | CRM.designerApp.vent.trigger('ufSaved'); | |
246 | $dialog.dialog('close'); | |
247 | } | |
248 | } | |
249 | }); | |
6a488035 | 250 | }, |
fb0aac1e CW |
251 | doPreview: function(e) { |
252 | e.preventDefault(); | |
6a488035 TO |
253 | this.previewMode = !this.previewMode; |
254 | if (!this.previewMode) { | |
255 | $('.crm-designer-preview-canvas').html(''); | |
256 | $('.crm-designer-canvas > *, .crm-designer-palette-region').show(); | |
d2f7ffaa | 257 | $('.crm-designer-preview').button('option', {icons: {primary: 'fa-television'}}).find('span').text(ts('Preview')); |
6a488035 TO |
258 | return; |
259 | } | |
260 | if (this.model.getRel('ufFieldCollection').hasDuplicates()) { | |
261 | CRM.alert(ts('Please correct errors before previewing.'), '', 'alert'); | |
262 | return; | |
263 | } | |
264 | var $dialog = this.$el.closest('.crm-designer-dialog'); // FIXME use events | |
c31942fe | 265 | $dialog.block(); |
4151fff2 CW |
266 | // CRM-12188 |
267 | CRM.designerApp.clearPreviewArea(); | |
fb0aac1e CW |
268 | $.post(CRM.url("civicrm/ajax/inline"), { |
269 | 'qfKey': CRM.profilePreviewKey, | |
270 | 'class_name': 'CRM_UF_Form_Inline_Preview', | |
271 | 'snippet': 1, | |
272 | 'ufData': JSON.stringify({ | |
273 | ufGroup: this.model.toStrictJSON(), | |
274 | ufFieldCollection: this.model.getRel('ufFieldCollection').toSortedJSON() | |
275 | }) | |
6a488035 TO |
276 | }).done(function(data) { |
277 | $dialog.unblock(); | |
278 | $('.crm-designer-canvas > *, .crm-designer-palette-region').hide(); | |
fb0aac1e | 279 | $('.crm-designer-preview-canvas').html(data).show().trigger('crmLoad').find(':input').prop('readOnly', true); |
d2f7ffaa | 280 | $('.crm-designer-preview').button('option', {icons: {primary: 'fa-pencil'}}).find('span').text(ts('Edit')); |
6a488035 | 281 | }); |
6a488035 TO |
282 | } |
283 | }); | |
284 | ||
285 | /** | |
286 | * Display a selection of available fields | |
287 | * | |
288 | * options: | |
289 | * - model: CRM.UF.UFGroupModel | |
290 | */ | |
291 | CRM.Designer.PaletteView = Backbone.Marionette.ItemView.extend({ | |
292 | serializeData: extendedSerializeData, | |
293 | template: '#palette_template', | |
294 | el: '<div class="full-height"></div>', | |
e871d6eb | 295 | openTreeNodes: [], |
6a488035 TO |
296 | events: { |
297 | 'keyup .crm-designer-palette-search input': 'doSearch', | |
3b66ad0c | 298 | 'change .crm-contact-types': 'doSetPaletteEntity', |
6a488035 | 299 | 'click .crm-designer-palette-clear-search': 'clearSearch', |
e871d6eb CW |
300 | 'click .crm-designer-palette-toggle': 'toggleAll', |
301 | 'click .crm-designer-palette-add button': 'doNewCustomFieldDialog', | |
2b83aa0d | 302 | 'click #crm-designer-add-custom-set': 'doNewCustomSetDialog', |
e871d6eb | 303 | 'dblclick .crm-designer-palette-field': 'doAddToCanvas' |
6a488035 TO |
304 | }, |
305 | initialize: function() { | |
306 | this.model.getRel('ufFieldCollection') | |
307 | .on('add', this.toggleActive, this) | |
308 | .on('remove', this.toggleActive, this); | |
309 | this.model.getRel('paletteFieldCollection') | |
310 | .on('reset', this.render, this); | |
311 | CRM.designerApp.vent.on('resize', this.onResize, this); | |
312 | }, | |
313 | onClose: function() { | |
314 | this.model.getRel('ufFieldCollection') | |
315 | .off('add', this.toggleActive, this) | |
316 | .off('remove', this.toggleActive, this); | |
317 | this.model.getRel('paletteFieldCollection') | |
318 | .off('reset', this.render, this); | |
319 | CRM.designerApp.vent.off('resize', this.onResize, this); | |
320 | }, | |
321 | onRender: function() { | |
322 | var paletteView = this; | |
323 | ||
324 | // Prepare data for jstree | |
325 | var treeData = []; | |
326 | var paletteFieldsByEntitySection = this.model.getRel('paletteFieldCollection').getFieldsByEntitySection(); | |
327 | ||
328 | paletteView.model.getRel('ufEntityCollection').each(function(ufEntityModel){ | |
329 | _.each(ufEntityModel.getSections(), function(section, sectionKey){ | |
8d82d04a | 330 | var defaultValue = paletteView.selectedContactType; |
331 | if (!defaultValue) { | |
d35c2913 | 332 | defaultValue = paletteView.model.calculateContactEntityType(); |
8d82d04a | 333 | } |
3b66ad0c | 334 | |
3b66ad0c | 335 | // set selected option as default, since we are rebuilding palette |
8d82d04a | 336 | paletteView.$('.crm-contact-types').val(defaultValue).prop('selected','selected'); |
3b66ad0c | 337 | |
1d36b248 | 338 | var entitySection = ufEntityModel.get('entity_name') + '-' + sectionKey; |
6a488035 | 339 | var items = []; |
1d36b248 | 340 | if (paletteFieldsByEntitySection[entitySection]) { |
341 | _.each(paletteFieldsByEntitySection[entitySection], function(paletteFieldModel, k) { | |
342 | items.push({data: paletteFieldModel.getLabel(), attr: {'class': 'crm-designer-palette-field', 'data-plm-cid': paletteFieldModel.cid}}); | |
343 | }); | |
344 | } | |
345 | if (section.is_addable) { | |
346 | items.push({data: ts('+ Add New Field'), attr: {'class': 'crm-designer-palette-add'}}); | |
347 | } | |
348 | if (items.length > 0) { | |
349 | treeData.push({ | |
350 | data: section.title, | |
351 | children: items, | |
352 | state: _.contains(paletteView.openTreeNodes, sectionKey) ? 'open' : 'closed', | |
353 | attr: { | |
354 | 'class': 'crm-designer-palette-section', | |
355 | 'data-section': sectionKey, | |
356 | 'data-entity': ufEntityModel.get('entity_name') | |
357 | } | |
358 | }); | |
359 | } | |
3e64165a | 360 | }); |
6a488035 TO |
361 | }); |
362 | ||
363 | this.$('.crm-designer-palette-tree').jstree({ | |
364 | 'json_data': {data: treeData}, | |
365 | 'search': { | |
366 | 'case_insensitive' : true, | |
367 | 'show_only_matches': true | |
368 | }, | |
369 | themes: { | |
370 | "theme": 'classic', | |
371 | "dots": false, | |
4441df69 CW |
372 | "icons": false, |
373 | "url": CRM.config.resourceBase + 'packages/jquery/plugins/jstree/themes/classic/style.css' | |
6a488035 TO |
374 | }, |
375 | 'plugins': ['themes', 'json_data', 'ui', 'search'] | |
376 | }).bind('loaded.jstree', function () { | |
377 | $('.crm-designer-palette-field', this).draggable({ | |
378 | appendTo: '.crm-designer', | |
379 | zIndex: $(this.$el).zIndex() + 5000, | |
380 | helper: 'clone', | |
381 | connectToSortable: '.crm-designer-fields' // FIXME: tight canvas/palette coupling | |
382 | }); | |
6a488035 | 383 | paletteView.model.getRel('ufFieldCollection').each(function(ufFieldModel) { |
3e64165a | 384 | paletteView.toggleActive(ufFieldModel, paletteView.model.getRel('ufFieldCollection')); |
6a488035 | 385 | }); |
e871d6eb | 386 | paletteView.$('.crm-designer-palette-add a').replaceWith('<button>' + $('.crm-designer-palette-add a').first().text() + '</<button>'); |
2b83aa0d CW |
387 | paletteView.$('.crm-designer-palette-tree > ul').append('<li><button id="crm-designer-add-custom-set">+ ' + ts('Add Set of Custom Fields') + '</button></li>'); |
388 | paletteView.$('.crm-designer-palette-tree button').button(); | |
6a488035 TO |
389 | }).bind("select_node.jstree", function (e, data) { |
390 | $(this).jstree("toggle_node", data.rslt.obj); | |
391 | $(this).jstree("deselect_node", data.rslt.obj); | |
392 | }); | |
393 | ||
394 | // FIXME: tight canvas/palette coupling | |
395 | this.$(".crm-designer-fields").droppable({ | |
396 | activeClass: "ui-state-default", | |
397 | hoverClass: "ui-state-hover", | |
398 | accept: ":not(.ui-sortable-helper)" | |
399 | }); | |
400 | ||
401 | this.onResize(); | |
402 | }, | |
403 | onResize: function() { | |
404 | var pos = this.$('.crm-designer-palette-tree').position(); | |
405 | var div = this.$('.crm-designer-palette-tree').closest('.crm-container').height(); | |
406 | this.$('.crm-designer-palette-tree').css({height: div - pos.top}); | |
407 | }, | |
fb0aac1e CW |
408 | doSearch: function(e) { |
409 | var str = $(e.target).val(); | |
410 | this.$('.crm-designer-palette-clear-search').css('visibility', str ? 'visible' : 'hidden'); | |
411 | this.$('.crm-designer-palette-tree').jstree("search", str); | |
6a488035 | 412 | }, |
3b66ad0c | 413 | doSetPaletteEntity: function(event) { |
8d82d04a | 414 | this.selectedContactType = $('.crm-contact-types :selected').val(); |
41fd8e26 | 415 | // loop through entity collection and remove non-valid entity section's |
416 | var newUfEntityModels = []; | |
417 | this.model.getRel('ufEntityCollection').each(function(oldUfEntityModel){ | |
418 | var values = oldUfEntityModel.toJSON(); | |
419 | if (values.entity_name == 'contact_1') { | |
420 | values.entity_type = $('.crm-contact-types :selected').val(); | |
421 | } | |
422 | newUfEntityModels.push(new CRM.UF.UFEntityModel(values)); | |
423 | }); | |
424 | this.model.getRel('ufEntityCollection').reset(newUfEntityModels); | |
3b66ad0c | 425 | }, |
e871d6eb CW |
426 | doAddToCanvas: function(event) { |
427 | var paletteFieldModel = this.model.getRel('paletteFieldCollection').get($(event.currentTarget).attr('data-plm-cid')); | |
428 | paletteFieldModel.addToUFCollection(this.model.getRel('ufFieldCollection')); | |
429 | event.stopPropagation(); | |
430 | }, | |
fb0aac1e CW |
431 | doNewCustomFieldDialog: function(e) { |
432 | e.preventDefault(); | |
6a488035 | 433 | var paletteView = this; |
fb0aac1e CW |
434 | var entityKey = $(e.currentTarget).closest('.crm-designer-palette-section').attr('data-entity'); |
435 | var sectionKey = $(e.currentTarget).closest('.crm-designer-palette-section').attr('data-section'); | |
e871d6eb CW |
436 | var ufEntityModel = paletteView.model.getRel('ufEntityCollection').getByName(entityKey); |
437 | var sections = ufEntityModel.getSections(); | |
3fde8ee5 CW |
438 | var url = CRM.url('civicrm/admin/custom/group/field/add', { |
439 | reset: 1, | |
440 | action: 'add', | |
e871d6eb | 441 | gid: sections[sectionKey].custom_group_id |
3fde8ee5 | 442 | }); |
205bb8ae CW |
443 | CRM.loadForm(url).on('crmFormSuccess', function(e, data) { |
444 | paletteView.doRefresh('custom_' + data.id); | |
3fde8ee5 | 445 | }); |
6a488035 | 446 | }, |
fb0aac1e CW |
447 | doNewCustomSetDialog: function(e) { |
448 | e.preventDefault(); | |
2b83aa0d CW |
449 | var paletteView = this; |
450 | var url = CRM.url('civicrm/admin/custom/group', 'action=add&reset=1'); | |
451 | // Create custom field set and automatically go to next step (create fields) after save button is clicked. | |
452 | CRM.loadForm(url, {refreshAction: ['next']}) | |
453 | .on('crmFormSuccess', function(e, data) { | |
454 | // When form switches to create custom field context, modify button behavior to only continue for "save and new" | |
3e64165a | 455 | if (data.customField) ($(this).data('civiCrmSnippet').options.crmForm.refreshAction = ['next_new']); |
2b83aa0d CW |
456 | paletteView.doRefresh(data.customField ? 'custom_' + data.id : null); |
457 | }); | |
2b83aa0d | 458 | }, |
0b1bae4a | 459 | doRefresh: function(fieldToAdd) { |
6a488035 | 460 | var ufGroupModel = this.model; |
e871d6eb | 461 | this.getOpenTreeNodes(); |
6a488035 TO |
462 | CRM.Schema.reloadModels() |
463 | .done(function(data){ | |
464 | ufGroupModel.resetEntities(); | |
0b1bae4a CW |
465 | if (fieldToAdd) { |
466 | var field = ufGroupModel.getRel('paletteFieldCollection').getFieldByName(null, fieldToAdd); | |
467 | field.addToUFCollection(ufGroupModel.getRel('ufFieldCollection')); | |
468 | } | |
6a488035 TO |
469 | }) |
470 | .fail(function() { | |
471 | CRM.alert(ts('Failed to retrieve schema'), ts('Error'), 'error'); | |
472 | }); | |
6a488035 | 473 | }, |
fb0aac1e CW |
474 | clearSearch: function(e) { |
475 | e.preventDefault(); | |
6a488035 | 476 | $('.crm-designer-palette-search input').val('').keyup(); |
6a488035 TO |
477 | }, |
478 | toggleActive: function(ufFieldModel, ufFieldCollection, options) { | |
479 | var paletteFieldCollection = this.model.getRel('paletteFieldCollection'); | |
480 | var paletteFieldModel = paletteFieldCollection.getFieldByName(ufFieldModel.get('entity_name'), ufFieldModel.get('field_name')); | |
481 | var isAddable = ufFieldCollection.isAddable(ufFieldModel); | |
41fd8e26 | 482 | if (paletteFieldModel) { |
483 | this.$('[data-plm-cid='+paletteFieldModel.cid+']').toggleClass('disabled', !isAddable); | |
484 | } | |
6a488035 | 485 | }, |
fb0aac1e | 486 | toggleAll: function(e) { |
8fa83fd7 | 487 | if (_.isEmpty($('.crm-designer-palette-search input').val())) { |
fb0aac1e | 488 | $('.crm-designer-palette-tree').jstree($(e.target).attr('rel')); |
6a488035 | 489 | } |
fb0aac1e | 490 | e.preventDefault(); |
e871d6eb CW |
491 | }, |
492 | getOpenTreeNodes: function() { | |
493 | var paletteView = this; | |
494 | this.openTreeNodes = []; | |
495 | this.$('.crm-designer-palette-section.jstree-open').each(function() { | |
496 | paletteView.openTreeNodes.push($(this).data('section')); | |
3e64165a | 497 | }); |
6a488035 TO |
498 | } |
499 | }); | |
500 | ||
501 | /** | |
502 | * Display all UFFieldModel objects in a UFGroupModel. | |
503 | * | |
504 | * options: | |
505 | * - model: CRM.UF.UFGroupModel | |
506 | */ | |
507 | CRM.Designer.UFFieldCanvasView = Backbone.Marionette.View.extend({ | |
508 | initialize: function() { | |
509 | this.model.getRel('ufFieldCollection') | |
510 | .on('add', this.updatePlaceholder, this) | |
511 | .on('remove', this.updatePlaceholder, this) | |
41fd8e26 | 512 | .on('add', this.addUFFieldView, this) |
513 | .on('reset', this.render, this); | |
6a488035 TO |
514 | }, |
515 | onClose: function() { | |
516 | this.model.getRel('ufFieldCollection') | |
517 | .off('add', this.updatePlaceholder, this) | |
518 | .off('remove', this.updatePlaceholder, this) | |
41fd8e26 | 519 | .off('add', this.addUFFieldView, this) |
520 | .off('reset', this.render, this); | |
6a488035 TO |
521 | }, |
522 | render: function() { | |
523 | var ufFieldCanvasView = this; | |
524 | this.$el.html(_.template($('#field_canvas_view_template').html())); | |
525 | ||
526 | // BOTTOM: Setup field-level editing | |
527 | var $fields = this.$('.crm-designer-fields'); | |
528 | this.updatePlaceholder(); | |
529 | var ufFieldModels = this.model.getRel('ufFieldCollection').sortBy(function(ufFieldModel) { | |
530 | return parseInt(ufFieldModel.get('weight')); | |
531 | }); | |
532 | _.each(ufFieldModels, function(ufFieldModel) { | |
533 | ufFieldCanvasView.addUFFieldView(ufFieldModel, ufFieldCanvasView.model.getRel('ufFieldCollection'), {skipWeights: true}); | |
534 | }); | |
535 | this.$(".crm-designer-fields").sortable({ | |
536 | placeholder: 'crm-designer-row-placeholder', | |
537 | forcePlaceholderSize: true, | |
f74718b1 | 538 | cancel: 'input,textarea,button,select,option,a,.crm-designer-open', |
6a488035 TO |
539 | receive: function(event, ui) { |
540 | var paletteFieldModel = ufFieldCanvasView.model.getRel('paletteFieldCollection').get(ui.item.attr('data-plm-cid')); | |
541 | var ufFieldModel = paletteFieldModel.addToUFCollection( | |
542 | ufFieldCanvasView.model.getRel('ufFieldCollection'), | |
543 | {skipWeights: true} | |
544 | ); | |
8fa83fd7 | 545 | if (_.isEmpty(ufFieldModel)) { |
6a488035 TO |
546 | ufFieldCanvasView.$('.crm-designer-fields .ui-draggable').remove(); |
547 | } else { | |
548 | // Move from end to the 'dropped' position | |
549 | var ufFieldViewEl = ufFieldCanvasView.$('div[data-field-cid='+ufFieldModel.cid+']').parent(); | |
550 | ufFieldCanvasView.$('.crm-designer-fields .ui-draggable').replaceWith(ufFieldViewEl); | |
551 | } | |
552 | // note: the sortable() update callback will call updateWeight | |
553 | }, | |
554 | update: function() { | |
555 | ufFieldCanvasView.updateWeights(); | |
556 | } | |
557 | }); | |
558 | }, | |
559 | /** Determine visual order of fields and set the model values for "weight" */ | |
560 | updateWeights: function() { | |
561 | var ufFieldCanvasView = this; | |
562 | var weight = 1; | |
563 | var rows = this.$('.crm-designer-row').each(function(key, row) { | |
564 | if ($(row).hasClass('placeholder')) { | |
565 | return; | |
566 | } | |
567 | var ufFieldCid = $(row).attr('data-field-cid'); | |
568 | var ufFieldModel = ufFieldCanvasView.model.getRel('ufFieldCollection').get(ufFieldCid); | |
569 | ufFieldModel.set('weight', weight); | |
570 | weight++; | |
571 | }); | |
572 | }, | |
573 | addUFFieldView: function(ufFieldModel, ufFieldCollection, options) { | |
574 | var paletteFieldModel = this.model.getRel('paletteFieldCollection').getFieldByName(ufFieldModel.get('entity_name'), ufFieldModel.get('field_name')); | |
575 | var ufFieldView = new CRM.Designer.UFFieldView({ | |
576 | el: $("<div></div>"), | |
577 | model: ufFieldModel, | |
578 | paletteFieldModel: paletteFieldModel | |
579 | }); | |
580 | ufFieldView.render(); | |
581 | this.$('.crm-designer-fields').append(ufFieldView.$el); | |
582 | if (! (options && options.skipWeights)) { | |
583 | this.updateWeights(); | |
584 | } | |
585 | }, | |
586 | updatePlaceholder: function() { | |
587 | if (this.model.getRel('ufFieldCollection').isEmpty()) { | |
588 | this.$('.placeholder').css({display: 'block', border: '0 none', cursor: 'default'}); | |
589 | } else { | |
590 | this.$('.placeholder').hide(); | |
591 | } | |
592 | } | |
593 | }); | |
594 | ||
595 | /** | |
596 | * options: | |
597 | * - model: CRM.UF.UFFieldModel | |
598 | * - paletteFieldModel: CRM.Designer.PaletteFieldModel | |
599 | */ | |
600 | CRM.Designer.UFFieldView = Backbone.Marionette.Layout.extend({ | |
601 | serializeData: extendedSerializeData, | |
602 | template: '#field_row_template', | |
603 | expanded: false, | |
604 | regions: { | |
605 | summary: '.crm-designer-field-summary', | |
606 | detail: '.crm-designer-field-detail' | |
607 | }, | |
608 | events: { | |
609 | "click .crm-designer-action-settings": 'doToggleForm', | |
2b83aa0d | 610 | "click button.crm-designer-edit-custom": 'doEditCustomField', |
6a488035 TO |
611 | "click .crm-designer-action-remove": 'doRemove' |
612 | }, | |
613 | modelEvents: { | |
614 | "destroy": 'remove', | |
615 | "change:is_duplicate": 'onChangeIsDuplicate' | |
616 | }, | |
617 | onRender: function() { | |
618 | this.summary.show(new CRM.Designer.UFFieldSummaryView({ | |
619 | model: this.model, | |
620 | fieldSchema: this.model.getFieldSchema(), | |
621 | paletteFieldModel: this.options.paletteFieldModel | |
622 | })); | |
623 | this.detail.show(new CRM.Designer.UFFieldDetailView({ | |
624 | model: this.model, | |
625 | fieldSchema: this.model.getFieldSchema() | |
626 | })); | |
3e64165a | 627 | this.onChangeIsDuplicate(this.model, this.model.get('is_duplicate')); |
6a488035 TO |
628 | if (!this.expanded) { |
629 | this.detail.$el.hide(); | |
630 | } | |
631 | var that = this; | |
632 | CRM.designerApp.vent.on('formOpened', function(event) { | |
633 | if (that.expanded && event != that.cid) { | |
634 | that.doToggleForm(false); | |
635 | } | |
636 | }); | |
637 | }, | |
638 | doToggleForm: function(event) { | |
639 | this.expanded = !this.expanded; | |
640 | if (this.expanded && event !== false) { | |
641 | CRM.designerApp.vent.trigger('formOpened', this.cid); | |
642 | } | |
643 | this.$el.toggleClass('crm-designer-open', this.expanded); | |
644 | var $detail = this.detail.$el; | |
645 | if (!this.expanded) { | |
646 | $detail.toggle('blind', 250); | |
2b83aa0d | 647 | this.$('button.crm-designer-edit-custom').remove(); |
6a488035 TO |
648 | } |
649 | else { | |
650 | var $canvas = $('.crm-designer-canvas'); | |
651 | var top = $canvas.offset().top; | |
652 | $detail.slideDown({ | |
653 | duration: 250, | |
654 | step: function(num, effect) { | |
655 | // Scroll canvas to keep field details visible | |
656 | if (effect.prop == 'height') { | |
657 | if (effect.now + $detail.offset().top - top > $canvas.height() - 9) { | |
658 | $canvas.scrollTop($canvas.scrollTop() + effect.now + $detail.offset().top - top - $canvas.height() + 9); | |
659 | } | |
660 | } | |
661 | } | |
662 | }); | |
2b83aa0d | 663 | if (this.model.get('field_name').split('_')[0] == 'custom') { |
fd66df85 | 664 | this.$('.crm-designer-field-summary > div').append('<button class="crm-designer-edit-custom">' + ts('Edit Custom Field') + '</button>'); |
d2f7ffaa | 665 | this.$('button.crm-designer-edit-custom').button({icons: {primary: 'fa-pencil'}}).attr('title', ts('Edit global settings for this custom field.')); |
2b83aa0d | 666 | } |
6a488035 TO |
667 | } |
668 | }, | |
fb0aac1e CW |
669 | doEditCustomField: function(e) { |
670 | e.preventDefault(); | |
fa3a5fe2 | 671 | var url = CRM.url('civicrm/admin/custom/group/field/update', { |
2b83aa0d CW |
672 | action: 'update', |
673 | reset: 1, | |
674 | id: this.model.get('field_name').split('_')[1] | |
fa3a5fe2 | 675 | }); |
fd66df85 | 676 | var form1 = CRM.loadForm(url) |
fa3a5fe2 | 677 | .on('crmFormLoad', function() { |
d2f7ffaa | 678 | $(this).prepend('<div class="messages status"><i class="crm-i fa-info-circle"></i> ' + ts('Note: This will modify the field system-wide, not just in this profile form.') + '</div>'); |
fb0aac1e | 679 | }); |
2b83aa0d | 680 | }, |
6a488035 TO |
681 | onChangeIsDuplicate: function(model, value, options) { |
682 | this.$el.toggleClass('crm-designer-duplicate', value); | |
683 | }, | |
684 | doRemove: function(event) { | |
685 | var that = this; | |
686 | this.$el.hide(250, function() { | |
687 | that.model.destroyLocal(); | |
688 | }); | |
689 | } | |
690 | }); | |
691 | ||
692 | /** | |
693 | * options: | |
694 | * - model: CRM.UF.UFFieldModel | |
695 | * - fieldSchema: (Backbone.Form schema element) | |
696 | * - paletteFieldModel: CRM.Designer.PaletteFieldModel | |
697 | */ | |
698 | CRM.Designer.UFFieldSummaryView = Backbone.Marionette.ItemView.extend({ | |
699 | serializeData: extendedSerializeData, | |
700 | template: '#field_summary_template', | |
701 | modelEvents: { | |
702 | 'change': 'render' | |
703 | }, | |
704 | ||
705 | /** | |
706 | * Compose a printable string which describes the binding of this UFField to the data model | |
707 | * @return {String} | |
708 | */ | |
709 | getBindingLabel: function() { | |
710 | var result = this.options.paletteFieldModel.getSection().title + ": " + this.options.paletteFieldModel.getLabel(); | |
711 | if (this.options.fieldSchema.civiIsPhone) { | |
712 | result = result + '-' + CRM.PseudoConstant.phoneType[this.model.get('phone_type_id')]; | |
713 | } | |
1a228b4c | 714 | if (this.options.fieldSchema.civiIsWebsite) { |
715 | result = result + '-' + CRM.PseudoConstant.websiteType[this.model.get('website_type_id')]; | |
716 | } | |
6a488035 TO |
717 | if (this.options.fieldSchema.civiIsLocation) { |
718 | var locType = this.model.get('location_type_id') ? CRM.PseudoConstant.locationType[this.model.get('location_type_id')] : ts('Primary'); | |
719 | result = result + ' (' + locType + ')'; | |
720 | } | |
721 | return result; | |
722 | }, | |
723 | ||
724 | /** | |
725 | * Return a string marking if the field is required | |
726 | * @return {String} | |
727 | */ | |
728 | getRequiredMarker: function() { | |
729 | if (this.model.get('is_required') == 1) { | |
730 | return ' <span class="crm-marker">*</span> '; | |
731 | } | |
732 | return ''; | |
733 | }, | |
734 | ||
735 | onRender: function() { | |
736 | this.$el.toggleClass('disabled', this.model.get('is_active') != 1); | |
737 | if (this.model.get("is_reserved") == 1) { | |
738 | this.$('.crm-designer-buttons').hide(); | |
739 | } | |
740 | } | |
741 | }); | |
742 | ||
743 | /** | |
744 | * options: | |
745 | * - model: CRM.UF.UFFieldModel | |
746 | * - fieldSchema: (Backbone.Form schema element) | |
747 | */ | |
748 | CRM.Designer.UFFieldDetailView = Backbone.View.extend({ | |
749 | initialize: function() { | |
750 | // FIXME: hide/display 'in_selector' if 'visibility' is one of the public options | |
1a228b4c | 751 | var fields = ['location_type_id', 'website_type_id', 'phone_type_id', 'label', 'is_multi_summary', 'is_required', 'is_view', 'visibility', 'in_selector', 'is_searchable', 'help_pre', 'help_post', 'is_active']; |
6a488035 TO |
752 | if (! this.options.fieldSchema.civiIsLocation) { |
753 | fields = _.without(fields, 'location_type_id'); | |
754 | } | |
1a228b4c | 755 | if (! this.options.fieldSchema.civiIsWebsite) { |
756 | fields = _.without(fields, 'website_type_id'); | |
757 | } | |
6a488035 TO |
758 | if (! this.options.fieldSchema.civiIsPhone) { |
759 | fields = _.without(fields, 'phone_type_id'); | |
760 | } | |
761 | if (!this.options.fieldSchema.civiIsMultiple) { | |
762 | fields = _.without(fields, 'is_multi_summary'); | |
763 | } | |
0103eaf0 CW |
764 | if (this.options.fieldSchema.type == 'Markup') { |
765 | fields = _.without(fields, 'is_required', 'is_view', 'visibility', 'in_selector', 'is_searchable', 'help_post'); | |
766 | } | |
6a488035 TO |
767 | |
768 | this.form = new Backbone.Form({ | |
769 | model: this.model, | |
770 | fields: fields | |
771 | }); | |
772 | this.form.on('change', this.onFormChange, this); | |
8fd0e719 | 773 | this.model.on('change', this.onModelChange, this); |
6a488035 TO |
774 | }, |
775 | render: function() { | |
776 | this.$el.html(this.form.render().el); | |
777 | this.onFormChange(); | |
778 | }, | |
8fd0e719 CW |
779 | onModelChange: function() { |
780 | $.each(this.form.fields, function(i, field) { | |
781 | this.form.setValue(field.key, this.model.get(field.key)); | |
782 | }); | |
783 | }, | |
6a488035 TO |
784 | onFormChange: function() { |
785 | this.form.commit(); | |
786 | this.$('.field-is_multi_summary').toggle(this.options.fieldSchema.civiIsMultiple ? true : false); | |
787 | this.$('.field-in_selector').toggle(this.model.isInSelectorAllowed()); | |
6a488035 TO |
788 | |
789 | if (!this.model.isInSelectorAllowed() && this.model.get('in_selector') != "0") { | |
790 | this.model.set('in_selector', "0"); | |
0103eaf0 CW |
791 | if (this.form.fields.in_selector) { |
792 | this.form.setValue('in_selector', "0"); | |
793 | } | |
6a488035 TO |
794 | // TODO: It might be nicer if we didn't completely discard in_selector -- e.g. |
795 | // if the value could be restored when the user isInSelectorAllowed becomes true | |
796 | // again. However, I haven't found a simple way to do this. | |
797 | } | |
798 | } | |
799 | }); | |
800 | ||
801 | /** | |
802 | * options: | |
803 | * - model: CRM.UF.UFGroupModel | |
804 | */ | |
805 | CRM.Designer.UFGroupView = Backbone.Marionette.Layout.extend({ | |
806 | serializeData: extendedSerializeData, | |
807 | template: '#form_row_template', | |
808 | expanded: false, | |
809 | regions: { | |
810 | summary: '.crm-designer-form-summary', | |
811 | detail: '.crm-designer-form-detail' | |
812 | }, | |
813 | events: { | |
814 | "click .crm-designer-action-settings": 'doToggleForm' | |
815 | }, | |
816 | onRender: function() { | |
817 | this.summary.show(new CRM.Designer.UFGroupSummaryView({ | |
818 | model: this.model | |
819 | })); | |
820 | this.detail.show(new CRM.Designer.UFGroupDetailView({ | |
821 | model: this.model | |
822 | })); | |
823 | if (!this.expanded) { | |
824 | this.detail.$el.hide(); | |
825 | } | |
826 | var that = this; | |
827 | CRM.designerApp.vent.on('formOpened', function(event) { | |
828 | if (that.expanded && event !== 0) { | |
829 | that.doToggleForm(false); | |
830 | } | |
831 | }); | |
832 | }, | |
833 | doToggleForm: function(event) { | |
834 | this.expanded = !this.expanded; | |
835 | if (this.expanded && event !== false) { | |
836 | CRM.designerApp.vent.trigger('formOpened', 0); | |
837 | } | |
838 | this.$el.toggleClass('crm-designer-open', this.expanded); | |
839 | this.detail.$el.toggle('blind', 250); | |
840 | } | |
841 | }); | |
842 | ||
843 | /** | |
844 | * options: | |
845 | * - model: CRM.UF.UFGroupModel | |
846 | */ | |
847 | CRM.Designer.UFGroupSummaryView = Backbone.Marionette.ItemView.extend({ | |
848 | serializeData: extendedSerializeData, | |
849 | template: '#form_summary_template', | |
850 | modelEvents: { | |
851 | 'change': 'render' | |
852 | }, | |
853 | onRender: function() { | |
854 | this.$el.toggleClass('disabled', this.model.get('is_active') != 1); | |
855 | if (this.model.get("is_reserved") == 1) { | |
856 | this.$('.crm-designer-buttons').hide(); | |
857 | } | |
858 | } | |
859 | }); | |
860 | ||
861 | /** | |
862 | * options: | |
863 | * - model: CRM.UF.UFGroupModel | |
864 | */ | |
865 | CRM.Designer.UFGroupDetailView = Backbone.View.extend({ | |
866 | initialize: function() { | |
867 | this.form = new Backbone.Form({ | |
868 | model: this.model, | |
869 | fields: ['title', 'help_pre', 'help_post', 'is_active'] | |
870 | }); | |
871 | this.form.on('change', this.form.commit, this.form); | |
872 | }, | |
873 | render: function() { | |
874 | this.$el.html(this.form.render().el); | |
875 | } | |
876 | }); | |
877 | ||
4b513f23 | 878 | })(CRM.$, CRM._); |