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