CRM-15855 - Add crmAutosave module
[civicrm-core.git] / js / angular-crmMailing.js
CommitLineData
030dce01 1(function (angular, $, _) {
030dce01 2
88e9e883 3 angular.module('crmMailing', [
a8e65974 4 'crmUtil', 'crmAttachment', '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);
42 }
f4f103fa 43 }
b7c3beb2 44 });
030dce01
TO
45 });
46 }
47 ]);
48
416abe87 49 angular.module('crmMailing').controller('ListMailingsCtrl', ['crmLegacy', 'crmNavigator', function ListMailingsCtrl(crmLegacy, crmNavigator) {
030dce01
TO
50 // We haven't implemented this in Angular, but some users may get clever
51 // about typing URLs, so we'll provide a redirect.
416abe87
PH
52 var new_url = crmLegacy.url('civicrm/mailing/browse/unscheduled', {reset: 1, scheduled: 'false'});
53 crmNavigator.redirect(new_url);
54 }]);
030dce01 55
b7c3beb2
TO
56 angular.module('crmMailing').controller('CreateMailingCtrl', function EditMailingCtrl($scope, selectedMail, $location) {
57 // Transition URL "/mailing/new/foo" => "/mailing/123/foo"
58 var parts = $location.path().split('/'); // e.g. "/mailing/new" or "/mailing/123/wizard"
59 parts[2] = selectedMail.id;
60 $location.path(parts.join('/'));
61 $location.replace();
62 });
63
58dfba8d 64 angular.module('crmMailing').controller('EditMailingCtrl', function EditMailingCtrl($scope, selectedMail, $location, crmMailingMgr, crmStatus, CrmAttachments, crmMailingPreviewMgr) {
27e690c2 65 $scope.mailing = selectedMail;
f4f103fa 66 $scope.attachments = new CrmAttachments(function () {
db083bf0
TO
67 return {entity_table: 'civicrm_mailing', entity_id: $scope.mailing.id};
68 });
ab0a4aec 69 $scope.attachments.load();
27e690c2
TO
70 $scope.crmMailingConst = CRM.crmMailing;
71
5d8901af 72 var ts = $scope.ts = CRM.ts(null);
27e690c2 73
86c3a327
TO
74 $scope.isSubmitted = function isSubmitted() {
75 return _.size($scope.mailing.jobs) > 0;
76 };
77
58dfba8d
TO
78 // @return Promise
79 $scope.previewMailing = function previewMailing(mailing, mode) {
80 return crmMailingPreviewMgr.preview(mailing, mode);
81 };
82
83 // @return Promise
84 $scope.sendTest = function sendTest(mailing, attachments, recipient) {
85 var savePromise = crmMailingMgr.save(mailing)
86 .then(function () {
87 return attachments.save();
b7c3beb2 88 });
58dfba8d
TO
89 return crmStatus({start: ts('Saving...'), success: ''}, savePromise)
90 .then(function () {
91 crmMailingPreviewMgr.sendTest(mailing, recipient);
92 });
93 };
94
705c61e9
TO
95 // @return Promise
96 $scope.submit = function submit() {
db083bf0 97 var promise = crmMailingMgr.save($scope.mailing)
86c3a327
TO
98 .then(function () {
99 // pre-condition: the mailing exists *before* saving attachments to it
100 return $scope.attachments.save();
101 })
102 .then(function () {
103 return crmMailingMgr.submit($scope.mailing);
104 })
105 .then(function () {
b0797ac3 106 leave('scheduled');
86c3a327
TO
107 })
108 ;
db083bf0 109 return crmStatus({start: ts('Submitting...'), success: ts('Submitted')}, promise);
2d36e6bc 110 };
58dfba8d 111
705c61e9
TO
112 // @return Promise
113 $scope.save = function save() {
f286acec 114 return crmStatus(null,
db083bf0
TO
115 crmMailingMgr
116 .save($scope.mailing)
117 .then(function () {
118 // pre-condition: the mailing exists *before* saving attachments to it
119 return $scope.attachments.save();
120 })
f286acec 121 );
2d36e6bc 122 };
58dfba8d 123
705c61e9
TO
124 // @return Promise
125 $scope.delete = function cancel() {
f286acec
TO
126 return crmStatus({start: ts('Deleting...'), success: ts('Deleted')},
127 crmMailingMgr.delete($scope.mailing)
b0797ac3 128 .then(function () {
f2bad133 129 leave('unscheduled');
b0797ac3 130 })
f286acec 131 );
2d36e6bc 132 };
58dfba8d 133
b0797ac3
TO
134 // @param string listingScreen 'archive', 'scheduled', 'unscheduled'
135 function leave(listingScreen) {
136 switch (listingScreen) {
137 case 'archive':
138 window.location = CRM.url('civicrm/mailing/browse/archived', {
139 reset: 1
140 });
141 break;
142 case 'scheduled':
143 window.location = CRM.url('civicrm/mailing/browse/scheduled', {
144 reset: 1,
145 scheduled: 'true'
146 });
147 break;
148 case 'unscheduled':
8acf0123 149 /* falls through */
b0797ac3
TO
150 default:
151 window.location = CRM.url('civicrm/mailing/browse/unscheduled', {
152 reset: 1,
153 scheduled: 'false'
154 });
155 }
f2bad133 156 }
030dce01
TO
157 });
158
7801d9b5
TO
159 // Controller for the edit-recipients fields (
160 // WISHLIST: Move most of this to a (cache-enabled) service
161 // Scope members:
162 // - [input] mailing: object
163 // - [output] recipients: array of recipient records
88e9e883 164 angular.module('crmMailing').controller('EditRecipCtrl', function EditRecipCtrl($scope, dialogService, crmApi, crmMailingMgr) {
5d8901af 165 var ts = $scope.ts = CRM.ts(null);
7801d9b5
TO
166 $scope.recipients = null;
167 $scope.getRecipientsEstimate = function () {
168 var ts = $scope.ts;
8acf0123 169 if ($scope.recipients === null) {
7801d9b5 170 return ts('(Estimating)');
f4f103fa 171 }
8acf0123 172 if ($scope.recipients.length === 0) {
7801d9b5 173 return ts('No recipients');
f4f103fa 174 }
8acf0123 175 if ($scope.recipients.length === 1) {
7801d9b5 176 return ts('~1 recipient');
f4f103fa
TO
177 }
178 if (RECIPIENTS_PREVIEW_LIMIT > 0 && $scope.recipients.length >= RECIPIENTS_PREVIEW_LIMIT) {
b73e0c53 179 return ts('>%1 recipients', {1: RECIPIENTS_PREVIEW_LIMIT});
f4f103fa 180 }
7801d9b5
TO
181 return ts('~%1 recipients', {1: $scope.recipients.length});
182 };
f4f103fa 183 $scope.getIncludesAsString = function () {
47bacc20
TO
184 var first = true;
185 var names = '';
f4f103fa
TO
186 _.each($scope.mailing.groups.include, function (id) {
187 if (!first) {
188 names = names + ', ';
189 }
190 var group = _.where(CRM.crmMailing.groupNames, {id: '' + id});
47bacc20
TO
191 names = names + group[0].title;
192 first = false;
193 });
f4f103fa
TO
194 _.each($scope.mailing.mailings.include, function (id) {
195 if (!first) {
196 names = names + ', ';
197 }
198 var oldMailing = _.where(CRM.crmMailing.civiMails, {id: '' + id});
47bacc20
TO
199 names = names + oldMailing[0].name;
200 first = false;
201 });
202 return names;
203 };
f4f103fa 204 $scope.getExcludesAsString = function () {
47bacc20
TO
205 var first = true;
206 var names = '';
f4f103fa
TO
207 _.each($scope.mailing.groups.exclude, function (id) {
208 if (!first) {
209 names = names + ', ';
210 }
211 var group = _.where(CRM.crmMailing.groupNames, {id: '' + id});
47bacc20
TO
212 names = names + group[0].title;
213 first = false;
214 });
f4f103fa
TO
215 _.each($scope.mailing.mailings.exclude, function (id) {
216 if (!first) {
217 names = names + ', ';
218 }
219 var oldMailing = _.where(CRM.crmMailing.civiMails, {id: '' + id});
47bacc20
TO
220 names = names + oldMailing[0].name;
221 first = false;
222 });
223 return names;
224 };
225
7801d9b5
TO
226 // We monitor four fields -- use debounce so that changes across the
227 // four fields can settle-down before AJAX.
228 var refreshRecipients = _.debounce(function () {
229 $scope.$apply(function () {
230 $scope.recipients = null;
8dfd5110
TO
231 crmMailingMgr.previewRecipients($scope.mailing, RECIPIENTS_PREVIEW_LIMIT).then(function (recipients) {
232 $scope.recipients = recipients;
b73e0c53 233 });
7801d9b5
TO
234 });
235 }, RECIPIENTS_DEBOUNCE_MS);
236 $scope.$watchCollection("mailing.groups.include", refreshRecipients);
237 $scope.$watchCollection("mailing.groups.exclude", refreshRecipients);
238 $scope.$watchCollection("mailing.mailings.include", refreshRecipients);
239 $scope.$watchCollection("mailing.mailings.exclude", refreshRecipients);
240
4dd19229 241 $scope.previewRecipients = function previewRecipients() {
7801d9b5
TO
242 var model = {
243 recipients: $scope.recipients
244 };
245 var options = {
246 autoOpen: false,
247 modal: true,
248 title: ts('Preview (%1)', {
249 1: $scope.getRecipientsEstimate()
52f515c6 250 })
7801d9b5 251 };
ef5d18a1 252 dialogService.open('recipDialog', '~/crmMailing/dialog/recipients.html', model, options);
7801d9b5
TO
253 };
254 });
255
256 // Controller for the "Preview Recipients" dialog
257 // Note: Expects $scope.model to be an object with properties:
258 // - recipients: array of contacts
88e9e883 259 angular.module('crmMailing').controller('PreviewRecipCtrl', function ($scope) {
5d8901af 260 $scope.ts = CRM.ts(null);
7801d9b5 261 });
493eb47a 262
493eb47a
TO
263 // Controller for the "Preview Mailing" dialog
264 // Note: Expects $scope.model to be an object with properties:
265 // - "subject"
266 // - "body_html"
267 // - "body_text"
870cbdbb 268 angular.module('crmMailing').controller('PreviewMailingDialogCtrl', function PreviewMailingDialogCtrl($scope) {
5d8901af 269 $scope.ts = CRM.ts(null);
493eb47a
TO
270 });
271
47bacc20
TO
272 // Controller for the "Preview Mailing Component" segment
273 // which displays header/footer/auto-responder
88e9e883 274 angular.module('crmMailing').controller('PreviewComponentCtrl', function PreviewMailingDialogCtrl($scope, dialogService) {
5d8901af 275 var ts = $scope.ts = CRM.ts(null);
52f515c6 276
47bacc20 277 $scope.previewComponent = function previewComponent(title, componentId) {
f4f103fa 278 var component = _.where(CRM.crmMailing.headerfooterList, {id: "" + componentId});
47bacc20
TO
279 if (!component || !component[0]) {
280 CRM.alert(ts('Invalid component ID (%1)', {
281 1: componentId
282 }));
283 return;
284 }
285 var options = {
286 autoOpen: false,
287 modal: true,
288 title: title // component[0].name
289 };
ef5d18a1 290 dialogService.open('previewComponentDialog', '~/crmMailing/dialog/previewComponent.html', component[0], options);
47bacc20
TO
291 };
292 });
293
294 // Controller for the "Preview Mailing" dialog
295 // Note: Expects $scope.model to be an object with properties:
296 // - "name"
297 // - "subject"
298 // - "body_html"
299 // - "body_text"
88e9e883 300 angular.module('crmMailing').controller('PreviewComponentDialogCtrl', function PreviewMailingDialogCtrl($scope) {
5d8901af 301 $scope.ts = CRM.ts(null);
47bacc20
TO
302 });
303
744bebee 304 // Controller for the in-place msg-template management
870cbdbb 305 angular.module('crmMailing').controller('MsgTemplateCtrl', function MsgTemplateCtrl($scope, crmMsgTemplates, dialogService) {
5d8901af 306 var ts = $scope.ts = CRM.ts(null);
744bebee
TO
307 $scope.crmMsgTemplates = crmMsgTemplates;
308
309 // @return Promise MessageTemplate (per APIv3)
74263d6b 310 $scope.saveTemplate = function saveTemplate(mailing) {
744bebee 311 var model = {
74263d6b 312 selected_id: mailing.msg_template_id,
744bebee
TO
313 tpl: {
314 msg_title: '',
74263d6b
TO
315 msg_subject: mailing.subject,
316 msg_text: mailing.body_text,
317 msg_html: mailing.body_html
744bebee
TO
318 }
319 };
320 var options = {
321 autoOpen: false,
322 modal: true,
323 title: ts('Save Template')
324 };
ef5d18a1 325 return dialogService.open('saveTemplateDialog', '~/crmMailing/dialog/saveTemplate.html', model, options)
f4f103fa 326 .then(function (item) {
74263d6b 327 mailing.msg_template_id = item.id;
744bebee
TO
328 return item;
329 });
330 };
331
332 // @param int id
333 // @return Promise
74263d6b 334 $scope.loadTemplate = function loadTemplate(mailing, id) {
744bebee 335 return crmMsgTemplates.get(id).then(function (tpl) {
74263d6b
TO
336 mailing.subject = tpl.msg_subject;
337 mailing.body_text = tpl.msg_text;
338 mailing.body_html = tpl.msg_html;
744bebee
TO
339 });
340 };
341 });
342
343 // Controller for the "Save Message Template" dialog
344 // Scope members:
345 // - [input] "model": Object
346 // - "selected_id": int
347 // - "tpl": Object
348 // - "msg_subject": string
349 // - "msg_text": string
350 // - "msg_html": string
88e9e883 351 angular.module('crmMailing').controller('SaveMsgTemplateDialogCtrl', function SaveMsgTemplateDialogCtrl($scope, crmMsgTemplates, dialogService) {
5d8901af 352 var ts = $scope.ts = CRM.ts(null);
744bebee
TO
353 $scope.saveOpt = {mode: '', newTitle: ''};
354 $scope.selected = null;
355
356 $scope.save = function save() {
357 var tpl = _.extend({}, $scope.model.tpl);
358 switch ($scope.saveOpt.mode) {
359 case 'add':
360 tpl.msg_title = $scope.saveOpt.newTitle;
361 break;
362 case 'update':
363 tpl.id = $scope.selected.id;
364 tpl.msg_title = $scope.selected.msg_title;
365 break;
366 default:
367 throw 'SaveMsgTemplateDialogCtrl: Unrecognized mode: ' + $scope.saveOpt.mode;
368 }
369 return crmMsgTemplates.save(tpl)
370 .then(function (item) {
371 CRM.status(ts('Saved'));
372 return item;
373 });
374 };
375
376 function scopeApply(f) {
377 return function () {
378 var args = arguments;
379 $scope.$apply(function () {
380 f.apply(args);
381 });
382 };
383 }
384
385 function init() {
386 crmMsgTemplates.get($scope.model.selected_id).then(
387 function (tpl) {
388 $scope.saveOpt.mode = 'update';
389 $scope.selected = tpl;
390 },
391 function () {
392 $scope.saveOpt.mode = 'add';
393 $scope.selected = null;
394 }
395 );
396 // When using dialogService with a button bar, the major button actions
397 // need to be registered with the dialog widget (and not embedded in
398 // the body of the dialog).
399 var buttons = {};
400 buttons[ts('Save')] = function () {
401 $scope.save().then(function (item) {
402 dialogService.close('saveTemplateDialog', item);
403 });
404 };
405 buttons[ts('Cancel')] = function () {
406 dialogService.cancel('saveTemplateDialog');
407 };
408 dialogService.setButtons('saveTemplateDialog', buttons);
409 }
f4f103fa 410
744bebee
TO
411 setTimeout(scopeApply(init), 0);
412 });
2d06b3b6 413
58dfba8d 414 angular.module('crmMailing').controller('EmailAddrCtrl', function EmailAddrCtrl($scope, crmFromAddresses) {
0a993c89
TO
415 $scope.crmFromAddresses = crmFromAddresses;
416 });
030dce01 417})(angular, CRM.$, CRM._);