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