Commit | Line | Data |
---|---|---|
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._); |