angular.module('crmMailing', [
'crmUtil', 'crmAttachment', 'crmAutosave', 'ngRoute', 'ui.utils', 'crmUi', 'dialogService'
- ]); // TODO ngSanitize, unsavedChanges
+ ]);
// Time to wait before triggering AJAX update to recipients list
var RECIPIENTS_DEBOUNCE_MS = 100;
var RECIPIENTS_PREVIEW_LIMIT = 10000;
+ var APPROVAL_STATUSES = {'Approved': 1, 'Rejected': 2, 'None': 3};
+
angular.module('crmMailing').config([
'$routeProvider',
function ($routeProvider) {
'/wizard': '~/crmMailing/edit-wizard.html'
};
angular.forEach(editorPaths, function(editTemplate, pathSuffix) {
+ if (CRM && CRM.crmMailing && CRM.crmMailing.workflowEnabled) {
+ editTemplate = '~/crmMailing/edit-workflow.html'; // override
+ }
$routeProvider.when('/mailing/new' + pathSuffix, {
template: '<p>' + ts('Initializing...') + '</p>',
controller: 'CreateMailingCtrl',
$location.replace();
});
- angular.module('crmMailing').controller('EditMailingCtrl', function EditMailingCtrl($scope, selectedMail, $location, crmMailingMgr, crmStatus, attachments, crmMailingPreviewMgr, crmBlocker) {
+ angular.module('crmMailing').controller('EditMailingCtrl', function EditMailingCtrl($scope, selectedMail, $location, crmMailingMgr, crmStatus, attachments, crmMailingPreviewMgr, crmBlocker, CrmAutosaveCtrl, $timeout) {
$scope.mailing = selectedMail;
$scope.attachments = attachments;
$scope.crmMailingConst = CRM.crmMailing;
+ $scope.checkPerm = CRM.checkPerm;
var ts = $scope.ts = CRM.ts(null);
var block = $scope.block = crmBlocker();
+ var myAutosave = null;
$scope.isSubmitted = function isSubmitted() {
return _.size($scope.mailing.jobs) > 0;
};
+ // usage: approve('Approved')
+ $scope.approve = function approve(status, options) {
+ $scope.mailing.approval_status_id = APPROVAL_STATUSES[status];
+ return myAutosave.suspend($scope.submit(options));
+ };
+
// @return Promise
$scope.previewMailing = function previewMailing(mailing, mode) {
return crmMailingPreviewMgr.preview(mailing, mode);
};
// @return Promise
- $scope.submit = function submit() {
+ $scope.submit = function submit(options) {
+ options = options || {};
if (block.check() || $scope.crmMailing.$invalid) {
return;
}
return crmMailingMgr.submit($scope.mailing);
})
.then(function () {
- $scope.leave('scheduled');
+ if (!options.stay) {
+ $scope.leave('scheduled');
+ }
})
;
return block(crmStatus({start: ts('Submitting...'), success: ts('Submitted')}, promise));
});
}
};
- });
- // Controller for the edit-recipients fields (
- // WISHLIST: Move most of this to a (cache-enabled) service
- // Scope members:
- // - [input] mailing: object
- // - [output] recipients: array of recipient records
- angular.module('crmMailing').controller('EditRecipCtrl', function EditRecipCtrl($scope, dialogService, crmApi, crmMailingMgr) {
- var ts = $scope.ts = CRM.ts(null);
- $scope.recipients = null;
- $scope.getRecipientsEstimate = function () {
- var ts = $scope.ts;
- if ($scope.recipients === null) {
- return ts('(Estimating)');
- }
- if ($scope.recipients.length === 0) {
- return ts('No recipients');
- }
- if ($scope.recipients.length === 1) {
- return ts('~1 recipient');
- }
- if (RECIPIENTS_PREVIEW_LIMIT > 0 && $scope.recipients.length >= RECIPIENTS_PREVIEW_LIMIT) {
- return ts('>%1 recipients', {1: RECIPIENTS_PREVIEW_LIMIT});
+ myAutosave = new CrmAutosaveCtrl({
+ save: $scope.save,
+ saveIf: function() {
+ return true;
+ },
+ model: function() {
+ return [$scope.mailing, $scope.attachments.getAutosaveSignature()];
+ },
+ form: function() {
+ return $scope.crmMailing;
}
- return ts('~%1 recipients', {1: $scope.recipients.length});
- };
- $scope.getIncludesAsString = function () {
+ });
+ $timeout(myAutosave.start);
+ $scope.$on('$destroy', myAutosave.stop);
+ });
+
+ angular.module('crmMailing').controller('ViewRecipCtrl', function EditRecipCtrl($scope) {
+ $scope.getIncludesAsString = function(mailing) {
var first = true;
var names = '';
- _.each($scope.mailing.groups.include, function (id) {
+ _.each(mailing.recipients.groups.include, function (id) {
if (!first) {
names = names + ', ';
}
names = names + group[0].title;
first = false;
});
- _.each($scope.mailing.mailings.include, function (id) {
+ _.each(mailing.recipients.mailings.include, function (id) {
if (!first) {
names = names + ', ';
}
});
return names;
};
- $scope.getExcludesAsString = function () {
+ $scope.getExcludesAsString = function (mailing) {
var first = true;
var names = '';
- _.each($scope.mailing.groups.exclude, function (id) {
+ _.each(mailing.recipients.groups.exclude, function (id) {
if (!first) {
names = names + ', ';
}
names = names + group[0].title;
first = false;
});
- _.each($scope.mailing.mailings.exclude, function (id) {
+ _.each(mailing.recipients.mailings.exclude, function (id) {
if (!first) {
names = names + ', ';
}
});
return names;
};
+ });
+
+ // Controller for the edit-recipients fields (
+ // WISHLIST: Move most of this to a (cache-enabled) service
+ // Scope members:
+ // - [input] mailing: object
+ // - [output] recipients: array of recipient records
+ angular.module('crmMailing').controller('EditRecipCtrl', function EditRecipCtrl($scope, dialogService, crmApi, crmMailingMgr, $q, crmMetadata) {
+ var ts = $scope.ts = CRM.ts(null);
+
+ $scope.isMailingList = function isMailingList(group) {
+ var GROUP_TYPE_MAILING_LIST = '2';
+ return _.contains(group.group_type, GROUP_TYPE_MAILING_LIST);
+ };
+
+ $scope.recipients = null;
+ $scope.getRecipientsEstimate = function () {
+ var ts = $scope.ts;
+ if ($scope.recipients === null) {
+ return ts('(Estimating)');
+ }
+ if ($scope.recipients.length === 0) {
+ return ts('No recipients');
+ }
+ if ($scope.recipients.length === 1) {
+ return ts('~1 recipient');
+ }
+ if (RECIPIENTS_PREVIEW_LIMIT > 0 && $scope.recipients.length >= RECIPIENTS_PREVIEW_LIMIT) {
+ return ts('>%1 recipients', {1: RECIPIENTS_PREVIEW_LIMIT});
+ }
+ return ts('~%1 recipients', {1: $scope.recipients.length});
+ };
// We monitor four fields -- use debounce so that changes across the
// four fields can settle-down before AJAX.
var refreshRecipients = _.debounce(function () {
$scope.$apply(function () {
$scope.recipients = null;
+ if (!$scope.mailing) return;
crmMailingMgr.previewRecipients($scope.mailing, RECIPIENTS_PREVIEW_LIMIT).then(function (recipients) {
$scope.recipients = recipients;
});
});
}, RECIPIENTS_DEBOUNCE_MS);
- $scope.$watchCollection("mailing.groups.include", refreshRecipients);
- $scope.$watchCollection("mailing.groups.exclude", refreshRecipients);
- $scope.$watchCollection("mailing.mailings.include", refreshRecipients);
- $scope.$watchCollection("mailing.mailings.exclude", refreshRecipients);
+ $scope.$watchCollection("mailing.recipients.groups.include", refreshRecipients);
+ $scope.$watchCollection("mailing.recipients.groups.exclude", refreshRecipients);
+ $scope.$watchCollection("mailing.recipients.mailings.include", refreshRecipients);
+ $scope.$watchCollection("mailing.recipients.mailings.exclude", refreshRecipients);
$scope.previewRecipients = function previewRecipients() {
var model = {
recipients: $scope.recipients
};
- var options = {
+ var options = CRM.utils.adjustDialogDefaults({
autoOpen: false,
- modal: true,
title: ts('Preview (%1)', {
1: $scope.getRecipientsEstimate()
})
- };
+ });
dialogService.open('recipDialog', '~/crmMailing/dialog/recipients.html', model, options);
};
+
+ // Open a dialog for editing the advanced recipient options.
+ $scope.editOptions = function editOptions(mailing) {
+ var options = CRM.utils.adjustDialogDefaults({
+ autoOpen: false,
+ title: ts('Edit Options')
+ });
+ $q.when(crmMetadata.getFields('Mailing')).then(function(fields) {
+ var model = {
+ fields: fields,
+ mailing: mailing
+ };
+ dialogService.open('previewComponentDialog', '~/crmMailing/dialog/recipientOptions.html', model, options);
+ });
+ };
});
// Controller for the "Preview Recipients" dialog
$scope.ts = CRM.ts(null);
});
+ // Controller for the "Recipients: Edit Options" dialog
+ // Note: Expects $scope.model to be an object with properties:
+ // - "mailing" (APIv3 mailing object)
+ // - "fields" (list of fields)
+ angular.module('crmMailing').controller('EditRecipOptionsDialogCtrl', function EditRecipOptionsDialogCtrl($scope) {
+ $scope.ts = CRM.ts(null);
+ });
+
// Controller for the "Preview Mailing Component" segment
// which displays header/footer/auto-responder
angular.module('crmMailing').controller('PreviewComponentCtrl', function PreviewComponentCtrl($scope, dialogService) {
}));
return;
}
- var options = {
+ var options = CRM.utils.adjustDialogDefaults({
autoOpen: false,
- modal: true,
title: title // component[0].name
- };
+ });
dialogService.open('previewComponentDialog', '~/crmMailing/dialog/previewComponent.html', component[0], options);
};
});
msg_html: mailing.body_html
}
};
- var options = {
+ var options = CRM.utils.adjustDialogDefaults({
autoOpen: false,
- modal: true,
title: ts('Save Template')
- };
+ });
return dialogService.open('saveTemplateDialog', '~/crmMailing/dialog/saveTemplate.html', model, options)
.then(function (item) {
mailing.msg_template_id = item.id;
setTimeout(scopeApply(init), 0);
});
- angular.module('crmMailing').controller('EmailAddrCtrl', function EmailAddrCtrl($scope, crmFromAddresses) {
+ angular.module('crmMailing').controller('EmailAddrCtrl', function EmailAddrCtrl($scope, crmFromAddresses, crmUiAlert) {
+ var ts = CRM.ts(null);
+ function changeAlert(winnerField, loserField) {
+ crmUiAlert({
+ title: ts('Conflict'),
+ text: ts('The "%1" option conflicts with the "%2" option. The "%2" option has been disabled.', {
+ 1: winnerField,
+ 2: loserField
+ })
+ });
+ }
+
$scope.crmFromAddresses = crmFromAddresses;
+ $scope.checkReplyToChange = function checkReplyToChange(mailing) {
+ if (!_.isEmpty(mailing.replyto_email) && mailing.override_verp == '0') {
+ mailing.override_verp = '1';
+ changeAlert(ts('Reply-To'), ts('Track Replies'));
+ }
+ };
+ $scope.checkVerpChange = function checkVerpChange(mailing) {
+ if (!_.isEmpty(mailing.replyto_email) && mailing.override_verp == '0') {
+ mailing.replyto_email = '';
+ changeAlert(ts('Track Replies'), ts('Reply-To'));
+ }
+ };
+ });
+
+ var lastEmailTokenAlert = null;
+ angular.module('crmMailing').controller('EmailBodyCtrl', function EmailBodyCtrl($scope, crmMailingMgr, crmUiAlert, $timeout) {
+ var ts = CRM.ts(null);
+
+ // ex: if (!hasAllTokens(myMailing, 'body_text)) alert('Oh noes!');
+ $scope.hasAllTokens = function hasAllTokens(mailing, field) {
+ return _.isEmpty(crmMailingMgr.findMissingTokens(mailing, field));
+ };
+
+ // ex: checkTokens(myMailing, 'body_text', 'insert:body_text')
+ // ex: checkTokens(myMailing, '*')
+ $scope.checkTokens = function checkTokens(mailing, field, insertEvent) {
+ if (lastEmailTokenAlert) {
+ lastEmailTokenAlert.close();
+ }
+ var missing, insertable;
+ if (field == '*') {
+ insertable = false;
+ missing = angular.extend({},
+ crmMailingMgr.findMissingTokens(mailing, 'body_html'),
+ crmMailingMgr.findMissingTokens(mailing, 'body_text')
+ );
+ } else {
+ insertable = !_.isEmpty(insertEvent);
+ missing = crmMailingMgr.findMissingTokens(mailing, field);
+ }
+ if (!_.isEmpty(missing)) {
+ lastEmailTokenAlert = crmUiAlert({
+ type: 'error',
+ title: ts('Required tokens'),
+ templateUrl: '~/crmMailing/dialog/tokenAlert.html',
+ scope: angular.extend($scope.$new(), {
+ insertable: insertable,
+ insertToken: function(token) {
+ $timeout(function(){
+ $scope.$broadcast(insertEvent, '{' + token + '}');
+ $timeout(function(){
+ checkTokens(mailing, field, insertEvent);
+ });
+ });
+ },
+ missing: missing
+ })
+ });
+ }
+ };
+ });
+
+ angular.module('crmMailing').controller('EditUnsubGroupCtrl', function EditUnsubGroupCtrl($scope) {
+ // CRM.crmMailing.groupNames is a global constant - since it doesn't change, we can digest & cache.
+ var mandatoryIds = [];
+ _.each(CRM.crmMailing.groupNames, function(grp){
+ if (grp.is_hidden == "1") {
+ mandatoryIds.push(parseInt(grp.id));
+ }
+ });
+
+ $scope.isUnsubGroupRequired = function isUnsubGroupRequired(mailing) {
+ return _.intersection(mandatoryIds, mailing.recipients.groups.include).length > 0;
+ };
});
})(angular, CRM.$, CRM._);