5a920362 |
1 | (function($) { |
2 | var CRM = (window.CRM) ? (window.CRM) : (window.CRM = {}); |
3 | if (!CRM.UF) CRM.UF = {}; |
4 | |
5 | var YESNO = [ |
6 | {val: 0, label: ts('No')}, |
7 | {val: 1, label: ts('Yes')} |
8 | ]; |
9 | |
10 | var VISIBILITY = [ |
11 | {val: 'User and User Admin Only', label: ts('User and User Admin Only'), isInSelectorAllowed: false}, |
12 | {val: 'Public Pages', label: ts('Public Pages'), isInSelectorAllowed: true}, |
13 | {val: 'Public Pages and Listings', label: ts('Public Pages and Listings'), isInSelectorAllowed: true} |
14 | ]; |
15 | |
16 | var LOCATION_TYPES = _.map(CRM.PseudoConstant.locationType, function(value, key) { |
17 | return {val: key, label: value}; |
18 | }); |
19 | LOCATION_TYPES.unshift({val: '', label: ts('Primary')}); |
20 | var DEFAULT_LOCATION_TYPE_ID = ''; |
21 | |
22 | var PHONE_TYPES = _.map(CRM.PseudoConstant.phoneType, function(value, key) { |
23 | return {val: key, label: value}; |
24 | }); |
25 | var DEFAULT_PHONE_TYPE_ID = PHONE_TYPES[0].val; |
26 | |
27 | /** |
28 | * Add a help link to a form label |
29 | */ |
30 | function addHelp(title, options) { |
31 | return title + ' <a href="#" onclick=\'CRM.help("' + title + '", ' + JSON.stringify(options) + '); return false;\' title="' + ts('%1 Help', {1: title}) + '" class="helpicon"></a>'; |
32 | } |
33 | |
34 | function watchChanges() { |
35 | CRM.designerApp.vent.trigger('ufUnsaved', true); |
36 | } |
37 | |
38 | /** |
39 | * Parse a "group_type" expression |
40 | * |
41 | * @param string groupTypeExpr example: "Individual,Activity\0ActivityType:2:28" |
42 | * Note: I've seen problems where HTML "�" != JS '\0', so we support ';;' as an equivalent delimiter |
43 | * @return Object example: {coreTypes: {"Individual":true,"Activity":true}, subTypes: {"ActivityType":{2: true, 28:true}]}} |
44 | */ |
45 | CRM.UF.parseTypeList = function(groupTypeExpr) { |
46 | var typeList = {coreTypes: {}, subTypes:{}}; |
47 | // The API may have automatically converted a string with '\0' to an array |
48 | var parts = _.isArray(groupTypeExpr) ? groupTypeExpr : groupTypeExpr.replace(';;','\0').split('\0'); |
49 | var coreTypesExpr = parts[0]; |
50 | var subTypesExpr = parts[1]; |
51 | |
52 | if (coreTypesExpr && coreTypesExpr != '') { |
53 | _.each(coreTypesExpr.split(','), function(coreType){ |
54 | typeList.coreTypes[coreType] = true; |
55 | }); |
56 | } |
57 | |
58 | if (subTypesExpr && subTypesExpr != '') { |
59 | var subTypes = subTypesExpr.split(':'); |
60 | var subTypeKey = subTypes.shift(); |
61 | typeList.subTypes[subTypeKey] = {}; |
62 | _.each(subTypes, function(subTypeId){ |
63 | typeList.subTypes[subTypeKey][subTypeId] = true; |
64 | }); |
65 | } |
66 | return typeList; |
67 | }; |
68 | |
69 | /** |
70 | * This function is a hack for generating simulated values of "entity_name" |
71 | * in the form-field model. |
72 | * |
73 | * @param {string} field_type |
74 | * @return {string} |
75 | */ |
76 | CRM.UF.guessEntityName = function(field_type) { |
77 | switch (field_type) { |
78 | case 'Contact': |
79 | case 'Individual': |
80 | case 'Organization': |
81 | return 'contact_1'; |
82 | case 'Activity': |
83 | return 'activity_1'; |
84 | case 'Contribution': |
85 | return 'contribution_1'; |
86 | case 'Membership': |
87 | return 'membership_1'; |
88 | case 'Participant': |
89 | return 'participant_1'; |
90 | default: |
91 | throw "Cannot guess entity name for field_type=" + field_type; |
92 | } |
93 | } |
94 | |
95 | /** |
96 | * Represents a field in a customizable form. |
97 | */ |
98 | CRM.UF.UFFieldModel = CRM.Backbone.Model.extend({ |
99 | /** |
100 | * Backbone.Form descripton of the field to which this refers |
101 | */ |
102 | defaults: { |
103 | help_pre: '', |
104 | help_post: '', |
105 | /** |
106 | * @var bool, non-persistent indication of whether this field is unique or duplicate |
107 | * within its UFFieldCollection |
108 | */ |
109 | is_duplicate: false |
110 | }, |
111 | schema: { |
112 | 'id': { |
113 | type: 'Number' |
114 | }, |
115 | 'uf_group_id': { |
116 | type: 'Number' |
117 | }, |
118 | 'entity_name': { |
119 | // pseudo-field |
120 | type: 'Text' |
121 | }, |
122 | 'field_name': { |
123 | type: 'Text' |
124 | }, |
125 | 'field_type': { |
126 | type: 'Select', |
127 | options: ['Contact', 'Individual', 'Organization', 'Contribution', 'Membership', 'Participant', 'Activity'] |
128 | }, |
129 | 'help_post': { |
130 | title: addHelp(ts('Field Post Help'), {id: "help", file:"CRM/UF/Form/Field"}), |
131 | type: 'TextArea' |
132 | }, |
133 | 'help_pre': { |
134 | title: addHelp(ts('Field Pre Help'), {id: "help", file:"CRM/UF/Form/Field"}), |
135 | type: 'TextArea' |
136 | }, |
137 | 'in_selector': { |
138 | title: addHelp(ts('Results Columns?'), {id: "in_selector", file:"CRM/UF/Form/Field"}), |
139 | type: 'Select', |
140 | options: YESNO |
141 | }, |
142 | 'is_active': { |
143 | title: addHelp(ts('Active?'), {id: "is_active", file:"CRM/UF/Form/Field"}), |
144 | type: 'Select', |
145 | options: YESNO |
146 | }, |
147 | 'is_multi_summary': { |
148 | title: ts("Include in multi-record listing?"), |
149 | type: 'Select', |
150 | options: YESNO |
151 | }, |
152 | 'is_required': { |
153 | title: addHelp(ts('Required?'), {id: "is_required", file:"CRM/UF/Form/Field"}), |
154 | type: 'Select', |
155 | options: YESNO |
156 | }, |
157 | 'is_reserved': { |
158 | type: 'Select', |
159 | options: YESNO |
160 | }, |
161 | 'is_searchable': { |
162 | title: addHelp(ts("Searchable"), {id: "is_searchable", file:"CRM/UF/Form/Field"}), |
163 | type: 'Select', |
164 | options: YESNO |
165 | }, |
166 | 'is_view': { |
167 | title: addHelp(ts('View Only?'), {id: "is_view", file:"CRM/UF/Form/Field"}), |
168 | type: 'Select', |
169 | options: YESNO |
170 | }, |
171 | 'label': { |
172 | title: ts('Field Label'), |
173 | type: 'Text' |
174 | }, |
175 | 'location_type_id': { |
176 | title: ts('Location Type'), |
177 | type: 'Select', |
178 | options: LOCATION_TYPES |
179 | }, |
180 | 'phone_type_id': { |
181 | title: ts('Phone Type'), |
182 | type: 'Select', |
183 | options: PHONE_TYPES |
184 | }, |
185 | 'visibility': { |
186 | title: addHelp(ts('Visibility'), {id: "visibility", file:"CRM/UF/Form/Field"}), |
187 | type: 'Select', |
188 | options: VISIBILITY |
189 | }, |
190 | 'weight': { |
191 | type: 'Number' |
192 | } |
193 | }, |
194 | initialize: function() { |
195 | this.set('entity_name', CRM.UF.guessEntityName(this.get('field_type'))); |
196 | this.on("rel:ufGroupModel", this.applyDefaults, this); |
197 | this.on('change', watchChanges); |
198 | }, |
199 | applyDefaults: function() { |
200 | var fieldSchema = this.getFieldSchema(); |
201 | if (fieldSchema && fieldSchema.civiIsLocation && !this.get('location_type_id')) { |
202 | this.set('location_type_id', DEFAULT_LOCATION_TYPE_ID); |
203 | } |
204 | if (fieldSchema && fieldSchema.civiIsPhone && !this.get('phone_type_id')) { |
205 | this.set('phone_type_id', DEFAULT_PHONE_TYPE_ID); |
206 | } |
207 | }, |
208 | isInSelectorAllowed: function() { |
209 | var visibility = _.first(_.where(VISIBILITY, {val: this.get('visibility')})); |
210 | return visibility.isInSelectorAllowed; |
211 | }, |
212 | getFieldSchema: function() { |
213 | return this.getRel('ufGroupModel').getFieldSchema(this.get('entity_name'), this.get('field_name')); |
214 | }, |
215 | /** |
216 | * Create a uniqueness signature. Ideally, each UFField in a UFGroup should |
217 | * have a unique signature. |
218 | * |
219 | * @return {String} |
220 | */ |
221 | getSignature: function() { |
222 | return this.get("entity_name") |
223 | + '::' + this.get("field_name") |
224 | + '::' + (this.get("location_type_id") ? this.get("location_type_id") : '') |
225 | + '::' + (this.get("phone_type_id") ? this.get("phone_type_id") : ''); |
226 | }, |
227 | |
228 | /** |
229 | * This is like destroy(), but it only destroys the item on the client-side; |
230 | * it does not trigger REST or Backbone.sync() operations. |
231 | * |
232 | * @return {Boolean} |
233 | */ |
234 | destroyLocal: function() { |
235 | this.trigger('destroy', this, this.collection, {}); |
236 | return false; |
237 | } |
238 | }); |
239 | |
240 | /** |
241 | * Represents a list of fields in a customizable form |
242 | * |
243 | * options: |
244 | * - uf_group_id: int |
245 | */ |
246 | CRM.UF.UFFieldCollection = CRM.Backbone.Collection.extend({ |
247 | model: CRM.UF.UFFieldModel, |
248 | uf_group_id: null, // int |
249 | initialize: function(models, options) { |
250 | options = options || {}; |
251 | this.uf_group_id = options.uf_group_id; |
252 | this.initializeCopyToChildrenRelation('ufGroupModel', options.ufGroupModel, models); |
253 | this.on('add', this.watchDuplicates, this); |
254 | this.on('remove', this.unwatchDuplicates, this); |
255 | this.on('change', watchChanges); |
256 | this.on('add', watchChanges); |
257 | this.on('remove', watchChanges); |
258 | }, |
259 | getFieldsByName: function(entityName, fieldName) { |
260 | return this.filter(function(ufFieldModel) { |
261 | return (ufFieldModel.get('entity_name') == entityName && ufFieldModel.get('field_name') == fieldName); |
262 | }); |
263 | }, |
264 | toSortedJSON: function() { |
265 | var fields = this.map(function(ufFieldModel){ |
266 | return ufFieldModel.toStrictJSON(); |
267 | }); |
268 | return _.sortBy(fields, function(ufFieldJSON){ |
269 | return parseInt(ufFieldJSON.weight); |
270 | }); |
271 | }, |
272 | isAddable: function(ufFieldModel) { |
273 | var entity_name = ufFieldModel.get('entity_name'), |
274 | field_name = ufFieldModel.get('field_name'), |
275 | fieldSchema = this.getRel('ufGroupModel').getFieldSchema(ufFieldModel.get('entity_name'), ufFieldModel.get('field_name')); |
276 | |
277 | if (! fieldSchema) { |
278 | return false; |
279 | } |
280 | var fields = this.getFieldsByName(entity_name, field_name); |
281 | var limit = 1; |
282 | if (fieldSchema.civiIsLocation) { |
283 | limit *= LOCATION_TYPES.length; |
284 | } |
285 | if (fieldSchema.civiIsPhone) { |
286 | limit *= PHONE_TYPES.length; |
287 | } |
288 | return fields.length < limit; |
289 | }, |
290 | watchDuplicates: function(model, collection, options) { |
291 | model.on('change:location_type_id', this.markDuplicates, this); |
292 | model.on('change:phone_type_id', this.markDuplicates, this); |
293 | this.markDuplicates(); |
294 | }, |
295 | unwatchDuplicates: function(model, collection, options) { |
296 | model.off('change:location_type_id', this.markDuplicates, this); |
297 | model.off('change:phone_type_id', this.markDuplicates, this); |
298 | this.markDuplicates(); |
299 | }, |
300 | hasDuplicates: function() { |
301 | var firstDupe = this.find(function(ufFieldModel){ |
302 | return ufFieldModel.get('is_duplicate'); |
303 | }); |
304 | return firstDupe ? true : false; |
305 | }, |
306 | /** |
307 | * |
308 | */ |
309 | markDuplicates: function() { |
310 | var ufFieldModelsByKey = this.groupBy(function(ufFieldModel) { |
311 | return ufFieldModel.getSignature(); |
312 | }); |
313 | this.each(function(ufFieldModel){ |
314 | var is_duplicate = ufFieldModelsByKey[ufFieldModel.getSignature()].length > 1; |
315 | if (is_duplicate != ufFieldModel.get('is_duplicate')) { |
316 | ufFieldModel.set('is_duplicate', is_duplicate); |
317 | } |
318 | }); |
319 | } |
320 | }); |
321 | |
322 | /** |
323 | * Represents an entity in a customizable form |
324 | */ |
325 | CRM.UF.UFEntityModel = CRM.Backbone.Model.extend({ |
326 | schema: { |
327 | 'id': { |
328 | // title: ts(''), |
329 | type: 'Number' |
330 | }, |
331 | 'entity_name': { |
332 | title: ts('Entity Name'), |
333 | help: ts('Symbolic name which referenced in the fields'), |
334 | type: 'Text' |
335 | }, |
336 | 'entity_type': { |
337 | title: ts('Entity Type'), |
338 | type: 'Select', |
339 | options: ['IndividualModel', 'ActivityModel'] |
340 | }, |
341 | 'entity_sub_type': { |
342 | // Use '*' to match all subtypes; use an int to match a specific type id; use empty-string to match none |
343 | title: ts('Sub Type'), |
344 | type: 'Text' |
345 | } |
346 | }, |
347 | defaults: { |
348 | entity_sub_type: '*' |
349 | }, |
350 | initialize: function() { |
351 | }, |
352 | /** |
353 | * Get a list of all fields that can be used with this entity. |
354 | * |
355 | * @return {Object} keys are field names; values are fieldSchemas |
356 | */ |
357 | getFieldSchemas: function() { |
358 | var ufEntityModel = this; |
359 | var modelClass= this.getModelClass(); |
360 | |
361 | if (this.get('entity_sub_type') == '*') { |
362 | return _.clone(modelClass.prototype.schema); |
363 | } |
364 | |
365 | var result = {}; |
366 | _.each(modelClass.prototype.schema, function(fieldSchema, fieldName){ |
367 | var section = modelClass.prototype.sections[fieldSchema.section]; |
368 | if (ufEntityModel.isSectionEnabled(section)) { |
369 | result[fieldName] = fieldSchema; |
370 | } |
371 | }); |
372 | return result; |
373 | }, |
374 | isSectionEnabled: function(section) { |
375 | return (!section || !section.extends_entity_column_value || _.contains(section.extends_entity_column_value, this.get('entity_sub_type'))); |
376 | }, |
377 | getSections: function() { |
378 | var ufEntityModel = this; |
379 | var result = {}; |
380 | _.each(ufEntityModel.getModelClass().prototype.sections, function(section, sectionKey){ |
381 | if (ufEntityModel.isSectionEnabled(section)) { |
382 | result[sectionKey] = section; |
383 | } |
384 | }); |
385 | return result; |
386 | }, |
387 | getModelClass: function() { |
388 | return CRM.Schema[this.get('entity_type')]; |
389 | } |
390 | }); |
391 | |
392 | /** |
393 | * Represents a list of entities in a customizable form |
394 | * |
395 | * options: |
396 | * - ufGroupModel: UFGroupModel |
397 | */ |
398 | CRM.UF.UFEntityCollection = CRM.Backbone.Collection.extend({ |
399 | model: CRM.UF.UFEntityModel, |
400 | byName: {}, |
401 | initialize: function(models, options) { |
402 | options = options || {}; |
403 | this.initializeCopyToChildrenRelation('ufGroupModel', options.ufGroupModel, models); |
404 | }, |
405 | /** |
406 | * |
407 | * @param name |
408 | * @return {UFEntityModel} if found; otherwise, null |
409 | */ |
410 | getByName: function(name) { |
411 | // TODO consider indexing |
412 | return this.find(function(ufEntityModel){ |
413 | return ufEntityModel.get('entity_name') == name; |
414 | }); |
415 | } |
416 | }); |
417 | |
418 | /** |
419 | * Represents a customizable form |
420 | */ |
421 | CRM.UF.UFGroupModel = CRM.Backbone.Model.extend({ |
422 | defaults: { |
423 | title: ts('Unnamed Profile'), |
424 | is_active: 1 |
425 | }, |
426 | schema: { |
427 | 'id': { |
428 | // title: ts(''), |
429 | type: 'Number' |
430 | }, |
431 | 'name': { |
432 | // title: ts(''), |
433 | type: 'Text' |
434 | }, |
435 | 'title': { |
436 | title: ts('Profile Name'), |
437 | help: ts(''), |
438 | type: 'Text', |
439 | validators: ['required'] |
440 | }, |
441 | 'group_type': { |
442 | // For a description of group_type, see CRM_Core_BAO_UFGroup::updateGroupTypes |
443 | // title: ts(''), |
444 | type: 'Text' |
445 | }, |
446 | 'add_captcha': { |
447 | title: ts('Include reCAPTCHA?'), |
448 | help: ts('FIXME'), |
449 | type: 'Select', |
450 | options: YESNO |
451 | }, |
452 | 'add_to_group_id': { |
453 | title: ts('Add new contacts to a Group?'), |
454 | help: ts('Select a group if you are using this profile for adding new contacts, AND you want the new contacts to be automatically assigned to a group.'), |
455 | type: 'Number' |
456 | }, |
457 | 'cancel_URL': { |
458 | title: ts('Cancel Redirect URL'), |
459 | help: ts('If you are using this profile as a contact signup or edit form, and want to redirect the user to a static URL if they click the Cancel button - enter the complete URL here. If this field is left blank, the built-in Profile form will be redisplayed.'), |
460 | type: 'Text' |
461 | }, |
462 | 'created_date': { |
463 | //title: ts(''), |
464 | type: 'Text'// FIXME |
465 | }, |
466 | 'created_id': { |
467 | //title: ts(''), |
468 | type: 'Number' |
469 | }, |
470 | 'help_post': { |
471 | title: ts('Post-form Help'), |
472 | help: ts('Explanatory text displayed at the end of the form.') |
473 | + ts('Note that this help text is displayed on profile create/edit screens only.'), |
474 | type: 'TextArea' |
475 | }, |
476 | 'help_pre': { |
477 | title: ts('Pre-form Help '), |
478 | help: ts('Explanatory text displayed at the beginning of the form.') |
479 | + ts('Note that this help text is displayed on profile create/edit screens only.'), |
480 | type: 'TextArea' |
481 | }, |
482 | 'is_active': { |
483 | title: ts('Is this CiviCRM Profile active?'), |
484 | type: 'Select', |
485 | options: YESNO |
486 | }, |
487 | 'is_cms_user': { |
488 | title: ts('Drupal user account registration option?'),// FIXME |
489 | help: ts('FIXME'), |
490 | type: 'Select', |
491 | options: YESNO // FIXME |
492 | }, |
493 | 'is_edit_link': { |
494 | title: ts('Include profile edit links in search results?'), |
495 | help: ts('Check this box if you want to include a link in the listings to Edit profile fields. Only users with permission to edit the contact will see this link.'), |
496 | type: 'Select', |
497 | options: YESNO |
498 | }, |
499 | 'is_map': { |
500 | title: ts('Enable mapping for this profile?'), |
501 | help: ts('If enabled, a Map link is included on the profile listings rows and detail screens for any contacts whose records include sufficient location data for your mapping provider.'), |
502 | type: 'Select', |
503 | options: YESNO |
504 | }, |
505 | 'is_proximity_search': { |
506 | title: ts('Proximity search'), |
507 | help: ts('FIXME'), |
508 | type: 'Select', |
509 | options: YESNO // FIXME |
510 | }, |
511 | 'is_reserved': { |
512 | // title: ts(''), |
513 | type: 'Select', |
514 | options: YESNO |
515 | }, |
516 | 'is_uf_link': { |
517 | title: ts('Include Drupal user account information links in search results?'), // FIXME |
518 | help: ts('FIXME'), |
519 | type: 'Select', |
520 | options: YESNO |
521 | }, |
522 | 'is_update_dupe': { |
523 | title: ts('What to do upon duplicate match'), |
524 | help: ts('FIXME'), |
525 | type: 'Select', |
526 | options: YESNO // FIXME |
527 | }, |
528 | 'limit_listings_group_id': { |
529 | title: ts('Limit listings to a specific Group?'), |
530 | help: ts('Select a group if you are using this profile for search and listings, AND you want to limit the listings to members of a specific group.'), |
531 | type: 'Number' |
532 | }, |
533 | 'notify': { |
534 | title: ts('Notify when profile form is submitted?'), |
535 | help: ts('If you want member(s) of your organization to receive a notification email whenever this Profile form is used to enter or update contact information, enter one or more email addresses here. Multiple email addresses should be separated by a comma (e.g. jane@example.org, paula@example.org). The first email address listed will be used as the FROM address in the notifications.'), |
536 | type: 'TextArea' |
537 | }, |
538 | 'post_URL': { |
539 | title: ts('Redirect URL'), |
540 | help: ts("If you are using this profile as a contact signup or edit form, and want to redirect the user to a static URL after they've submitted the form, you can also use contact tokens in URL - enter the complete URL here. If this field is left blank, the built-in Profile form will be redisplayed with a generic status message - 'Your contact information has been saved.'"), |
541 | type: 'Text' |
542 | }, |
543 | 'weight': { |
544 | title: ts('Order'), |
545 | help: ts('Weight controls the order in which profiles are presented when more than one profile is included in User Registration or My Account screens. Enter a positive or negative integer - lower numbers are displayed ahead of higher numbers.'), |
546 | type: 'Number' |
547 | // FIXME positive int |
548 | } |
549 | }, |
550 | initialize: function() { |
551 | var ufGroupModel = this; |
552 | |
553 | if (!this.getRel('ufEntityCollection')) { |
554 | var ufEntityCollection = new CRM.UF.UFEntityCollection([], { |
555 | ufGroupModel: this, |
556 | silent: false |
557 | }); |
558 | this.setRel('ufEntityCollection', ufEntityCollection); |
559 | } |
560 | |
561 | if (!this.getRel('ufFieldCollection')) { |
562 | var ufFieldCollection = new CRM.UF.UFFieldCollection([], { |
563 | uf_group_id: this.id, |
564 | ufGroupModel: this |
565 | }); |
566 | this.setRel('ufFieldCollection', ufFieldCollection); |
567 | } |
568 | |
569 | if (!this.getRel('paletteFieldCollection')) { |
570 | var paletteFieldCollection = new CRM.Designer.PaletteFieldCollection([], { |
571 | ufGroupModel: this |
572 | }); |
573 | paletteFieldCollection.sync = function(method, model, options) { |
574 | options || (options = {}); |
575 | // console.log(method, model, options); |
576 | switch (method) { |
577 | case 'read': |
578 | var success = options.success; |
579 | options.success = function(resp, status, xhr) { |
580 | if (success) success(resp, status, xhr); |
581 | model.trigger('sync', model, resp, options); |
582 | }; |
583 | success(ufGroupModel.buildPaletteFields()); |
584 | |
585 | break; |
586 | case 'create': |
587 | case 'update': |
588 | case 'delete': |
589 | default: |
590 | throw 'Unsupported method: ' + method; |
591 | } |
592 | }; |
593 | this.setRel('paletteFieldCollection', paletteFieldCollection); |
594 | } |
595 | |
596 | this.getRel('ufEntityCollection').on('reset', this.resetEntities, this); |
597 | this.resetEntities(); |
598 | |
599 | this.on('change', watchChanges); |
600 | }, |
601 | /** |
602 | * Generate a copy of this UFGroupModel and its fields, with all ID's removed. The result |
603 | * is suitable for a new, identical UFGroup. |
604 | * |
605 | * @return {CRM.UF.UFGroupModel} |
606 | */ |
607 | deepCopy: function() { |
608 | var copy = new CRM.UF.UFGroupModel(_.omit(this.toStrictJSON(), ['id','created_id','created_date','is_reserved','group_type'])); |
609 | copy.getRel('ufEntityCollection').reset( |
610 | this.getRel('ufEntityCollection').toJSON() |
611 | // FIXME: for configurable entities, omit ['id', 'uf_group_id'] |
612 | ); |
613 | copy.getRel('ufFieldCollection').reset( |
614 | this.getRel('ufFieldCollection').map(function(ufFieldModel) { |
615 | return _.omit(ufFieldModel.toStrictJSON(), ['id', 'uf_group_id']); |
616 | }) |
617 | ); |
618 | copy.set('title', ts('%1 (Copy)', { |
619 | 1: copy.get('title') |
620 | })); |
621 | return copy; |
622 | }, |
623 | getModelClass: function(entity_name) { |
624 | var ufEntity = this.getRel('ufEntityCollection').getByName(entity_name); |
625 | if (!ufEntity) throw 'Failed to locate entity: ' + entity_name; |
626 | return ufEntity.getModelClass(); |
627 | }, |
628 | getFieldSchema: function(entity_name, field_name) { |
629 | var modelClass = this.getModelClass(entity_name); |
630 | var fieldSchema = modelClass.prototype.schema[field_name]; |
631 | if (!fieldSchema) { |
632 | if (console.log) { |
633 | console.log('Failed to locate field: ' + entity_name + "." + field_name); |
634 | } |
635 | return null; |
636 | } |
637 | return fieldSchema; |
638 | }, |
639 | /** |
640 | * Check that the group_type contains *only* the types listed in validTypes |
641 | * |
642 | * @param string validTypesExpr |
643 | * @return {Boolean} |
644 | */ |
645 | checkGroupType: function(validTypesExpr) { |
646 | var allMatched = true; |
647 | if (! this.get('group_type') || this.get('group_type') == '') { |
648 | return true; |
649 | } |
650 | |
651 | var actualTypes = CRM.UF.parseTypeList(this.get('group_type')); |
652 | var validTypes = CRM.UF.parseTypeList(validTypesExpr); |
653 | |
654 | // Every actual.coreType is a valid.coreType |
655 | _.each(actualTypes.coreTypes, function(ignore, actualCoreType) { |
656 | if (! validTypes.coreTypes[actualCoreType]) { |
657 | allMatched = false; |
658 | } |
659 | }); |
660 | |
661 | // Every actual.subType is a valid.subType |
662 | _.each(actualTypes.subTypes, function(actualSubTypeIds, actualSubTypeKey) { |
663 | if (!validTypes.subTypes[actualSubTypeKey]) { |
664 | allMatched = false; |
665 | return; |
666 | } |
667 | // actualSubTypeIds is a list of all subtypes which can be used by group, |
668 | // so it's sufficient to match any one of them |
669 | var subTypeMatched = false; |
670 | _.each(actualSubTypeIds, function(ignore, actualSubTypeId) { |
671 | if (validTypes.subTypes[actualSubTypeKey][actualSubTypeId]) { |
672 | subTypeMatched = true; |
673 | } |
674 | }); |
675 | allMatched = allMatched && subTypeMatched; |
676 | }); |
677 | return allMatched; |
678 | }, |
679 | resetEntities: function() { |
680 | var ufGroupModel = this; |
681 | ufGroupModel.getRel('ufFieldCollection').each(function(ufFieldModel){ |
682 | if (!ufFieldModel.getFieldSchema()) { |
683 | CRM.alert(ts('The data model no longer includes field "%1"! All references to the field have been removed.', { |
684 | 1: ufFieldModel.get('entity_name') + "." + ufFieldModel.get('field_name') |
685 | }), '', 'alert', {expires: false}); |
686 | ufFieldModel.destroyLocal(); |
687 | } |
688 | }); |
689 | this.getRel('paletteFieldCollection').reset(this.buildPaletteFields()); |
690 | }, |
691 | /** |
692 | * |
693 | * @return {Array} of PaletteFieldModel |
694 | */ |
695 | buildPaletteFields: function() { |
696 | // rebuild list of fields; reuse old instances of PaletteFieldModel and create new ones |
697 | // as appropriate |
698 | // Note: The system as a whole is ill-defined in cases where we have an existing |
699 | // UFField that references a model field that disappears. |
700 | |
701 | var ufGroupModel = this; |
702 | |
703 | var oldPaletteFieldModelsBySig = {}; |
704 | this.getRel('paletteFieldCollection').each(function(paletteFieldModel){ |
705 | oldPaletteFieldModelsBySig[paletteFieldModel.get("entityName") + '::' + paletteFieldModel.get("fieldName")] = paletteFieldModel; |
706 | }); |
707 | |
708 | var newPaletteFieldModels = []; |
709 | this.getRel('ufEntityCollection').each(function(ufEntityModel){ |
710 | var modelClass = ufEntityModel.getModelClass(); |
711 | _.each(ufEntityModel.getFieldSchemas(), function(value, key, list) { |
712 | var model = oldPaletteFieldModelsBySig[ufEntityModel.get('entity_name') + '::' + key]; |
713 | if (!model) { |
714 | model = new CRM.Designer.PaletteFieldModel({ |
715 | modelClass: modelClass, |
716 | entityName: ufEntityModel.get('entity_name'), |
717 | fieldName: key |
718 | }); |
719 | } |
720 | newPaletteFieldModels.push(model); |
721 | }); |
722 | }); |
723 | |
724 | return newPaletteFieldModels; |
725 | } |
726 | }); |
727 | |
728 | /** |
729 | * Represents a list of customizable form |
730 | */ |
731 | CRM.UF.UFGroupCollection = CRM.Backbone.Collection.extend({ |
732 | model: CRM.UF.UFGroupModel |
733 | }); |
734 | })(cj); |