CRM-16119: Fix string typo.
[civicrm-core.git] / js / angular-crmMailing.js
index 889f8af88a8e8d12d64f3170d73fc7ec4291cda5..256cda54e88f641b2466e9b74288066ae7a35065 100644 (file)
@@ -2,12 +2,14 @@
 
   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) {
@@ -23,6 +25,9 @@
         '/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._);