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