CRM-15855 - crmMailingAB
[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
88e9e883 172 angular.module('crmMailing').controller('EditRecipCtrl', function EditRecipCtrl($scope, dialogService, crmApi, crmMailingMgr) {
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 = '';
f4f103fa
TO
194 _.each($scope.mailing.groups.include, function (id) {
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 });
f4f103fa
TO
202 _.each($scope.mailing.mailings.include, function (id) {
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 = '';
f4f103fa
TO
215 _.each($scope.mailing.groups.exclude, function (id) {
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 });
f4f103fa
TO
223 _.each($scope.mailing.mailings.exclude, function (id) {
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);
244 $scope.$watchCollection("mailing.groups.include", refreshRecipients);
245 $scope.$watchCollection("mailing.groups.exclude", refreshRecipients);
246 $scope.$watchCollection("mailing.mailings.include", refreshRecipients);
247 $scope.$watchCollection("mailing.mailings.exclude", refreshRecipients);
248
4dd19229 249 $scope.previewRecipients = function previewRecipients() {
7801d9b5
TO
250 var model = {
251 recipients: $scope.recipients
252 };
253 var options = {
254 autoOpen: false,
255 modal: true,
256 title: ts('Preview (%1)', {
257 1: $scope.getRecipientsEstimate()
52f515c6 258 })
7801d9b5 259 };
ef5d18a1 260 dialogService.open('recipDialog', '~/crmMailing/dialog/recipients.html', model, options);
7801d9b5
TO
261 };
262 });
263
264 // Controller for the "Preview Recipients" dialog
265 // Note: Expects $scope.model to be an object with properties:
266 // - recipients: array of contacts
88e9e883 267 angular.module('crmMailing').controller('PreviewRecipCtrl', function ($scope) {
5d8901af 268 $scope.ts = CRM.ts(null);
7801d9b5 269 });
493eb47a 270
493eb47a
TO
271 // Controller for the "Preview Mailing" dialog
272 // Note: Expects $scope.model to be an object with properties:
273 // - "subject"
274 // - "body_html"
275 // - "body_text"
870cbdbb 276 angular.module('crmMailing').controller('PreviewMailingDialogCtrl', function PreviewMailingDialogCtrl($scope) {
5d8901af 277 $scope.ts = CRM.ts(null);
493eb47a
TO
278 });
279
47bacc20
TO
280 // Controller for the "Preview Mailing Component" segment
281 // which displays header/footer/auto-responder
d376f33e 282 angular.module('crmMailing').controller('PreviewComponentCtrl', function PreviewComponentCtrl($scope, dialogService) {
5d8901af 283 var ts = $scope.ts = CRM.ts(null);
52f515c6 284
47bacc20 285 $scope.previewComponent = function previewComponent(title, componentId) {
f4f103fa 286 var component = _.where(CRM.crmMailing.headerfooterList, {id: "" + componentId});
47bacc20
TO
287 if (!component || !component[0]) {
288 CRM.alert(ts('Invalid component ID (%1)', {
289 1: componentId
290 }));
291 return;
292 }
293 var options = {
294 autoOpen: false,
295 modal: true,
296 title: title // component[0].name
297 };
ef5d18a1 298 dialogService.open('previewComponentDialog', '~/crmMailing/dialog/previewComponent.html', component[0], options);
47bacc20
TO
299 };
300 });
301
d376f33e 302 // Controller for the "Preview Mailing Component" dialog
47bacc20
TO
303 // Note: Expects $scope.model to be an object with properties:
304 // - "name"
305 // - "subject"
306 // - "body_html"
307 // - "body_text"
d376f33e 308 angular.module('crmMailing').controller('PreviewComponentDialogCtrl', function PreviewComponentDialogCtrl($scope) {
5d8901af 309 $scope.ts = CRM.ts(null);
47bacc20
TO
310 });
311
744bebee 312 // Controller for the in-place msg-template management
870cbdbb 313 angular.module('crmMailing').controller('MsgTemplateCtrl', function MsgTemplateCtrl($scope, crmMsgTemplates, dialogService) {
5d8901af 314 var ts = $scope.ts = CRM.ts(null);
744bebee
TO
315 $scope.crmMsgTemplates = crmMsgTemplates;
316
317 // @return Promise MessageTemplate (per APIv3)
74263d6b 318 $scope.saveTemplate = function saveTemplate(mailing) {
744bebee 319 var model = {
74263d6b 320 selected_id: mailing.msg_template_id,
744bebee
TO
321 tpl: {
322 msg_title: '',
74263d6b
TO
323 msg_subject: mailing.subject,
324 msg_text: mailing.body_text,
325 msg_html: mailing.body_html
744bebee
TO
326 }
327 };
328 var options = {
329 autoOpen: false,
330 modal: true,
331 title: ts('Save Template')
332 };
ef5d18a1 333 return dialogService.open('saveTemplateDialog', '~/crmMailing/dialog/saveTemplate.html', model, options)
f4f103fa 334 .then(function (item) {
74263d6b 335 mailing.msg_template_id = item.id;
744bebee
TO
336 return item;
337 });
338 };
339
340 // @param int id
341 // @return Promise
74263d6b 342 $scope.loadTemplate = function loadTemplate(mailing, id) {
744bebee 343 return crmMsgTemplates.get(id).then(function (tpl) {
74263d6b
TO
344 mailing.subject = tpl.msg_subject;
345 mailing.body_text = tpl.msg_text;
346 mailing.body_html = tpl.msg_html;
744bebee
TO
347 });
348 };
349 });
350
351 // Controller for the "Save Message Template" dialog
352 // Scope members:
353 // - [input] "model": Object
354 // - "selected_id": int
355 // - "tpl": Object
356 // - "msg_subject": string
357 // - "msg_text": string
358 // - "msg_html": string
88e9e883 359 angular.module('crmMailing').controller('SaveMsgTemplateDialogCtrl', function SaveMsgTemplateDialogCtrl($scope, crmMsgTemplates, dialogService) {
5d8901af 360 var ts = $scope.ts = CRM.ts(null);
744bebee
TO
361 $scope.saveOpt = {mode: '', newTitle: ''};
362 $scope.selected = null;
363
364 $scope.save = function save() {
365 var tpl = _.extend({}, $scope.model.tpl);
366 switch ($scope.saveOpt.mode) {
367 case 'add':
368 tpl.msg_title = $scope.saveOpt.newTitle;
369 break;
370 case 'update':
371 tpl.id = $scope.selected.id;
372 tpl.msg_title = $scope.selected.msg_title;
373 break;
374 default:
375 throw 'SaveMsgTemplateDialogCtrl: Unrecognized mode: ' + $scope.saveOpt.mode;
376 }
377 return crmMsgTemplates.save(tpl)
378 .then(function (item) {
379 CRM.status(ts('Saved'));
380 return item;
381 });
382 };
383
384 function scopeApply(f) {
385 return function () {
386 var args = arguments;
387 $scope.$apply(function () {
388 f.apply(args);
389 });
390 };
391 }
392
393 function init() {
394 crmMsgTemplates.get($scope.model.selected_id).then(
395 function (tpl) {
396 $scope.saveOpt.mode = 'update';
397 $scope.selected = tpl;
398 },
399 function () {
400 $scope.saveOpt.mode = 'add';
401 $scope.selected = null;
402 }
403 );
404 // When using dialogService with a button bar, the major button actions
405 // need to be registered with the dialog widget (and not embedded in
406 // the body of the dialog).
269d44f5
CW
407 var buttons = [
408 {
409 text: ts('Save'),
410 icons: {primary: 'ui-icon-check'},
411 click: function () {
412 $scope.save().then(function (item) {
413 dialogService.close('saveTemplateDialog', item);
414 });
415 }
416 },
417 {
418 text: ts('Cancel'),
419 icons: {primary: 'ui-icon-close'},
420 click: function () {
421 dialogService.cancel('saveTemplateDialog');
422 }
423 }
424 ];
744bebee
TO
425 dialogService.setButtons('saveTemplateDialog', buttons);
426 }
f4f103fa 427
744bebee
TO
428 setTimeout(scopeApply(init), 0);
429 });
2d06b3b6 430
58dfba8d 431 angular.module('crmMailing').controller('EmailAddrCtrl', function EmailAddrCtrl($scope, crmFromAddresses) {
0a993c89
TO
432 $scope.crmFromAddresses = crmFromAddresses;
433 });
030dce01 434})(angular, CRM.$, CRM._);