1 (function (angular
, $, _
) {
2 var partialUrl
= function partialUrl(relPath
) {
3 return CRM
.resourceUrls
['civicrm'] + '/partials/crmMailing/' + relPath
;
6 angular
.module('crmMailing', [
7 'crmUtil', 'crmAttachment', 'ngRoute', 'ui.utils', 'crmUi', 'dialogService'
8 ]); // TODO ngSanitize, unsavedChanges
10 // Time to wait before triggering AJAX update to recipients list
11 var RECIPIENTS_DEBOUNCE_MS
= 100;
12 var RECIPIENTS_PREVIEW_LIMIT
= 10000;
14 angular
.module('crmMailing').config([
16 function ($routeProvider
) {
17 $routeProvider
.when('/mailing', {
18 template
: '<div></div>',
19 controller
: 'ListMailingsCtrl'
21 $routeProvider
.when('/mailing/:id', {
22 templateUrl
: partialUrl('edit.html'),
23 controller
: 'EditMailingCtrl',
25 selectedMail
: function selectedMail($route
, crmMailingMgr
) {
26 return crmMailingMgr
.getOrCreate($route
.current
.params
.id
);
30 $routeProvider
.when('/mailing/:id/unified', {
31 templateUrl
: partialUrl('edit-unified.html'),
32 controller
: 'EditMailingCtrl',
34 selectedMail
: function selectedMail($route
, crmMailingMgr
) {
35 return crmMailingMgr
.getOrCreate($route
.current
.params
.id
);
39 $routeProvider
.when('/mailing/:id/unified2', {
40 templateUrl
: partialUrl('edit-unified2.html'),
41 controller
: 'EditMailingCtrl',
43 selectedMail
: function selectedMail($route
, crmMailingMgr
) {
44 return crmMailingMgr
.getOrCreate($route
.current
.params
.id
);
48 $routeProvider
.when('/mailing/:id/wizard', {
49 templateUrl
: partialUrl('edit-wizard.html'),
50 controller
: 'EditMailingCtrl',
52 selectedMail
: function selectedMail($route
, crmMailingMgr
) {
53 return crmMailingMgr
.getOrCreate($route
.current
.params
.id
);
60 angular
.module('crmMailing').controller('ListMailingsCtrl', function ListMailingsCtrl() {
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', {
69 angular
.module('crmMailing').controller('EditMailingCtrl', function EditMailingCtrl($scope
, selectedMail
, $location
, crmMailingMgr
, crmStatus
, CrmAttachments
, crmMailingPreviewMgr
) {
70 $scope
.mailing
= selectedMail
;
71 $scope
.attachments
= new CrmAttachments(function () {
72 return {entity_table
: 'civicrm_mailing', entity_id
: $scope
.mailing
.id
};
74 $scope
.attachments
.load();
75 $scope
.crmMailingConst
= CRM
.crmMailing
;
77 $scope
.partialUrl
= partialUrl
;
78 var ts
= $scope
.ts
= CRM
.ts('CiviMail');
81 $scope
.previewMailing
= function previewMailing(mailing
, mode
) {
82 return crmMailingPreviewMgr
.preview(mailing
, mode
);
86 $scope
.sendTest
= function sendTest(mailing
, attachments
, recipient
) {
87 var savePromise
= crmMailingMgr
.save(mailing
)
89 return attachments
.save();
91 return crmStatus({start
: ts('Saving...'), success
: ''}, savePromise
)
93 crmMailingPreviewMgr
.sendTest(mailing
, recipient
);
98 $scope
.submit
= function submit() {
99 var promise
= crmMailingMgr
.save($scope
.mailing
)
101 // pre-condition: the mailing exists *before* saving attachments to it
102 return $scope
.attachments
.save();
105 return crmMailingMgr
.submit($scope
.mailing
);
107 return crmStatus({start
: ts('Submitting...'), success
: ts('Submitted')}, promise
);
111 $scope
.save
= function save() {
112 return crmStatus(null,
114 .save($scope
.mailing
)
116 // pre-condition: the mailing exists *before* saving attachments to it
117 return $scope
.attachments
.save();
123 $scope
.delete = function cancel() {
124 return crmStatus({start
: ts('Deleting...'), success
: ts('Deleted')},
125 crmMailingMgr
.delete($scope
.mailing
)
129 $scope
.leave
= function leave() {
130 window
.location
= CRM
.url('civicrm/mailing/browse/unscheduled', {
136 // Transition URL "/mailing/new" => "/mailing/123" as soon as ID is known
137 $scope
.$watch('mailing.id', function (newValue
, oldValue
) {
138 if (newValue
&& newValue
!= oldValue
) {
139 var parts
= $location
.path().split('/'); // e.g. "/mailing/new" or "/mailing/123/wizard"
141 $location
.path(parts
.join('/'));
143 // FIXME: Angular unnecessarily refreshes UI
148 // Controller for the edit-recipients fields (
149 // WISHLIST: Move most of this to a (cache-enabled) service
151 // - [input] mailing: object
152 // - [output] recipients: array of recipient records
153 angular
.module('crmMailing').controller('EditRecipCtrl', function EditRecipCtrl($scope
, dialogService
, crmApi
, crmMailingMgr
) {
154 var ts
= $scope
.ts
= CRM
.ts('CiviMail');
155 $scope
.recipients
= null;
156 $scope
.getRecipientsEstimate = function () {
158 if ($scope
.recipients
== null) {
159 return ts('(Estimating)');
161 if ($scope
.recipients
.length
== 0) {
162 return ts('No recipients');
164 if ($scope
.recipients
.length
== 1) {
165 return ts('~1 recipient');
167 if (RECIPIENTS_PREVIEW_LIMIT
> 0 && $scope
.recipients
.length
>= RECIPIENTS_PREVIEW_LIMIT
) {
168 return ts('>%1 recipients', {1: RECIPIENTS_PREVIEW_LIMIT
});
170 return ts('~%1 recipients', {1: $scope
.recipients
.length
});
172 $scope
.getIncludesAsString = function () {
175 _
.each($scope
.mailing
.groups
.include
, function (id
) {
177 names
= names
+ ', ';
179 var group
= _
.where(CRM
.crmMailing
.groupNames
, {id
: '' + id
});
180 names
= names
+ group
[0].title
;
183 _
.each($scope
.mailing
.mailings
.include
, function (id
) {
185 names
= names
+ ', ';
187 var oldMailing
= _
.where(CRM
.crmMailing
.civiMails
, {id
: '' + id
});
188 names
= names
+ oldMailing
[0].name
;
193 $scope
.getExcludesAsString = function () {
196 _
.each($scope
.mailing
.groups
.exclude
, function (id
) {
198 names
= names
+ ', ';
200 var group
= _
.where(CRM
.crmMailing
.groupNames
, {id
: '' + id
});
201 names
= names
+ group
[0].title
;
204 _
.each($scope
.mailing
.mailings
.exclude
, function (id
) {
206 names
= names
+ ', ';
208 var oldMailing
= _
.where(CRM
.crmMailing
.civiMails
, {id
: '' + id
});
209 names
= names
+ oldMailing
[0].name
;
215 // We monitor four fields -- use debounce so that changes across the
216 // four fields can settle-down before AJAX.
217 var refreshRecipients
= _
.debounce(function () {
218 $scope
.$apply(function () {
219 $scope
.recipients
= null;
220 crmMailingMgr
.previewRecipients($scope
.mailing
, RECIPIENTS_PREVIEW_LIMIT
).then(function (recipients
) {
221 $scope
.recipients
= recipients
;
224 }, RECIPIENTS_DEBOUNCE_MS
);
225 $scope
.$watchCollection("mailing.groups.include", refreshRecipients
);
226 $scope
.$watchCollection("mailing.groups.exclude", refreshRecipients
);
227 $scope
.$watchCollection("mailing.mailings.include", refreshRecipients
);
228 $scope
.$watchCollection("mailing.mailings.exclude", refreshRecipients
);
230 $scope
.previewRecipients
= function previewRecipients() {
232 recipients
: $scope
.recipients
237 title
: ts('Preview (%1)', {
238 1: $scope
.getRecipientsEstimate()
241 dialogService
.open('recipDialog', partialUrl('dialog/recipients.html'), model
, options
);
245 // Controller for the "Preview Recipients" dialog
246 // Note: Expects $scope.model to be an object with properties:
247 // - recipients: array of contacts
248 angular
.module('crmMailing').controller('PreviewRecipCtrl', function ($scope
) {
249 $scope
.ts
= CRM
.ts('CiviMail');
252 // Controller for the "Preview Mailing" dialog
253 // Note: Expects $scope.model to be an object with properties:
257 angular
.module('crmMailing').controller('PreviewMailingDialogCtrl', function PreviewMailingDialogCtrl($scope
, crmMailingMgr
) {
258 $scope
.ts
= CRM
.ts('CiviMail');
261 // Controller for the "Preview Mailing Component" segment
262 // which displays header/footer/auto-responder
263 angular
.module('crmMailing').controller('PreviewComponentCtrl', function PreviewMailingDialogCtrl($scope
, dialogService
) {
264 var ts
= $scope
.ts
= CRM
.ts('CiviMail');
266 $scope
.previewComponent
= function previewComponent(title
, componentId
) {
267 var component
= _
.where(CRM
.crmMailing
.headerfooterList
, {id
: "" + componentId
});
268 if (!component
|| !component
[0]) {
269 CRM
.alert(ts('Invalid component ID (%1)', {
277 title
: title
// component[0].name
279 dialogService
.open('previewComponentDialog', partialUrl('dialog/previewComponent.html'), component
[0], options
);
283 // Controller for the "Preview Mailing" dialog
284 // Note: Expects $scope.model to be an object with properties:
289 angular
.module('crmMailing').controller('PreviewComponentDialogCtrl', function PreviewMailingDialogCtrl($scope
) {
290 $scope
.ts
= CRM
.ts('CiviMail');
293 // Controller for the in-place msg-template management
294 angular
.module('crmMailing').controller('MsgTemplateCtrl', function MsgTemplateCtrl($scope
, crmMsgTemplates
, dialogService
, $parse
) {
295 var ts
= $scope
.ts
= CRM
.ts('CiviMail');
296 $scope
.crmMsgTemplates
= crmMsgTemplates
;
298 // @return Promise MessageTemplate (per APIv3)
299 $scope
.saveTemplate
= function saveTemplate(mailing
) {
301 selected_id
: mailing
.msg_template_id
,
304 msg_subject
: mailing
.subject
,
305 msg_text
: mailing
.body_text
,
306 msg_html
: mailing
.body_html
312 title
: ts('Save Template')
314 return dialogService
.open('saveTemplateDialog', partialUrl('dialog/saveTemplate.html'), model
, options
)
315 .then(function (item
) {
316 mailing
.msg_template_id
= item
.id
;
323 $scope
.loadTemplate
= function loadTemplate(mailing
, id
) {
324 return crmMsgTemplates
.get(id
).then(function (tpl
) {
325 mailing
.subject
= tpl
.msg_subject
;
326 mailing
.body_text
= tpl
.msg_text
;
327 mailing
.body_html
= tpl
.msg_html
;
332 // Controller for the "Save Message Template" dialog
334 // - [input] "model": Object
335 // - "selected_id": int
337 // - "msg_subject": string
338 // - "msg_text": string
339 // - "msg_html": string
340 angular
.module('crmMailing').controller('SaveMsgTemplateDialogCtrl', function SaveMsgTemplateDialogCtrl($scope
, crmMsgTemplates
, dialogService
) {
341 var ts
= $scope
.ts
= CRM
.ts('CiviMail');
342 $scope
.saveOpt
= {mode
: '', newTitle
: ''};
343 $scope
.selected
= null;
345 $scope
.save
= function save() {
346 var tpl
= _
.extend({}, $scope
.model
.tpl
);
347 switch ($scope
.saveOpt
.mode
) {
349 tpl
.msg_title
= $scope
.saveOpt
.newTitle
;
352 tpl
.id
= $scope
.selected
.id
;
353 tpl
.msg_title
= $scope
.selected
.msg_title
;
356 throw 'SaveMsgTemplateDialogCtrl: Unrecognized mode: ' + $scope
.saveOpt
.mode
;
358 return crmMsgTemplates
.save(tpl
)
359 .then(function (item
) {
360 CRM
.status(ts('Saved'));
365 function scopeApply(f
) {
367 var args
= arguments
;
368 $scope
.$apply(function () {
375 crmMsgTemplates
.get($scope
.model
.selected_id
).then(
377 $scope
.saveOpt
.mode
= 'update';
378 $scope
.selected
= tpl
;
381 $scope
.saveOpt
.mode
= 'add';
382 $scope
.selected
= null;
385 // When using dialogService with a button bar, the major button actions
386 // need to be registered with the dialog widget (and not embedded in
387 // the body of the dialog).
389 buttons
[ts('Save')] = function () {
390 $scope
.save().then(function (item
) {
391 dialogService
.close('saveTemplateDialog', item
);
394 buttons
[ts('Cancel')] = function () {
395 dialogService
.cancel('saveTemplateDialog');
397 dialogService
.setButtons('saveTemplateDialog', buttons
);
400 setTimeout(scopeApply(init
), 0);
403 angular
.module('crmMailing').controller('EmailAddrCtrl', function EmailAddrCtrl($scope
, crmFromAddresses
) {
404 $scope
.crmFromAddresses
= crmFromAddresses
;
406 })(angular
, CRM
.$, CRM
._
);