1 (function (angular
, $, _
) {
3 angular
.module('crmMailing', [
4 'crmUtil', 'crmAttachment', 'crmAutosave', 'ngRoute', 'ui.utils', 'crmUi', 'dialogService'
5 ]); // TODO ngSanitize, unsavedChanges
7 // Time to wait before triggering AJAX update to recipients list
8 var RECIPIENTS_DEBOUNCE_MS
= 100;
9 var RECIPIENTS_PREVIEW_LIMIT
= 10000;
11 angular
.module('crmMailing').config([
13 function ($routeProvider
) {
14 $routeProvider
.when('/mailing', {
15 template
: '<div></div>',
16 controller
: 'ListMailingsCtrl'
20 '': '~/crmMailing/edit.html',
21 '/unified': '~/crmMailing/edit-unified.html',
22 '/unified2': '~/crmMailing/edit-unified2.html',
23 '/wizard': '~/crmMailing/edit-wizard.html'
25 angular
.forEach(editorPaths
, function(editTemplate
, pathSuffix
) {
26 $routeProvider
.when('/mailing/new' + pathSuffix
, {
27 template
: '<p>' + ts('Initializing...') + '</p>',
28 controller
: 'CreateMailingCtrl',
30 selectedMail: function(crmMailingMgr
) {
31 var m
= crmMailingMgr
.create();
32 return crmMailingMgr
.save(m
);
36 $routeProvider
.when('/mailing/:id' + pathSuffix
, {
37 templateUrl
: editTemplate
,
38 controller
: 'EditMailingCtrl',
40 selectedMail: function($route
, crmMailingMgr
) {
41 return crmMailingMgr
.get($route
.current
.params
.id
);
49 angular
.module('crmMailing').controller('ListMailingsCtrl', ['crmLegacy', 'crmNavigator', function ListMailingsCtrl(crmLegacy
, crmNavigator
) {
50 // We haven't implemented this in Angular, but some users may get clever
51 // about typing URLs, so we'll provide a redirect.
52 var new_url
= crmLegacy
.url('civicrm/mailing/browse/unscheduled', {reset
: 1, scheduled
: 'false'});
53 crmNavigator
.redirect(new_url
);
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('/'));
64 angular
.module('crmMailing').controller('EditMailingCtrl', function EditMailingCtrl($scope
, selectedMail
, $location
, crmMailingMgr
, crmStatus
, CrmAttachments
, crmMailingPreviewMgr
, crmBlocker
) {
65 $scope
.mailing
= selectedMail
;
66 $scope
.attachments
= new CrmAttachments(function () {
67 return {entity_table
: 'civicrm_mailing', entity_id
: $scope
.mailing
.id
};
69 $scope
.attachments
.load();
70 $scope
.crmMailingConst
= CRM
.crmMailing
;
72 var ts
= $scope
.ts
= CRM
.ts(null);
73 var block
= $scope
.block
= crmBlocker();
75 $scope
.isSubmitted
= function isSubmitted() {
76 return _
.size($scope
.mailing
.jobs
) > 0;
80 $scope
.previewMailing
= function previewMailing(mailing
, mode
) {
81 return crmMailingPreviewMgr
.preview(mailing
, mode
);
85 $scope
.sendTest
= function sendTest(mailing
, attachments
, recipient
) {
86 var savePromise
= crmMailingMgr
.save(mailing
)
88 return attachments
.save();
90 return block(crmStatus({start
: ts('Saving...'), success
: ''}, savePromise
)
92 crmMailingPreviewMgr
.sendTest(mailing
, recipient
);
97 $scope
.submit
= function submit() {
98 if (block
.check() || $scope
.crmMailing
.$invalid
) {
102 var promise
= crmMailingMgr
.save($scope
.mailing
)
104 // pre-condition: the mailing exists *before* saving attachments to it
105 return $scope
.attachments
.save();
108 return crmMailingMgr
.submit($scope
.mailing
);
114 return block(crmStatus({start
: ts('Submitting...'), success
: ts('Submitted')}, promise
));
118 $scope
.save
= function save() {
119 return block(crmStatus(null,
121 .save($scope
.mailing
)
123 // pre-condition: the mailing exists *before* saving attachments to it
124 return $scope
.attachments
.save();
130 $scope
.delete = function cancel() {
131 return block(crmStatus({start
: ts('Deleting...'), success
: ts('Deleted')},
132 crmMailingMgr
.delete($scope
.mailing
)
134 leave('unscheduled');
139 // @param string listingScreen 'archive', 'scheduled', 'unscheduled'
140 function leave(listingScreen
) {
141 switch (listingScreen
) {
143 window
.location
= CRM
.url('civicrm/mailing/browse/archived', {
148 window
.location
= CRM
.url('civicrm/mailing/browse/scheduled', {
156 window
.location
= CRM
.url('civicrm/mailing/browse/unscheduled', {
164 // Controller for the edit-recipients fields (
165 // WISHLIST: Move most of this to a (cache-enabled) service
167 // - [input] mailing: object
168 // - [output] recipients: array of recipient records
169 angular
.module('crmMailing').controller('EditRecipCtrl', function EditRecipCtrl($scope
, dialogService
, crmApi
, crmMailingMgr
) {
170 var ts
= $scope
.ts
= CRM
.ts(null);
171 $scope
.recipients
= null;
172 $scope
.getRecipientsEstimate = function () {
174 if ($scope
.recipients
=== null) {
175 return ts('(Estimating)');
177 if ($scope
.recipients
.length
=== 0) {
178 return ts('No recipients');
180 if ($scope
.recipients
.length
=== 1) {
181 return ts('~1 recipient');
183 if (RECIPIENTS_PREVIEW_LIMIT
> 0 && $scope
.recipients
.length
>= RECIPIENTS_PREVIEW_LIMIT
) {
184 return ts('>%1 recipients', {1: RECIPIENTS_PREVIEW_LIMIT
});
186 return ts('~%1 recipients', {1: $scope
.recipients
.length
});
188 $scope
.getIncludesAsString = function () {
191 _
.each($scope
.mailing
.groups
.include
, function (id
) {
193 names
= names
+ ', ';
195 var group
= _
.where(CRM
.crmMailing
.groupNames
, {id
: '' + id
});
196 names
= names
+ group
[0].title
;
199 _
.each($scope
.mailing
.mailings
.include
, function (id
) {
201 names
= names
+ ', ';
203 var oldMailing
= _
.where(CRM
.crmMailing
.civiMails
, {id
: '' + id
});
204 names
= names
+ oldMailing
[0].name
;
209 $scope
.getExcludesAsString = function () {
212 _
.each($scope
.mailing
.groups
.exclude
, function (id
) {
214 names
= names
+ ', ';
216 var group
= _
.where(CRM
.crmMailing
.groupNames
, {id
: '' + id
});
217 names
= names
+ group
[0].title
;
220 _
.each($scope
.mailing
.mailings
.exclude
, function (id
) {
222 names
= names
+ ', ';
224 var oldMailing
= _
.where(CRM
.crmMailing
.civiMails
, {id
: '' + id
});
225 names
= names
+ oldMailing
[0].name
;
231 // We monitor four fields -- use debounce so that changes across the
232 // four fields can settle-down before AJAX.
233 var refreshRecipients
= _
.debounce(function () {
234 $scope
.$apply(function () {
235 $scope
.recipients
= null;
236 crmMailingMgr
.previewRecipients($scope
.mailing
, RECIPIENTS_PREVIEW_LIMIT
).then(function (recipients
) {
237 $scope
.recipients
= recipients
;
240 }, RECIPIENTS_DEBOUNCE_MS
);
241 $scope
.$watchCollection("mailing.groups.include", refreshRecipients
);
242 $scope
.$watchCollection("mailing.groups.exclude", refreshRecipients
);
243 $scope
.$watchCollection("mailing.mailings.include", refreshRecipients
);
244 $scope
.$watchCollection("mailing.mailings.exclude", refreshRecipients
);
246 $scope
.previewRecipients
= function previewRecipients() {
248 recipients
: $scope
.recipients
253 title
: ts('Preview (%1)', {
254 1: $scope
.getRecipientsEstimate()
257 dialogService
.open('recipDialog', '~/crmMailing/dialog/recipients.html', model
, options
);
261 // Controller for the "Preview Recipients" dialog
262 // Note: Expects $scope.model to be an object with properties:
263 // - recipients: array of contacts
264 angular
.module('crmMailing').controller('PreviewRecipCtrl', function ($scope
) {
265 $scope
.ts
= CRM
.ts(null);
268 // Controller for the "Preview Mailing" dialog
269 // Note: Expects $scope.model to be an object with properties:
273 angular
.module('crmMailing').controller('PreviewMailingDialogCtrl', function PreviewMailingDialogCtrl($scope
) {
274 $scope
.ts
= CRM
.ts(null);
277 // Controller for the "Preview Mailing Component" segment
278 // which displays header/footer/auto-responder
279 angular
.module('crmMailing').controller('PreviewComponentCtrl', function PreviewMailingDialogCtrl($scope
, dialogService
) {
280 var ts
= $scope
.ts
= CRM
.ts(null);
282 $scope
.previewComponent
= function previewComponent(title
, componentId
) {
283 var component
= _
.where(CRM
.crmMailing
.headerfooterList
, {id
: "" + componentId
});
284 if (!component
|| !component
[0]) {
285 CRM
.alert(ts('Invalid component ID (%1)', {
293 title
: title
// component[0].name
295 dialogService
.open('previewComponentDialog', '~/crmMailing/dialog/previewComponent.html', component
[0], options
);
299 // Controller for the "Preview Mailing" dialog
300 // Note: Expects $scope.model to be an object with properties:
305 angular
.module('crmMailing').controller('PreviewComponentDialogCtrl', function PreviewMailingDialogCtrl($scope
) {
306 $scope
.ts
= CRM
.ts(null);
309 // Controller for the in-place msg-template management
310 angular
.module('crmMailing').controller('MsgTemplateCtrl', function MsgTemplateCtrl($scope
, crmMsgTemplates
, dialogService
) {
311 var ts
= $scope
.ts
= CRM
.ts(null);
312 $scope
.crmMsgTemplates
= crmMsgTemplates
;
314 // @return Promise MessageTemplate (per APIv3)
315 $scope
.saveTemplate
= function saveTemplate(mailing
) {
317 selected_id
: mailing
.msg_template_id
,
320 msg_subject
: mailing
.subject
,
321 msg_text
: mailing
.body_text
,
322 msg_html
: mailing
.body_html
328 title
: ts('Save Template')
330 return dialogService
.open('saveTemplateDialog', '~/crmMailing/dialog/saveTemplate.html', model
, options
)
331 .then(function (item
) {
332 mailing
.msg_template_id
= item
.id
;
339 $scope
.loadTemplate
= function loadTemplate(mailing
, id
) {
340 return crmMsgTemplates
.get(id
).then(function (tpl
) {
341 mailing
.subject
= tpl
.msg_subject
;
342 mailing
.body_text
= tpl
.msg_text
;
343 mailing
.body_html
= tpl
.msg_html
;
348 // Controller for the "Save Message Template" dialog
350 // - [input] "model": Object
351 // - "selected_id": int
353 // - "msg_subject": string
354 // - "msg_text": string
355 // - "msg_html": string
356 angular
.module('crmMailing').controller('SaveMsgTemplateDialogCtrl', function SaveMsgTemplateDialogCtrl($scope
, crmMsgTemplates
, dialogService
) {
357 var ts
= $scope
.ts
= CRM
.ts(null);
358 $scope
.saveOpt
= {mode
: '', newTitle
: ''};
359 $scope
.selected
= null;
361 $scope
.save
= function save() {
362 var tpl
= _
.extend({}, $scope
.model
.tpl
);
363 switch ($scope
.saveOpt
.mode
) {
365 tpl
.msg_title
= $scope
.saveOpt
.newTitle
;
368 tpl
.id
= $scope
.selected
.id
;
369 tpl
.msg_title
= $scope
.selected
.msg_title
;
372 throw 'SaveMsgTemplateDialogCtrl: Unrecognized mode: ' + $scope
.saveOpt
.mode
;
374 return crmMsgTemplates
.save(tpl
)
375 .then(function (item
) {
376 CRM
.status(ts('Saved'));
381 function scopeApply(f
) {
383 var args
= arguments
;
384 $scope
.$apply(function () {
391 crmMsgTemplates
.get($scope
.model
.selected_id
).then(
393 $scope
.saveOpt
.mode
= 'update';
394 $scope
.selected
= tpl
;
397 $scope
.saveOpt
.mode
= 'add';
398 $scope
.selected
= null;
401 // When using dialogService with a button bar, the major button actions
402 // need to be registered with the dialog widget (and not embedded in
403 // the body of the dialog).
405 buttons
[ts('Save')] = function () {
406 $scope
.save().then(function (item
) {
407 dialogService
.close('saveTemplateDialog', item
);
410 buttons
[ts('Cancel')] = function () {
411 dialogService
.cancel('saveTemplateDialog');
413 dialogService
.setButtons('saveTemplateDialog', buttons
);
416 setTimeout(scopeApply(init
), 0);
419 angular
.module('crmMailing').controller('EmailAddrCtrl', function EmailAddrCtrl($scope
, crmFromAddresses
) {
420 $scope
.crmFromAddresses
= crmFromAddresses
;
422 })(angular
, CRM
.$, CRM
._
);