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