Merge pull request #5457 from colemanw/CRM-15705
[civicrm-core.git] / js / angular-crmMailingAB.js
index 77262db65f560f90f802640a0abe451b5ef74447..cc23dad906fe8064847f24536f4d82a6bcd4ca01 100644 (file)
@@ -1,6 +1,6 @@
 (function (angular, $, _) {
 
-  angular.module('crmMailingAB', ['ngRoute', 'ui.utils', 'ngSanitize', 'crmUi', 'crmAttachment', 'crmMailing', 'crmD3']);
+  angular.module('crmMailingAB', ['ngRoute', 'ui.utils', 'crmUi', 'crmAttachment', 'crmMailing', 'crmD3']);
   angular.module('crmMailingAB').config([
     '$routeProvider',
     function ($routeProvider) {
         resolve: {
           mailingABList: function ($route, crmApi) {
             return crmApi('MailingAB', 'get', {rowCount: 0});
+          },
+          fields: function(crmMetadata){
+            return crmMetadata.getFields('MailingAB');
           }
         }
       });
-      $routeProvider.when('/abtest/:id', {
-        templateUrl: '~/crmMailingAB/edit.html',
-        controller: 'CrmMailingABEditCtrl',
+      $routeProvider.when('/abtest/new', {
+        template: '<p>' + ts('Initializing...') + '</p>',
+        controller: 'CrmMailingABNewCtrl',
         resolve: {
           abtest: function ($route, CrmMailingAB) {
-            var abtest = new CrmMailingAB($route.current.params.id == 'new' ? null : $route.current.params.id);
-            return abtest.load();
+            var abtest = new CrmMailingAB(null);
+            return abtest.load().then(function(){
+              return abtest.save();
+            });
           }
         }
       });
-      $routeProvider.when('/abtest/:id/report', {
-        templateUrl: '~/crmMailingAB/report.html',
-        controller: 'CrmMailingABReportCtrl',
+      $routeProvider.when('/abtest/:id', {
+        templateUrl: '~/crmMailingAB/main.html',
+        controller: 'CrmMailingABEditCtrl',
         resolve: {
           abtest: function ($route, CrmMailingAB) {
-            var abtest = new CrmMailingAB($route.current.params.id);
+            var abtest = new CrmMailingAB($route.current.params.id == 'new' ? null : $route.current.params.id);
             return abtest.load();
           }
         }
     }
   ]);
 
-  angular.module('crmMailingAB').controller('CrmMailingABListCtrl', function ($scope, mailingABList, crmMailingABCriteria, crmMailingABStatus) {
+  angular.module('crmMailingAB').controller('CrmMailingABListCtrl', function($scope, mailingABList, crmMailingABCriteria, crmMailingABStatus, fields) {
     var ts = $scope.ts = CRM.ts(null);
-    $scope.mailingABList = mailingABList.values;
+    $scope.mailingABList = _.values(mailingABList.values);
     $scope.crmMailingABCriteria = crmMailingABCriteria;
     $scope.crmMailingABStatus = crmMailingABStatus;
+    $scope.fields = fields;
+    $scope.filter = {};
   });
 
-  angular.module('crmMailingAB').controller('CrmMailingABEditCtrl', function ($scope, abtest, crmMailingABCriteria, crmMailingMgr, crmMailingPreviewMgr, crmStatus, $q, $location) {
+  angular.module('crmMailingAB').controller('CrmMailingABNewCtrl', function ($scope, abtest, $location) {
+    // Transition URL "/abtest/new/foo" => "/abtest/123/foo"
+    var parts = $location.path().split('/'); // e.g. "/mailing/new" or "/mailing/123/wizard"
+    parts[2] = abtest.id;
+    $location.path(parts.join('/'));
+    $location.replace();
+  });
+
+  angular.module('crmMailingAB').controller('CrmMailingABEditCtrl', function ($scope, abtest, crmMailingABCriteria, crmMailingMgr, crmMailingPreviewMgr, crmStatus, $q, $location, crmBlocker, $interval, $timeout, CrmAutosaveCtrl, dialogService) {
     $scope.abtest = abtest;
     var ts = $scope.ts = CRM.ts(null);
+    var block = $scope.block = crmBlocker();
+    $scope.crmUrl = CRM.url;
+    var myAutosave = null;
     $scope.crmMailingABCriteria = crmMailingABCriteria;
     $scope.crmMailingConst = CRM.crmMailing;
+    $scope.checkPerm = CRM.checkPerm;
 
     $scope.isSubmitted = function isSubmitted() {
       return _.size(abtest.mailings.a.jobs) > 0 || _.size(abtest.mailings.b.jobs) > 0;
     $scope.sync = function sync() {
       abtest.mailings.a.name = ts('Test A (%1)', {1: abtest.ab.name});
       abtest.mailings.b.name = ts('Test B (%1)', {1: abtest.ab.name});
-      abtest.mailings.c.name = ts('Winner (%1)', {1: abtest.ab.name});
+      abtest.mailings.c.name = ts('Final (%1)', {1: abtest.ab.name});
 
-      var criteria = crmMailingABCriteria.get(abtest.ab.testing_criteria_id);
-      if (criteria) {
+      if (abtest.ab.testing_criteria) {
         // TODO review fields exposed in UI and make sure the sync rules match
-        switch (criteria.name) {
-          case 'Subject lines':
+        switch (abtest.ab.testing_criteria) {
+          case 'subject':
             crmMailingMgr.mergeInto(abtest.mailings.b, abtest.mailings.a, [
               'name',
-              'groups',
-              'mailings',
+              'recipients',
               'subject'
             ]);
             break;
-          case 'From names':
+          case 'from':
             crmMailingMgr.mergeInto(abtest.mailings.b, abtest.mailings.a, [
               'name',
-              'groups',
-              'mailings',
+              'recipients',
               'from_name',
               'from_email'
             ]);
             break;
-          case 'Two different emails':
+          case 'full_email':
             crmMailingMgr.mergeInto(abtest.mailings.b, abtest.mailings.a, [
               'name',
-              'groups',
-              'mailings',
+              'recipients',
               'subject',
               'from_name',
               'from_email',
+              'replyto_email',
+              'override_verp', // keep override_verp and replyto_Email linked
               'body_html',
               'body_text'
             ]);
         }
       }
       crmMailingMgr.mergeInto(abtest.mailings.c, abtest.mailings.a, ['name']);
-      return $q.when(true);
+      return true;
     };
 
     // @return Promise
     $scope.save = function save() {
-      $scope.sync();
-      return crmStatus({start: ts('Saving...'), success: ts('Saved')}, abtest.save().then(updateUrl));
+      return block(crmStatus({start: ts('Saving...'), success: ts('Saved')}, abtest.save()));
     };
 
     // @return Promise
     $scope.previewMailing = function previewMailing(mailingName, mode) {
-      $scope.sync();
       return crmMailingPreviewMgr.preview(abtest.mailings[mailingName], mode);
     };
 
     // @return Promise
     $scope.sendTest = function sendTest(mailingName, recipient) {
-      $scope.sync();
-      return crmStatus({start: ts('Saving...'), success: ''}, abtest.save().then(updateUrl))
+      return block(crmStatus({start: ts('Saving...'), success: ''}, abtest.save())
         .then(function () {
           crmMailingPreviewMgr.sendTest(abtest.mailings[mailingName], recipient);
-        });
+        }));
     };
 
     // @return Promise
     $scope.delete = function () {
-      return crmStatus({start: ts('Deleting...'), success: ts('Deleted')}, abtest.delete().then(leave));
+      return block(crmStatus({start: ts('Deleting...'), success: ts('Deleted')}, abtest.delete().then($scope.leave)));
     };
 
     // @return Promise
     $scope.submit = function submit() {
-      $scope.sync();
-      return crmStatus({start: ts('Saving...'), success: ''}, abtest.save())
-        .then(function () {
-          return crmStatus({start: ts('Submitting...'), success: ts('Submitted')}, abtest.submitTest());
-          // Note: We're going to leave, so we don't care that submit() modifies several server-side records.
-          // If we stayed on this page, then we'd care about updating and call: abtest.submitTest().then(...abtest.load()...)
-        })
-        .then(leave);
+      if (block.check() || $scope.crmMailingAB.$invalid) {
+        return;
+      }
+      return block(crmStatus({start: ts('Saving...'), success: ''}, abtest.save())
+          .then(function() {
+            return crmStatus({start: ts('Submitting...'), success: ts('Submitted')}, myAutosave.suspend(abtest.submitTest()));
+            // Note: We're going to leave, so we don't care that submit() modifies several server-side records.
+            // If we stayed on this page, then we'd care about updating and call: abtest.submitTest().then(...abtest.load()...)
+          })
+      );
     };
 
-    function leave() {
+    $scope.leave = function leave() {
       $location.path('abtest');
       $location.replace();
-    }
-
-    function updateCriteriaName() {
-      var criteria = crmMailingABCriteria.get($scope.abtest.ab.testing_criteria_id);
-      $scope.criteriaName = criteria ? criteria.name : null;
-    }
+    };
 
-    // Transition URL "/abtest/new" => "/abtest/123"
-    function updateUrl() {
-      var parts = $location.path().split('/'); // e.g. "/abtest/new" or "/abtest/123/wizard"
-      if (parts[2] != $scope.abtest.ab.id) {
-        parts[2] = $scope.abtest.ab.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.
-      }
-    }
+    $scope.selectWinner = function selectWinner(mailingName) {
+      var model = {
+        abtest: $scope.abtest,
+        mailingName: mailingName
+      };
+      var options = CRM.utils.adjustDialogDefaults({
+        autoOpen: false,
+        title: ts('Select Final Mailing (Test %1)', {
+          1: mailingName.toUpperCase()
+        })
+      });
+      return myAutosave.suspend(dialogService.open('selectWinnerDialog', '~/crmMailingAB/selectWinner.html', model, options));
+    };
 
     // initialize
-    updateCriteriaName();
-    $scope.sync();
-    $scope.$watch('abtest.ab.testing_criteria_id', updateCriteriaName);
+    var syncJob = $interval($scope.sync, 333);
+    $scope.$on('$destroy', function(){
+      $interval.cancel(syncJob);
+    });
+
+    myAutosave = new CrmAutosaveCtrl({
+      save: $scope.save,
+      saveIf: function(){
+        return abtest.ab.status == 'Draft' && $scope.sync();
+      },
+      model: function(){
+        return abtest.getAutosaveSignature();
+      },
+      form: function() {
+        return $scope.crmMailingAB;
+      }
+    });
+    $timeout(myAutosave.start);
+    $scope.$on('$destroy', myAutosave.stop);
   });
 
-  angular.module('crmMailingAB').controller('CrmMailingABReportCtrl', function ($scope, abtest, crmApi, crmMailingPreviewMgr, dialogService) {
+  angular.module('crmMailingAB').controller('CrmMailingABReportCtrl', function ($scope, crmApi, crmMailingStats) {
     var ts = $scope.ts = CRM.ts(null);
 
-    $scope.abtest = abtest;
+    var CrmMailingABReportCnt = 1, activeMailings = null;
+    $scope.getActiveMailings = function() {
+      if ($scope.abtest.$CrmMailingABReportCnt != CrmMailingABReportCnt) {
+        $scope.abtest.$CrmMailingABReportCnt = ++CrmMailingABReportCnt;
+        activeMailings = [
+          {name: 'a', title: ts('Mailing A'), mailing: $scope.abtest.mailings.a, attachments: $scope.abtest.attachments.a},
+          {name: 'b', title: ts('Mailing B'), mailing: $scope.abtest.mailings.b, attachments: $scope.abtest.attachments.b}
+        ];
+        if ($scope.abtest.ab.status == 'Final') {
+          activeMailings.push({name: 'c', title: ts('Final'), mailing: $scope.abtest.mailings.c, attachments: $scope.abtest.attachments.c});
+        }
+      }
+      return activeMailings;
+    };
 
-    $scope.stats = {};
-    crmApi('Mailing', 'stats', {mailing_id: abtest.ab.mailing_id_a}).then(function(data){
-      $scope.stats.a = data.values[abtest.ab.mailing_id_a];
-    });
-    crmApi('Mailing', 'stats', {mailing_id: abtest.ab.mailing_id_b}).then(function(data){
-      $scope.stats.b = data.values[abtest.ab.mailing_id_b];
-    });
-    crmApi('Mailing', 'stats', {mailing_id: abtest.ab.mailing_id_c}).then(function(data){
-      $scope.stats.c = data.values[abtest.ab.mailing_id_c];
+    crmMailingStats.getStats({
+      a: $scope.abtest.ab.mailing_id_a,
+      b: $scope.abtest.ab.mailing_id_b,
+      c: $scope.abtest.ab.mailing_id_c
+    }).then(function(stats) {
+      $scope.stats = stats;
     });
 
-    $scope.previewMailing = function previewMailing(mailingName, mode) {
-      return crmMailingPreviewMgr.preview(abtest.mailings[mailingName], mode);
-    };
-    $scope.selectWinner = function selectWinner(mailingName) {
-      var model = {
-        abtest: abtest,
-        mailingName: mailingName
-      };
-      var options = {
-        autoOpen: false,
-        modal: true,
-        title: ts('Select Winner (%1)', {
-          1: mailingName.toUpperCase()
-        })
-      };
-      return dialogService.open('selectWinnerDialog', '~/crmMailingAB/selectWinner.html', model, options);
+    $scope.statTypes = crmMailingStats.getStatTypes();
+    $scope.statUrl = function statUrl(mailing, statType, view) {
+      return crmMailingStats.getUrl(mailing, statType, view, 'abtest/' + $scope.abtest.ab.id);
     };
-  });
 
+    $scope.checkPerm = CRM.checkPerm;
+  });
 
   angular.module('crmMailingAB').controller('CrmMailingABWinnerDialogCtrl', function ($scope, $timeout, dialogService, crmMailingMgr, crmStatus) {
     var ts = $scope.ts = CRM.ts(null);
       // need to be registered with the dialog widget (and not embedded in
       // the body of the dialog).
       var buttons = {};
-      buttons[ts('Select Winner')] = function () {
+      buttons[ts('Submit final mailing')] = function () {
         crmMailingMgr.mergeInto(abtest.mailings.c, abtest.mailings[mailingName], [
           'name',
-          'groups',
-          'mailings',
+          'recipients',
           'scheduled_date'
         ]);
         crmStatus({start: ts('Saving...'), success: ''}, abtest.save())
           .then(function () {
             return crmStatus({start: ts('Submitting...'), success: ts('Submitted')},
-              abtest.submitFinal().then(function(){
-                return abtest.load();
+              abtest.submitFinal().then(function(r){
+                delete abtest.$CrmMailingABReportCnt;
+                return r;
               }));
           })
           .then(function(){