Merge branch '4.5' of https://github.com/civicrm/civicrm-core into 4.6
[civicrm-core.git] / js / angular-crmMailing.js
CommitLineData
030dce01 1(function (angular, $, _) {
030dce01 2
88e9e883 3 angular.module('crmMailing', [
8443385d 4 'crmUtil', 'crmAttachment', 'crmAutosave', 'ngRoute', 'ui.utils', 'crmUi', 'dialogService'
882e1558 5 ]);
030dce01 6
7801d9b5
TO
7 // Time to wait before triggering AJAX update to recipients list
8 var RECIPIENTS_DEBOUNCE_MS = 100;
b73e0c53 9 var RECIPIENTS_PREVIEW_LIMIT = 10000;
7801d9b5 10
c0e89319
TO
11 var APPROVAL_STATUSES = {'Approved': 1, 'Rejected': 2, 'None': 3};
12
88e9e883 13 angular.module('crmMailing').config([
f4f103fa 14 '$routeProvider',
030dce01 15 function ($routeProvider) {
96ac27bd 16 $routeProvider.when('/mailing', {
030dce01
TO
17 template: '<div></div>',
18 controller: 'ListMailingsCtrl'
19 });
b7c3beb2
TO
20
21 var editorPaths = {
ef5d18a1
TO
22 '': '~/crmMailing/edit.html',
23 '/unified': '~/crmMailing/edit-unified.html',
24 '/unified2': '~/crmMailing/edit-unified2.html',
25 '/wizard': '~/crmMailing/edit-wizard.html'
b7c3beb2
TO
26 };
27 angular.forEach(editorPaths, function(editTemplate, pathSuffix) {
c0e89319
TO
28 if (CRM && CRM.crmMailing && CRM.crmMailing.workflowEnabled) {
29 editTemplate = '~/crmMailing/edit-workflow.html'; // override
30 }
b7c3beb2
TO
31 $routeProvider.when('/mailing/new' + pathSuffix, {
32 template: '<p>' + ts('Initializing...') + '</p>',
33 controller: 'CreateMailingCtrl',
34 resolve: {
35 selectedMail: function(crmMailingMgr) {
36 var m = crmMailingMgr.create();
37 return crmMailingMgr.save(m);
38 }
f4f103fa 39 }
b7c3beb2
TO
40 });
41 $routeProvider.when('/mailing/:id' + pathSuffix, {
42 templateUrl: editTemplate,
43 controller: 'EditMailingCtrl',
44 resolve: {
45 selectedMail: function($route, crmMailingMgr) {
46 return crmMailingMgr.get($route.current.params.id);
f2cdd789
TO
47 },
48 attachments: function($route, CrmAttachments) {
49 var attachments = new CrmAttachments(function () {
50 return {entity_table: 'civicrm_mailing', entity_id: $route.current.params.id};
51 });
52 return attachments.load();
b7c3beb2 53 }
f4f103fa 54 }
b7c3beb2 55 });
030dce01
TO
56 });
57 }
58 ]);
59
416abe87 60 angular.module('crmMailing').controller('ListMailingsCtrl', ['crmLegacy', 'crmNavigator', function ListMailingsCtrl(crmLegacy, crmNavigator) {
030dce01
TO
61 // We haven't implemented this in Angular, but some users may get clever
62 // about typing URLs, so we'll provide a redirect.
416abe87
PH
63 var new_url = crmLegacy.url('civicrm/mailing/browse/unscheduled', {reset: 1, scheduled: 'false'});
64 crmNavigator.redirect(new_url);
65 }]);
030dce01 66
b7c3beb2
TO
67 angular.module('crmMailing').controller('CreateMailingCtrl', function EditMailingCtrl($scope, selectedMail, $location) {
68 // Transition URL "/mailing/new/foo" => "/mailing/123/foo"
69 var parts = $location.path().split('/'); // e.g. "/mailing/new" or "/mailing/123/wizard"
70 parts[2] = selectedMail.id;
71 $location.path(parts.join('/'));
72 $location.replace();
73 });
74
207819ec 75 angular.module('crmMailing').controller('EditMailingCtrl', function EditMailingCtrl($scope, selectedMail, $location, crmMailingMgr, crmStatus, attachments, crmMailingPreviewMgr, crmBlocker, CrmAutosaveCtrl, $timeout, crmUiHelp) {
27e690c2 76 $scope.mailing = selectedMail;
f2cdd789 77 $scope.attachments = attachments;
27e690c2 78 $scope.crmMailingConst = CRM.crmMailing;
c0e89319 79 $scope.checkPerm = CRM.checkPerm;
27e690c2 80
5d8901af 81 var ts = $scope.ts = CRM.ts(null);
207819ec 82 $scope.hs = crmUiHelp({file: 'CRM/Mailing/MailingUI'});
bdd3f781 83 var block = $scope.block = crmBlocker();
c0e89319 84 var myAutosave = null;
27e690c2 85
86c3a327
TO
86 $scope.isSubmitted = function isSubmitted() {
87 return _.size($scope.mailing.jobs) > 0;
88 };
89
c0e89319
TO
90 // usage: approve('Approved')
91 $scope.approve = function approve(status, options) {
92 $scope.mailing.approval_status_id = APPROVAL_STATUSES[status];
93 return myAutosave.suspend($scope.submit(options));
94 };
95
58dfba8d
TO
96 // @return Promise
97 $scope.previewMailing = function previewMailing(mailing, mode) {
98 return crmMailingPreviewMgr.preview(mailing, mode);
99 };
100
101 // @return Promise
102 $scope.sendTest = function sendTest(mailing, attachments, recipient) {
103 var savePromise = crmMailingMgr.save(mailing)
104 .then(function () {
105 return attachments.save();
b7c3beb2 106 });
bdd3f781 107 return block(crmStatus({start: ts('Saving...'), success: ''}, savePromise)
58dfba8d
TO
108 .then(function () {
109 crmMailingPreviewMgr.sendTest(mailing, recipient);
bdd3f781 110 }));
58dfba8d
TO
111 };
112
705c61e9 113 // @return Promise
c0e89319
TO
114 $scope.submit = function submit(options) {
115 options = options || {};
c9ae63bb
TO
116 if (block.check() || $scope.crmMailing.$invalid) {
117 return;
118 }
119
db083bf0 120 var promise = crmMailingMgr.save($scope.mailing)
86c3a327
TO
121 .then(function () {
122 // pre-condition: the mailing exists *before* saving attachments to it
123 return $scope.attachments.save();
124 })
125 .then(function () {
126 return crmMailingMgr.submit($scope.mailing);
127 })
128 .then(function () {
c0e89319
TO
129 if (!options.stay) {
130 $scope.leave('scheduled');
131 }
86c3a327
TO
132 })
133 ;
bdd3f781 134 return block(crmStatus({start: ts('Submitting...'), success: ts('Submitted')}, promise));
2d36e6bc 135 };
58dfba8d 136
705c61e9
TO
137 // @return Promise
138 $scope.save = function save() {
bdd3f781 139 return block(crmStatus(null,
db083bf0
TO
140 crmMailingMgr
141 .save($scope.mailing)
142 .then(function () {
143 // pre-condition: the mailing exists *before* saving attachments to it
144 return $scope.attachments.save();
145 })
bdd3f781 146 ));
2d36e6bc 147 };
58dfba8d 148
705c61e9
TO
149 // @return Promise
150 $scope.delete = function cancel() {
bdd3f781 151 return block(crmStatus({start: ts('Deleting...'), success: ts('Deleted')},
f286acec 152 crmMailingMgr.delete($scope.mailing)
b0797ac3 153 .then(function () {
650a6ffc 154 $scope.leave('unscheduled');
b0797ac3 155 })
bdd3f781 156 ));
2d36e6bc 157 };
58dfba8d 158
b0797ac3 159 // @param string listingScreen 'archive', 'scheduled', 'unscheduled'
650a6ffc 160 $scope.leave = function leave(listingScreen) {
b0797ac3
TO
161 switch (listingScreen) {
162 case 'archive':
163 window.location = CRM.url('civicrm/mailing/browse/archived', {
164 reset: 1
165 });
166 break;
167 case 'scheduled':
168 window.location = CRM.url('civicrm/mailing/browse/scheduled', {
169 reset: 1,
170 scheduled: 'true'
171 });
172 break;
173 case 'unscheduled':
8acf0123 174 /* falls through */
b0797ac3
TO
175 default:
176 window.location = CRM.url('civicrm/mailing/browse/unscheduled', {
177 reset: 1,
178 scheduled: 'false'
179 });
180 }
650a6ffc 181 };
e08a61f9 182
c0e89319 183 myAutosave = new CrmAutosaveCtrl({
e08a61f9
TO
184 save: $scope.save,
185 saveIf: function() {
186 return true;
187 },
188 model: function() {
189 return [$scope.mailing, $scope.attachments.getAutosaveSignature()];
190 },
191 form: function() {
192 return $scope.crmMailing;
193 }
194 });
195 $timeout(myAutosave.start);
196 $scope.$on('$destroy', myAutosave.stop);
030dce01
TO
197 });
198
deea897f
TO
199 angular.module('crmMailing').controller('ViewRecipCtrl', function EditRecipCtrl($scope) {
200 $scope.getIncludesAsString = function(mailing) {
47bacc20
TO
201 var first = true;
202 var names = '';
deea897f 203 _.each(mailing.recipients.groups.include, function (id) {
f4f103fa
TO
204 if (!first) {
205 names = names + ', ';
206 }
207 var group = _.where(CRM.crmMailing.groupNames, {id: '' + id});
47bacc20
TO
208 names = names + group[0].title;
209 first = false;
210 });
deea897f 211 _.each(mailing.recipients.mailings.include, function (id) {
f4f103fa
TO
212 if (!first) {
213 names = names + ', ';
214 }
215 var oldMailing = _.where(CRM.crmMailing.civiMails, {id: '' + id});
47bacc20
TO
216 names = names + oldMailing[0].name;
217 first = false;
218 });
219 return names;
220 };
deea897f 221 $scope.getExcludesAsString = function (mailing) {
47bacc20
TO
222 var first = true;
223 var names = '';
deea897f 224 _.each(mailing.recipients.groups.exclude, function (id) {
f4f103fa
TO
225 if (!first) {
226 names = names + ', ';
227 }
228 var group = _.where(CRM.crmMailing.groupNames, {id: '' + id});
47bacc20
TO
229 names = names + group[0].title;
230 first = false;
231 });
deea897f 232 _.each(mailing.recipients.mailings.exclude, function (id) {
f4f103fa
TO
233 if (!first) {
234 names = names + ', ';
235 }
236 var oldMailing = _.where(CRM.crmMailing.civiMails, {id: '' + id});
47bacc20
TO
237 names = names + oldMailing[0].name;
238 first = false;
239 });
240 return names;
241 };
deea897f
TO
242 });
243
244 // Controller for the edit-recipients fields (
245 // WISHLIST: Move most of this to a (cache-enabled) service
246 // Scope members:
247 // - [input] mailing: object
248 // - [output] recipients: array of recipient records
249 angular.module('crmMailing').controller('EditRecipCtrl', function EditRecipCtrl($scope, dialogService, crmApi, crmMailingMgr, $q, crmMetadata) {
250 var ts = $scope.ts = CRM.ts(null);
5d544a77
TO
251
252 $scope.isMailingList = function isMailingList(group) {
253 var GROUP_TYPE_MAILING_LIST = '2';
254 return _.contains(group.group_type, GROUP_TYPE_MAILING_LIST);
255 };
256
deea897f
TO
257 $scope.recipients = null;
258 $scope.getRecipientsEstimate = function () {
259 var ts = $scope.ts;
260 if ($scope.recipients === null) {
261 return ts('(Estimating)');
262 }
263 if ($scope.recipients.length === 0) {
264 return ts('No recipients');
265 }
266 if ($scope.recipients.length === 1) {
267 return ts('~1 recipient');
268 }
269 if (RECIPIENTS_PREVIEW_LIMIT > 0 && $scope.recipients.length >= RECIPIENTS_PREVIEW_LIMIT) {
270 return ts('>%1 recipients', {1: RECIPIENTS_PREVIEW_LIMIT});
271 }
272 return ts('~%1 recipients', {1: $scope.recipients.length});
273 };
47bacc20 274
7801d9b5
TO
275 // We monitor four fields -- use debounce so that changes across the
276 // four fields can settle-down before AJAX.
277 var refreshRecipients = _.debounce(function () {
278 $scope.$apply(function () {
279 $scope.recipients = null;
c0e89319 280 if (!$scope.mailing) return;
8dfd5110
TO
281 crmMailingMgr.previewRecipients($scope.mailing, RECIPIENTS_PREVIEW_LIMIT).then(function (recipients) {
282 $scope.recipients = recipients;
b73e0c53 283 });
7801d9b5
TO
284 });
285 }, RECIPIENTS_DEBOUNCE_MS);
9cd4f489
TO
286 $scope.$watchCollection("mailing.recipients.groups.include", refreshRecipients);
287 $scope.$watchCollection("mailing.recipients.groups.exclude", refreshRecipients);
288 $scope.$watchCollection("mailing.recipients.mailings.include", refreshRecipients);
289 $scope.$watchCollection("mailing.recipients.mailings.exclude", refreshRecipients);
7801d9b5 290
4dd19229 291 $scope.previewRecipients = function previewRecipients() {
7801d9b5
TO
292 var model = {
293 recipients: $scope.recipients
294 };
b1fc510d 295 var options = CRM.utils.adjustDialogDefaults({
b315fbbc 296 width: '40%',
7801d9b5 297 autoOpen: false,
7801d9b5
TO
298 title: ts('Preview (%1)', {
299 1: $scope.getRecipientsEstimate()
52f515c6 300 })
b1fc510d 301 });
ef5d18a1 302 dialogService.open('recipDialog', '~/crmMailing/dialog/recipients.html', model, options);
7801d9b5 303 };
b8b2e316
TO
304
305 // Open a dialog for editing the advanced recipient options.
306 $scope.editOptions = function editOptions(mailing) {
b1fc510d 307 var options = CRM.utils.adjustDialogDefaults({
b8b2e316 308 autoOpen: false,
b315fbbc
CW
309 width: '40%',
310 height: 'auto',
b8b2e316 311 title: ts('Edit Options')
b1fc510d 312 });
08baf3ec
TO
313 $q.when(crmMetadata.getFields('Mailing')).then(function(fields) {
314 var model = {
315 fields: fields,
316 mailing: mailing
317 };
318 dialogService.open('previewComponentDialog', '~/crmMailing/dialog/recipientOptions.html', model, options);
319 });
b8b2e316 320 };
7801d9b5
TO
321 });
322
323 // Controller for the "Preview Recipients" dialog
324 // Note: Expects $scope.model to be an object with properties:
325 // - recipients: array of contacts
88e9e883 326 angular.module('crmMailing').controller('PreviewRecipCtrl', function ($scope) {
5d8901af 327 $scope.ts = CRM.ts(null);
7801d9b5 328 });
493eb47a 329
493eb47a
TO
330 // Controller for the "Preview Mailing" dialog
331 // Note: Expects $scope.model to be an object with properties:
332 // - "subject"
333 // - "body_html"
334 // - "body_text"
870cbdbb 335 angular.module('crmMailing').controller('PreviewMailingDialogCtrl', function PreviewMailingDialogCtrl($scope) {
5d8901af 336 $scope.ts = CRM.ts(null);
493eb47a
TO
337 });
338
b8b2e316 339 // Controller for the "Recipients: Edit Options" dialog
3cf58cc3
TO
340 // Note: Expects $scope.model to be an object with properties:
341 // - "mailing" (APIv3 mailing object)
342 // - "fields" (list of fields)
3edc9b05 343 angular.module('crmMailing').controller('EditRecipOptionsDialogCtrl', function EditRecipOptionsDialogCtrl($scope, crmUiHelp) {
b8b2e316 344 $scope.ts = CRM.ts(null);
1ce7f3e4 345 $scope.hs = crmUiHelp({file: 'CRM/Mailing/MailingUI'});
b8b2e316
TO
346 });
347
47bacc20
TO
348 // Controller for the "Preview Mailing Component" segment
349 // which displays header/footer/auto-responder
d376f33e 350 angular.module('crmMailing').controller('PreviewComponentCtrl', function PreviewComponentCtrl($scope, dialogService) {
5d8901af 351 var ts = $scope.ts = CRM.ts(null);
52f515c6 352
47bacc20 353 $scope.previewComponent = function previewComponent(title, componentId) {
f4f103fa 354 var component = _.where(CRM.crmMailing.headerfooterList, {id: "" + componentId});
47bacc20
TO
355 if (!component || !component[0]) {
356 CRM.alert(ts('Invalid component ID (%1)', {
357 1: componentId
358 }));
359 return;
360 }
b1fc510d 361 var options = CRM.utils.adjustDialogDefaults({
47bacc20 362 autoOpen: false,
47bacc20 363 title: title // component[0].name
b1fc510d 364 });
ef5d18a1 365 dialogService.open('previewComponentDialog', '~/crmMailing/dialog/previewComponent.html', component[0], options);
47bacc20
TO
366 };
367 });
368
d376f33e 369 // Controller for the "Preview Mailing Component" dialog
47bacc20
TO
370 // Note: Expects $scope.model to be an object with properties:
371 // - "name"
372 // - "subject"
373 // - "body_html"
374 // - "body_text"
d376f33e 375 angular.module('crmMailing').controller('PreviewComponentDialogCtrl', function PreviewComponentDialogCtrl($scope) {
5d8901af 376 $scope.ts = CRM.ts(null);
47bacc20
TO
377 });
378
744bebee 379 // Controller for the in-place msg-template management
870cbdbb 380 angular.module('crmMailing').controller('MsgTemplateCtrl', function MsgTemplateCtrl($scope, crmMsgTemplates, dialogService) {
5d8901af 381 var ts = $scope.ts = CRM.ts(null);
744bebee
TO
382 $scope.crmMsgTemplates = crmMsgTemplates;
383
384 // @return Promise MessageTemplate (per APIv3)
74263d6b 385 $scope.saveTemplate = function saveTemplate(mailing) {
744bebee 386 var model = {
74263d6b 387 selected_id: mailing.msg_template_id,
744bebee
TO
388 tpl: {
389 msg_title: '',
74263d6b
TO
390 msg_subject: mailing.subject,
391 msg_text: mailing.body_text,
392 msg_html: mailing.body_html
744bebee
TO
393 }
394 };
b1fc510d 395 var options = CRM.utils.adjustDialogDefaults({
744bebee 396 autoOpen: false,
b315fbbc
CW
397 height: 'auto',
398 width: '40%',
744bebee 399 title: ts('Save Template')
b1fc510d 400 });
ef5d18a1 401 return dialogService.open('saveTemplateDialog', '~/crmMailing/dialog/saveTemplate.html', model, options)
f4f103fa 402 .then(function (item) {
74263d6b 403 mailing.msg_template_id = item.id;
744bebee
TO
404 return item;
405 });
406 };
407
408 // @param int id
409 // @return Promise
74263d6b 410 $scope.loadTemplate = function loadTemplate(mailing, id) {
744bebee 411 return crmMsgTemplates.get(id).then(function (tpl) {
74263d6b
TO
412 mailing.subject = tpl.msg_subject;
413 mailing.body_text = tpl.msg_text;
414 mailing.body_html = tpl.msg_html;
744bebee
TO
415 });
416 };
417 });
418
419 // Controller for the "Save Message Template" dialog
420 // Scope members:
421 // - [input] "model": Object
422 // - "selected_id": int
423 // - "tpl": Object
424 // - "msg_subject": string
425 // - "msg_text": string
426 // - "msg_html": string
88e9e883 427 angular.module('crmMailing').controller('SaveMsgTemplateDialogCtrl', function SaveMsgTemplateDialogCtrl($scope, crmMsgTemplates, dialogService) {
5d8901af 428 var ts = $scope.ts = CRM.ts(null);
744bebee
TO
429 $scope.saveOpt = {mode: '', newTitle: ''};
430 $scope.selected = null;
431
432 $scope.save = function save() {
433 var tpl = _.extend({}, $scope.model.tpl);
434 switch ($scope.saveOpt.mode) {
435 case 'add':
436 tpl.msg_title = $scope.saveOpt.newTitle;
437 break;
438 case 'update':
439 tpl.id = $scope.selected.id;
440 tpl.msg_title = $scope.selected.msg_title;
441 break;
442 default:
443 throw 'SaveMsgTemplateDialogCtrl: Unrecognized mode: ' + $scope.saveOpt.mode;
444 }
445 return crmMsgTemplates.save(tpl)
446 .then(function (item) {
447 CRM.status(ts('Saved'));
448 return item;
449 });
450 };
451
452 function scopeApply(f) {
453 return function () {
454 var args = arguments;
455 $scope.$apply(function () {
456 f.apply(args);
457 });
458 };
459 }
460
461 function init() {
462 crmMsgTemplates.get($scope.model.selected_id).then(
463 function (tpl) {
464 $scope.saveOpt.mode = 'update';
465 $scope.selected = tpl;
466 },
467 function () {
468 $scope.saveOpt.mode = 'add';
469 $scope.selected = null;
470 }
471 );
472 // When using dialogService with a button bar, the major button actions
473 // need to be registered with the dialog widget (and not embedded in
474 // the body of the dialog).
269d44f5
CW
475 var buttons = [
476 {
477 text: ts('Save'),
478 icons: {primary: 'ui-icon-check'},
479 click: function () {
480 $scope.save().then(function (item) {
481 dialogService.close('saveTemplateDialog', item);
482 });
483 }
484 },
485 {
486 text: ts('Cancel'),
487 icons: {primary: 'ui-icon-close'},
488 click: function () {
489 dialogService.cancel('saveTemplateDialog');
490 }
491 }
492 ];
744bebee
TO
493 dialogService.setButtons('saveTemplateDialog', buttons);
494 }
f4f103fa 495
744bebee
TO
496 setTimeout(scopeApply(init), 0);
497 });
2d06b3b6 498
87391d86
TO
499 angular.module('crmMailing').controller('EmailAddrCtrl', function EmailAddrCtrl($scope, crmFromAddresses, crmUiAlert) {
500 var ts = CRM.ts(null);
501 function changeAlert(winnerField, loserField) {
502 crmUiAlert({
503 title: ts('Conflict'),
504 text: ts('The "%1" option conflicts with the "%2" option. The "%2" option has been disabled.', {
505 1: winnerField,
506 2: loserField
507 })
508 });
509 }
510
0a993c89 511 $scope.crmFromAddresses = crmFromAddresses;
87391d86
TO
512 $scope.checkReplyToChange = function checkReplyToChange(mailing) {
513 if (!_.isEmpty(mailing.replyto_email) && mailing.override_verp == '0') {
514 mailing.override_verp = '1';
16f12592 515 changeAlert(ts('Reply-To'), ts('Track Replies'));
87391d86
TO
516 }
517 };
518 $scope.checkVerpChange = function checkVerpChange(mailing) {
519 if (!_.isEmpty(mailing.replyto_email) && mailing.override_verp == '0') {
520 mailing.replyto_email = '';
521 changeAlert(ts('Track Replies'), ts('Reply-To'));
522 }
523 };
0a993c89 524 });
21fb26b8
TO
525
526 var lastEmailTokenAlert = null;
89f476c4 527 angular.module('crmMailing').controller('EmailBodyCtrl', function EmailBodyCtrl($scope, crmMailingMgr, crmUiAlert, $timeout) {
21fb26b8
TO
528 var ts = CRM.ts(null);
529
89f476c4
TO
530 // ex: if (!hasAllTokens(myMailing, 'body_text)) alert('Oh noes!');
531 $scope.hasAllTokens = function hasAllTokens(mailing, field) {
21fb26b8
TO
532 return _.isEmpty(crmMailingMgr.findMissingTokens(mailing, field));
533 };
534
89f476c4
TO
535 // ex: checkTokens(myMailing, 'body_text', 'insert:body_text')
536 // ex: checkTokens(myMailing, '*')
537 $scope.checkTokens = function checkTokens(mailing, field, insertEvent) {
21fb26b8
TO
538 if (lastEmailTokenAlert) {
539 lastEmailTokenAlert.close();
540 }
89f476c4
TO
541 var missing, insertable;
542 if (field == '*') {
543 insertable = false;
544 missing = angular.extend({},
545 crmMailingMgr.findMissingTokens(mailing, 'body_html'),
546 crmMailingMgr.findMissingTokens(mailing, 'body_text')
547 );
548 } else {
549 insertable = !_.isEmpty(insertEvent);
550 missing = crmMailingMgr.findMissingTokens(mailing, field);
551 }
552 if (!_.isEmpty(missing)) {
553 lastEmailTokenAlert = crmUiAlert({
554 type: 'error',
555 title: ts('Required tokens'),
556 templateUrl: '~/crmMailing/dialog/tokenAlert.html',
557 scope: angular.extend($scope.$new(), {
558 insertable: insertable,
559 insertToken: function(token) {
560 $timeout(function(){
561 $scope.$broadcast(insertEvent, '{' + token + '}');
562 $timeout(function(){
563 checkTokens(mailing, field, insertEvent);
564 });
565 });
566 },
567 missing: missing
568 })
21fb26b8 569 });
21fb26b8 570 }
d03e0d13 571 };
21fb26b8 572 });
720c658b
TO
573
574 angular.module('crmMailing').controller('EditUnsubGroupCtrl', function EditUnsubGroupCtrl($scope) {
575 // CRM.crmMailing.groupNames is a global constant - since it doesn't change, we can digest & cache.
576 var mandatoryIds = [];
577 _.each(CRM.crmMailing.groupNames, function(grp){
578 if (grp.is_hidden == "1") {
579 mandatoryIds.push(parseInt(grp.id));
580 }
581 });
582
583 $scope.isUnsubGroupRequired = function isUnsubGroupRequired(mailing) {
584 return _.intersection(mandatoryIds, mailing.recipients.groups.include).length > 0;
585 };
586 });
030dce01 587})(angular, CRM.$, CRM._);