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