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
);
43 attachments: function($route
, CrmAttachments
) {
44 var attachments
= new CrmAttachments(function () {
45 return {entity_table
: 'civicrm_mailing', entity_id
: $route
.current
.params
.id
};
47 return attachments
.load();
55 angular
.module('crmMailing').controller('ListMailingsCtrl', ['crmLegacy', 'crmNavigator', function ListMailingsCtrl(crmLegacy
, crmNavigator
) {
56 // We haven't implemented this in Angular, but some users may get clever
57 // about typing URLs, so we'll provide a redirect.
58 var new_url
= crmLegacy
.url('civicrm/mailing/browse/unscheduled', {reset
: 1, scheduled
: 'false'});
59 crmNavigator
.redirect(new_url
);
62 angular
.module('crmMailing').controller('CreateMailingCtrl', function EditMailingCtrl($scope
, selectedMail
, $location
) {
63 // Transition URL "/mailing/new/foo" => "/mailing/123/foo"
64 var parts
= $location
.path().split('/'); // e.g. "/mailing/new" or "/mailing/123/wizard"
65 parts
[2] = selectedMail
.id
;
66 $location
.path(parts
.join('/'));
70 angular
.module('crmMailing').controller('EditMailingCtrl', function EditMailingCtrl($scope
, selectedMail
, $location
, crmMailingMgr
, crmStatus
, attachments
, crmMailingPreviewMgr
, crmBlocker
) {
71 $scope
.mailing
= selectedMail
;
72 $scope
.attachments
= attachments
;
73 $scope
.crmMailingConst
= CRM
.crmMailing
;
75 var ts
= $scope
.ts
= CRM
.ts(null);
76 var block
= $scope
.block
= crmBlocker();
78 $scope
.isSubmitted
= function isSubmitted() {
79 return _
.size($scope
.mailing
.jobs
) > 0;
83 $scope
.previewMailing
= function previewMailing(mailing
, mode
) {
84 return crmMailingPreviewMgr
.preview(mailing
, mode
);
88 $scope
.sendTest
= function sendTest(mailing
, attachments
, recipient
) {
89 var savePromise
= crmMailingMgr
.save(mailing
)
91 return attachments
.save();
93 return block(crmStatus({start
: ts('Saving...'), success
: ''}, savePromise
)
95 crmMailingPreviewMgr
.sendTest(mailing
, recipient
);
100 $scope
.submit
= function submit() {
101 if (block
.check() || $scope
.crmMailing
.$invalid
) {
105 var promise
= crmMailingMgr
.save($scope
.mailing
)
107 // pre-condition: the mailing exists *before* saving attachments to it
108 return $scope
.attachments
.save();
111 return crmMailingMgr
.submit($scope
.mailing
);
114 $scope
.leave('scheduled');
117 return block(crmStatus({start
: ts('Submitting...'), success
: ts('Submitted')}, promise
));
121 $scope
.save
= function save() {
122 return block(crmStatus(null,
124 .save($scope
.mailing
)
126 // pre-condition: the mailing exists *before* saving attachments to it
127 return $scope
.attachments
.save();
133 $scope
.delete = function cancel() {
134 return block(crmStatus({start
: ts('Deleting...'), success
: ts('Deleted')},
135 crmMailingMgr
.delete($scope
.mailing
)
137 $scope
.leave('unscheduled');
142 // @param string listingScreen 'archive', 'scheduled', 'unscheduled'
143 $scope
.leave
= function leave(listingScreen
) {
144 switch (listingScreen
) {
146 window
.location
= CRM
.url('civicrm/mailing/browse/archived', {
151 window
.location
= CRM
.url('civicrm/mailing/browse/scheduled', {
159 window
.location
= CRM
.url('civicrm/mailing/browse/unscheduled', {
167 // Controller for the edit-recipients fields (
168 // WISHLIST: Move most of this to a (cache-enabled) service
170 // - [input] mailing: object
171 // - [output] recipients: array of recipient records
172 angular
.module('crmMailing').controller('EditRecipCtrl', function EditRecipCtrl($scope
, dialogService
, crmApi
, crmMailingMgr
) {
173 var ts
= $scope
.ts
= CRM
.ts(null);
174 $scope
.recipients
= null;
175 $scope
.getRecipientsEstimate = function () {
177 if ($scope
.recipients
=== null) {
178 return ts('(Estimating)');
180 if ($scope
.recipients
.length
=== 0) {
181 return ts('No recipients');
183 if ($scope
.recipients
.length
=== 1) {
184 return ts('~1 recipient');
186 if (RECIPIENTS_PREVIEW_LIMIT
> 0 && $scope
.recipients
.length
>= RECIPIENTS_PREVIEW_LIMIT
) {
187 return ts('>%1 recipients', {1: RECIPIENTS_PREVIEW_LIMIT
});
189 return ts('~%1 recipients', {1: $scope
.recipients
.length
});
191 $scope
.getIncludesAsString = function () {
194 _
.each($scope
.mailing
.groups
.include
, function (id
) {
196 names
= names
+ ', ';
198 var group
= _
.where(CRM
.crmMailing
.groupNames
, {id
: '' + id
});
199 names
= names
+ group
[0].title
;
202 _
.each($scope
.mailing
.mailings
.include
, function (id
) {
204 names
= names
+ ', ';
206 var oldMailing
= _
.where(CRM
.crmMailing
.civiMails
, {id
: '' + id
});
207 names
= names
+ oldMailing
[0].name
;
212 $scope
.getExcludesAsString = function () {
215 _
.each($scope
.mailing
.groups
.exclude
, function (id
) {
217 names
= names
+ ', ';
219 var group
= _
.where(CRM
.crmMailing
.groupNames
, {id
: '' + id
});
220 names
= names
+ group
[0].title
;
223 _
.each($scope
.mailing
.mailings
.exclude
, function (id
) {
225 names
= names
+ ', ';
227 var oldMailing
= _
.where(CRM
.crmMailing
.civiMails
, {id
: '' + id
});
228 names
= names
+ oldMailing
[0].name
;
234 // We monitor four fields -- use debounce so that changes across the
235 // four fields can settle-down before AJAX.
236 var refreshRecipients
= _
.debounce(function () {
237 $scope
.$apply(function () {
238 $scope
.recipients
= null;
239 crmMailingMgr
.previewRecipients($scope
.mailing
, RECIPIENTS_PREVIEW_LIMIT
).then(function (recipients
) {
240 $scope
.recipients
= recipients
;
243 }, RECIPIENTS_DEBOUNCE_MS
);
244 $scope
.$watchCollection("mailing.groups.include", refreshRecipients
);
245 $scope
.$watchCollection("mailing.groups.exclude", refreshRecipients
);
246 $scope
.$watchCollection("mailing.mailings.include", refreshRecipients
);
247 $scope
.$watchCollection("mailing.mailings.exclude", refreshRecipients
);
249 $scope
.previewRecipients
= function previewRecipients() {
251 recipients
: $scope
.recipients
256 title
: ts('Preview (%1)', {
257 1: $scope
.getRecipientsEstimate()
260 dialogService
.open('recipDialog', '~/crmMailing/dialog/recipients.html', model
, options
);
264 // Controller for the "Preview Recipients" dialog
265 // Note: Expects $scope.model to be an object with properties:
266 // - recipients: array of contacts
267 angular
.module('crmMailing').controller('PreviewRecipCtrl', function ($scope
) {
268 $scope
.ts
= CRM
.ts(null);
271 // Controller for the "Preview Mailing" dialog
272 // Note: Expects $scope.model to be an object with properties:
276 angular
.module('crmMailing').controller('PreviewMailingDialogCtrl', function PreviewMailingDialogCtrl($scope
) {
277 $scope
.ts
= CRM
.ts(null);
280 // Controller for the "Preview Mailing Component" segment
281 // which displays header/footer/auto-responder
282 angular
.module('crmMailing').controller('PreviewComponentCtrl', function PreviewComponentCtrl($scope
, dialogService
) {
283 var ts
= $scope
.ts
= CRM
.ts(null);
285 $scope
.previewComponent
= function previewComponent(title
, componentId
) {
286 var component
= _
.where(CRM
.crmMailing
.headerfooterList
, {id
: "" + componentId
});
287 if (!component
|| !component
[0]) {
288 CRM
.alert(ts('Invalid component ID (%1)', {
296 title
: title
// component[0].name
298 dialogService
.open('previewComponentDialog', '~/crmMailing/dialog/previewComponent.html', component
[0], options
);
302 // Controller for the "Preview Mailing Component" dialog
303 // Note: Expects $scope.model to be an object with properties:
308 angular
.module('crmMailing').controller('PreviewComponentDialogCtrl', function PreviewComponentDialogCtrl($scope
) {
309 $scope
.ts
= CRM
.ts(null);
312 // Controller for the in-place msg-template management
313 angular
.module('crmMailing').controller('MsgTemplateCtrl', function MsgTemplateCtrl($scope
, crmMsgTemplates
, dialogService
) {
314 var ts
= $scope
.ts
= CRM
.ts(null);
315 $scope
.crmMsgTemplates
= crmMsgTemplates
;
317 // @return Promise MessageTemplate (per APIv3)
318 $scope
.saveTemplate
= function saveTemplate(mailing
) {
320 selected_id
: mailing
.msg_template_id
,
323 msg_subject
: mailing
.subject
,
324 msg_text
: mailing
.body_text
,
325 msg_html
: mailing
.body_html
331 title
: ts('Save Template')
333 return dialogService
.open('saveTemplateDialog', '~/crmMailing/dialog/saveTemplate.html', model
, options
)
334 .then(function (item
) {
335 mailing
.msg_template_id
= item
.id
;
342 $scope
.loadTemplate
= function loadTemplate(mailing
, id
) {
343 return crmMsgTemplates
.get(id
).then(function (tpl
) {
344 mailing
.subject
= tpl
.msg_subject
;
345 mailing
.body_text
= tpl
.msg_text
;
346 mailing
.body_html
= tpl
.msg_html
;
351 // Controller for the "Save Message Template" dialog
353 // - [input] "model": Object
354 // - "selected_id": int
356 // - "msg_subject": string
357 // - "msg_text": string
358 // - "msg_html": string
359 angular
.module('crmMailing').controller('SaveMsgTemplateDialogCtrl', function SaveMsgTemplateDialogCtrl($scope
, crmMsgTemplates
, dialogService
) {
360 var ts
= $scope
.ts
= CRM
.ts(null);
361 $scope
.saveOpt
= {mode
: '', newTitle
: ''};
362 $scope
.selected
= null;
364 $scope
.save
= function save() {
365 var tpl
= _
.extend({}, $scope
.model
.tpl
);
366 switch ($scope
.saveOpt
.mode
) {
368 tpl
.msg_title
= $scope
.saveOpt
.newTitle
;
371 tpl
.id
= $scope
.selected
.id
;
372 tpl
.msg_title
= $scope
.selected
.msg_title
;
375 throw 'SaveMsgTemplateDialogCtrl: Unrecognized mode: ' + $scope
.saveOpt
.mode
;
377 return crmMsgTemplates
.save(tpl
)
378 .then(function (item
) {
379 CRM
.status(ts('Saved'));
384 function scopeApply(f
) {
386 var args
= arguments
;
387 $scope
.$apply(function () {
394 crmMsgTemplates
.get($scope
.model
.selected_id
).then(
396 $scope
.saveOpt
.mode
= 'update';
397 $scope
.selected
= tpl
;
400 $scope
.saveOpt
.mode
= 'add';
401 $scope
.selected
= null;
404 // When using dialogService with a button bar, the major button actions
405 // need to be registered with the dialog widget (and not embedded in
406 // the body of the dialog).
410 icons
: {primary
: 'ui-icon-check'},
412 $scope
.save().then(function (item
) {
413 dialogService
.close('saveTemplateDialog', item
);
419 icons
: {primary
: 'ui-icon-close'},
421 dialogService
.cancel('saveTemplateDialog');
425 dialogService
.setButtons('saveTemplateDialog', buttons
);
428 setTimeout(scopeApply(init
), 0);
431 angular
.module('crmMailing').controller('EmailAddrCtrl', function EmailAddrCtrl($scope
, crmFromAddresses
) {
432 $scope
.crmFromAddresses
= crmFromAddresses
;
434 })(angular
, CRM
.$, CRM
._
);