X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=js%2Fangular-crmMailing.js;h=09fa277fb924b7bdfde86f61a87294e0de7fed2e;hb=720c658b42e4fe51729e272f2f76ea5ce833a538;hp=dfeffb355597ee21ef24b6376b9901a889611b19;hpb=8acf01236e62050c6d6b226eba6b71911bb2dee1;p=civicrm-core.git diff --git a/js/angular-crmMailing.js b/js/angular-crmMailing.js index dfeffb3555..09fa277fb9 100644 --- a/js/angular-crmMailing.js +++ b/js/angular-crmMailing.js @@ -1,10 +1,7 @@ (function (angular, $, _) { - var partialUrl = function partialUrl(relPath) { - return CRM.resourceUrls.civicrm + '/partials/crmMailing/' + relPath; - }; angular.module('crmMailing', [ - 'crmUtil', 'crmAttachment', 'ngRoute', 'ui.utils', 'crmUi', 'dialogService' + 'crmUtil', 'crmAttachment', 'crmAutosave', 'ngRoute', 'ui.utils', 'crmUi', 'dialogService' ]); // TODO ngSanitize, unsavedChanges // Time to wait before triggering AJAX update to recipients list @@ -18,41 +15,39 @@ template: '
', controller: 'ListMailingsCtrl' }); - $routeProvider.when('/mailing/:id', { - templateUrl: partialUrl('edit.html'), - controller: 'EditMailingCtrl', - resolve: { - selectedMail: function selectedMail($route, crmMailingMgr) { - return crmMailingMgr.getOrCreate($route.current.params.id); - } - } - }); - $routeProvider.when('/mailing/:id/unified', { - templateUrl: partialUrl('edit-unified.html'), - controller: 'EditMailingCtrl', - resolve: { - selectedMail: function selectedMail($route, crmMailingMgr) { - return crmMailingMgr.getOrCreate($route.current.params.id); - } - } - }); - $routeProvider.when('/mailing/:id/unified2', { - templateUrl: partialUrl('edit-unified2.html'), - controller: 'EditMailingCtrl', - resolve: { - selectedMail: function selectedMail($route, crmMailingMgr) { - return crmMailingMgr.getOrCreate($route.current.params.id); + + var editorPaths = { + '': '~/crmMailing/edit.html', + '/unified': '~/crmMailing/edit-unified.html', + '/unified2': '~/crmMailing/edit-unified2.html', + '/wizard': '~/crmMailing/edit-wizard.html' + }; + angular.forEach(editorPaths, function(editTemplate, pathSuffix) { + $routeProvider.when('/mailing/new' + pathSuffix, { + template: '

' + ts('Initializing...') + '

', + controller: 'CreateMailingCtrl', + resolve: { + selectedMail: function(crmMailingMgr) { + var m = crmMailingMgr.create(); + return crmMailingMgr.save(m); + } } - } - }); - $routeProvider.when('/mailing/:id/wizard', { - templateUrl: partialUrl('edit-wizard.html'), - controller: 'EditMailingCtrl', - resolve: { - selectedMail: function selectedMail($route, crmMailingMgr) { - return crmMailingMgr.getOrCreate($route.current.params.id); + }); + $routeProvider.when('/mailing/:id' + pathSuffix, { + templateUrl: editTemplate, + controller: 'EditMailingCtrl', + resolve: { + selectedMail: function($route, crmMailingMgr) { + return crmMailingMgr.get($route.current.params.id); + }, + attachments: function($route, CrmAttachments) { + var attachments = new CrmAttachments(function () { + return {entity_table: 'civicrm_mailing', entity_id: $route.current.params.id}; + }); + return attachments.load(); + } } - } + }); }); } ]); @@ -64,16 +59,21 @@ crmNavigator.redirect(new_url); }]); - angular.module('crmMailing').controller('EditMailingCtrl', function EditMailingCtrl($scope, selectedMail, $location, crmMailingMgr, crmStatus, CrmAttachments, crmMailingPreviewMgr) { + angular.module('crmMailing').controller('CreateMailingCtrl', function EditMailingCtrl($scope, selectedMail, $location) { + // Transition URL "/mailing/new/foo" => "/mailing/123/foo" + var parts = $location.path().split('/'); // e.g. "/mailing/new" or "/mailing/123/wizard" + parts[2] = selectedMail.id; + $location.path(parts.join('/')); + $location.replace(); + }); + + angular.module('crmMailing').controller('EditMailingCtrl', function EditMailingCtrl($scope, selectedMail, $location, crmMailingMgr, crmStatus, attachments, crmMailingPreviewMgr, crmBlocker) { $scope.mailing = selectedMail; - $scope.attachments = new CrmAttachments(function () { - return {entity_table: 'civicrm_mailing', entity_id: $scope.mailing.id}; - }); - $scope.attachments.load(); + $scope.attachments = attachments; $scope.crmMailingConst = CRM.crmMailing; - $scope.partialUrl = partialUrl; - var ts = $scope.ts = CRM.ts('CiviMail'); + var ts = $scope.ts = CRM.ts(null); + var block = $scope.block = crmBlocker(); $scope.isSubmitted = function isSubmitted() { return _.size($scope.mailing.jobs) > 0; @@ -89,16 +89,19 @@ var savePromise = crmMailingMgr.save(mailing) .then(function () { return attachments.save(); - }) - .then(updateUrl); - return crmStatus({start: ts('Saving...'), success: ''}, savePromise) + }); + return block(crmStatus({start: ts('Saving...'), success: ''}, savePromise) .then(function () { crmMailingPreviewMgr.sendTest(mailing, recipient); - }); + })); }; // @return Promise $scope.submit = function submit() { + if (block.check() || $scope.crmMailing.$invalid) { + return; + } + var promise = crmMailingMgr.save($scope.mailing) .then(function () { // pre-condition: the mailing exists *before* saving attachments to it @@ -108,37 +111,36 @@ return crmMailingMgr.submit($scope.mailing); }) .then(function () { - leave('scheduled'); + $scope.leave('scheduled'); }) ; - return crmStatus({start: ts('Submitting...'), success: ts('Submitted')}, promise); + return block(crmStatus({start: ts('Submitting...'), success: ts('Submitted')}, promise)); }; // @return Promise $scope.save = function save() { - return crmStatus(null, + return block(crmStatus(null, crmMailingMgr .save($scope.mailing) .then(function () { // pre-condition: the mailing exists *before* saving attachments to it return $scope.attachments.save(); }) - .then(updateUrl) - ); + )); }; // @return Promise $scope.delete = function cancel() { - return crmStatus({start: ts('Deleting...'), success: ts('Deleted')}, + return block(crmStatus({start: ts('Deleting...'), success: ts('Deleted')}, crmMailingMgr.delete($scope.mailing) .then(function () { - leave('unscheduled'); + $scope.leave('unscheduled'); }) - ); + )); }; // @param string listingScreen 'archive', 'scheduled', 'unscheduled' - function leave(listingScreen) { + $scope.leave = function leave(listingScreen) { switch (listingScreen) { case 'archive': window.location = CRM.url('civicrm/mailing/browse/archived', { @@ -159,20 +161,7 @@ scheduled: 'false' }); } - } - - // Transition URL "/mailing/new" => "/mailing/123" - function updateUrl() { - var parts = $location.path().split('/'); // e.g. "/mailing/new" or "/mailing/123/wizard" - if (parts[2] != $scope.mailing.id) { - parts[2] = $scope.mailing.id; - $location.path(parts.join('/')); - $location.replace(); - // FIXME: Angular unnecessarily refreshes UI - // WARNING: Changing the URL triggers a full reload. Any pending AJAX operations - // could be inconsistently applied. Run updateUrl() after other changes complete. - } - } + }; }); // Controller for the edit-recipients fields ( @@ -180,8 +169,8 @@ // 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('CiviMail'); + angular.module('crmMailing').controller('EditRecipCtrl', function EditRecipCtrl($scope, dialogService, crmApi, crmMailingMgr, $q, crmMetadata) { + var ts = $scope.ts = CRM.ts(null); $scope.recipients = null; $scope.getRecipientsEstimate = function () { var ts = $scope.ts; @@ -202,7 +191,7 @@ $scope.getIncludesAsString = function () { var first = true; var names = ''; - _.each($scope.mailing.groups.include, function (id) { + _.each($scope.mailing.recipients.groups.include, function (id) { if (!first) { names = names + ', '; } @@ -210,7 +199,7 @@ names = names + group[0].title; first = false; }); - _.each($scope.mailing.mailings.include, function (id) { + _.each($scope.mailing.recipients.mailings.include, function (id) { if (!first) { names = names + ', '; } @@ -223,7 +212,7 @@ $scope.getExcludesAsString = function () { var first = true; var names = ''; - _.each($scope.mailing.groups.exclude, function (id) { + _.each($scope.mailing.recipients.groups.exclude, function (id) { if (!first) { names = names + ', '; } @@ -231,7 +220,7 @@ names = names + group[0].title; first = false; }); - _.each($scope.mailing.mailings.exclude, function (id) { + _.each($scope.mailing.recipients.mailings.exclude, function (id) { if (!first) { names = names + ', '; } @@ -252,23 +241,37 @@ }); }); }, 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', partialUrl('dialog/recipients.html'), model, options); + }); + 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); + }); }; }); @@ -276,7 +279,7 @@ // Note: Expects $scope.model to be an object with properties: // - recipients: array of contacts angular.module('crmMailing').controller('PreviewRecipCtrl', function ($scope) { - $scope.ts = CRM.ts('CiviMail'); + $scope.ts = CRM.ts(null); }); // Controller for the "Preview Mailing" dialog @@ -285,13 +288,21 @@ // - "body_html" // - "body_text" angular.module('crmMailing').controller('PreviewMailingDialogCtrl', function PreviewMailingDialogCtrl($scope) { - $scope.ts = CRM.ts('CiviMail'); + $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 PreviewMailingDialogCtrl($scope, dialogService) { - var ts = $scope.ts = CRM.ts('CiviMail'); + angular.module('crmMailing').controller('PreviewComponentCtrl', function PreviewComponentCtrl($scope, dialogService) { + var ts = $scope.ts = CRM.ts(null); $scope.previewComponent = function previewComponent(title, componentId) { var component = _.where(CRM.crmMailing.headerfooterList, {id: "" + componentId}); @@ -301,28 +312,27 @@ })); return; } - var options = { + var options = CRM.utils.adjustDialogDefaults({ autoOpen: false, - modal: true, title: title // component[0].name - }; - dialogService.open('previewComponentDialog', partialUrl('dialog/previewComponent.html'), component[0], options); + }); + dialogService.open('previewComponentDialog', '~/crmMailing/dialog/previewComponent.html', component[0], options); }; }); - // Controller for the "Preview Mailing" dialog + // Controller for the "Preview Mailing Component" dialog // Note: Expects $scope.model to be an object with properties: // - "name" // - "subject" // - "body_html" // - "body_text" - angular.module('crmMailing').controller('PreviewComponentDialogCtrl', function PreviewMailingDialogCtrl($scope) { - $scope.ts = CRM.ts('CiviMail'); + angular.module('crmMailing').controller('PreviewComponentDialogCtrl', function PreviewComponentDialogCtrl($scope) { + $scope.ts = CRM.ts(null); }); // Controller for the in-place msg-template management angular.module('crmMailing').controller('MsgTemplateCtrl', function MsgTemplateCtrl($scope, crmMsgTemplates, dialogService) { - var ts = $scope.ts = CRM.ts('CiviMail'); + var ts = $scope.ts = CRM.ts(null); $scope.crmMsgTemplates = crmMsgTemplates; // @return Promise MessageTemplate (per APIv3) @@ -336,12 +346,11 @@ msg_html: mailing.body_html } }; - var options = { + var options = CRM.utils.adjustDialogDefaults({ autoOpen: false, - modal: true, title: ts('Save Template') - }; - return dialogService.open('saveTemplateDialog', partialUrl('dialog/saveTemplate.html'), model, options) + }); + return dialogService.open('saveTemplateDialog', '~/crmMailing/dialog/saveTemplate.html', model, options) .then(function (item) { mailing.msg_template_id = item.id; return item; @@ -368,7 +377,7 @@ // - "msg_text": string // - "msg_html": string angular.module('crmMailing').controller('SaveMsgTemplateDialogCtrl', function SaveMsgTemplateDialogCtrl($scope, crmMsgTemplates, dialogService) { - var ts = $scope.ts = CRM.ts('CiviMail'); + var ts = $scope.ts = CRM.ts(null); $scope.saveOpt = {mode: '', newTitle: ''}; $scope.selected = null; @@ -415,22 +424,116 @@ // When using dialogService with a button bar, the major button actions // need to be registered with the dialog widget (and not embedded in // the body of the dialog). - var buttons = {}; - buttons[ts('Save')] = function () { - $scope.save().then(function (item) { - dialogService.close('saveTemplateDialog', item); - }); - }; - buttons[ts('Cancel')] = function () { - dialogService.cancel('saveTemplateDialog'); - }; + var buttons = [ + { + text: ts('Save'), + icons: {primary: 'ui-icon-check'}, + click: function () { + $scope.save().then(function (item) { + dialogService.close('saveTemplateDialog', item); + }); + } + }, + { + text: ts('Cancel'), + icons: {primary: 'ui-icon-close'}, + click: function () { + dialogService.cancel('saveTemplateDialog'); + } + } + ]; dialogService.setButtons('saveTemplateDialog', buttons); } 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._);