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