CRM-15578 - crmMailingAB2 - Implement joint composition screen (for "Subject" and...
authorTim Otten <totten@civicrm.org>
Sun, 14 Dec 2014 02:36:03 +0000 (18:36 -0800)
committerTim Otten <totten@civicrm.org>
Sun, 14 Dec 2014 22:55:40 +0000 (14:55 -0800)
CRM/Mailing/Info.php
js/angular-crmMailingAB2-directives.js [new file with mode: 0644]
js/angular-crmMailingAB2-services.js
js/angular-crmMailingAB2.js
partials/crmMailing2/mailing.html
partials/crmMailingAB2/edit-split.html [new file with mode: 0644]
partials/crmMailingAB2/edit.html
partials/crmMailingAB2/joint-mailing.html [new file with mode: 0644]

index 86e9c42f47f6f4c72863b3656e2fde3bb8976631..b384bed4f606d6f7e803c7d1ea3928ead3f4acee 100644 (file)
@@ -72,7 +72,7 @@ class CRM_Mailing_Info extends CRM_Core_Component_Info {
     );
     $result['crmMailingAB2'] = array(
       'ext' => 'civicrm',
-      'js' => array('js/angular-crmMailingAB2.js', 'js/angular-crmMailingAB2-services.js'),
+      'js' => array('js/angular-crmMailingAB2.js', 'js/angular-crmMailingAB2-services.js', 'js/angular-crmMailingAB2-directives.js'),
       'css' => array('css/angular-crmMailingAB2.css'),
     );
     $result['crmMailingAB'] = array(
diff --git a/js/angular-crmMailingAB2-directives.js b/js/angular-crmMailingAB2-directives.js
new file mode 100644 (file)
index 0000000..b8f4828
--- /dev/null
@@ -0,0 +1,27 @@
+(function (angular, $, _) {
+  var partialUrl = function (relPath) {
+    return CRM.resourceUrls['civicrm'] + '/partials/crmMailingAB2/' + relPath;
+  };
+
+  // example:
+  //   scope.myAbtest = new CrmMailingAB();
+  //   <crm-mailing-ab-block-mailing="{fromAddressA: 1, fromAddressB: 1}" crm-abtest="myAbtest" />
+  angular.module('crmMailingAB2').directive('crmMailingAbBlockMailing', function ($parse) {
+    return {
+      scope: {
+        crmMailingAbBlockMailing: '@',
+        crmAbtest: '@'
+      },
+      templateUrl: partialUrl('joint-mailing.html'),
+      link: function (scope, elm, attr) {
+        var model = $parse(attr.crmAbtest);
+        scope.abtest = model(scope.$parent);
+        scope.crmMailingConst = CRM.crmMailing;
+        scope.ts = CRM.ts('CiviMail');
+
+        var fieldsModel = $parse(attr.crmMailingAbBlockMailing);
+        scope.fields = fieldsModel(scope.$parent);
+      }
+    };
+  })
+})(angular, CRM.$, CRM._);
index b077d50931f32354ee813c93268b3289eaa8f7fc..398a7d6293bcf73aa5fa7ec0a5ab9ffe597d4902 100644 (file)
         var crmMailingAB = this;
         if (!crmMailingAB.id) {
           crmMailingAB.ab = {
-            name: '',
+            name: 'Example', // FIXME
             mailing_id_a: null,
             mailing_id_b: null,
             mailing_id_c: null,
             domain_id: null,
-            testing_criteria_id: null,
+            testing_criteria_id: 1, // FIXME
             winner_criteria_id: null,
             specific_url: '',
             declare_winning_time: null,
index 8805dca3cf9399e3a42f9d19207a6bc8810cf531..22c2709f9d377839fb6b523e8e1b248376dab9da 100644 (file)
@@ -43,6 +43,8 @@
     $scope.abtest = abtest;
     $scope.ts = CRM.ts('CiviMail');
     $scope.crmMailingABCriteria = crmMailingABCriteria;
+    $scope.crmMailingConst = CRM.crmMailing;;
+    $scope.partialUrl = partialUrl;
 
     $scope.sync = function sync() {
       abtest.mailings.a.name = ts('Test A (%1)', {1: abtest.ab.name});
     $scope.delete = function () {
       throw "Not implemented: EditCtrl.delete"
     };
+    $scope.submit = function () {
+      throw "Not implemented: EditCtrl.submit"
+    };
+
+    function updateCriteriaName() {
+      $scope.criteriaName = crmMailingABCriteria.get($scope.abtest.ab.testing_criteria_id).name;
+    }
 
+    // initialize
+    updateCriteriaName();
     $scope.sync();
+    $scope.$watch('abtest.ab.testing_criteria_id', updateCriteriaName);
   });
 
 })(angular, CRM.$, CRM._);
index 62b4b524ad2a1040e81f9ff3ff377be461732801..7e93c121474c78d8ff69cc8ba66faed3b7c2cf5d 100644 (file)
@@ -1,6 +1,8 @@
 <!--
 Controller: EditMailingCtrl
 Required vars: mailing, crmMailingConst
+Note: Much of this file is duplicated in crmMailing and crmMailingAB with variations on placement/title/binding.
+It could perhaps be thinned by 30-60% by making more directives.
 -->
 <div class="crm-block" ng-form="subform" crm-ui-id-scope>
   <div class="crm-group">
diff --git a/partials/crmMailingAB2/edit-split.html b/partials/crmMailingAB2/edit-split.html
new file mode 100644 (file)
index 0000000..626a15c
--- /dev/null
@@ -0,0 +1 @@
+Split screen!
index 09c6763311bb2bc7b5c24c682c1e74b6cedb1e09..a1704eadb5c1e21272dcca09eed9f636ae29762c 100644 (file)
@@ -2,10 +2,17 @@
   <pre>{{abtest|json}}</pre>
 </div>
 
-<form name="crmMailingAB2">
+<!--
+  An ABTest includes two mailings, but we don't require the user to enter two complete mailings. For
+  simplicity, the email composition UI generally displays A (unless we specifically decided to expose an
+  individual field from B). At the end of the composition process, the controller's "sync" operation will
+  merge shared settings from "A" into "B".
+-->
+
+<form name="crmMailingAB2" novalidate>
   <div class="crm-block crm-form-block crmMailing2">
     <div crm-ui-wizard>
-      <div crm-ui-wizard-step crm-title="ts('Setup')">
+      <div crm-ui-wizard-step="10" crm-title="ts('Setup')">
         <div ng-form="setupForm" crm-ui-id-scope>
           <div>
             <label crm-ui-for="setupForm.abName">{{ts('What would you like to name the test?')}}</label>
           </div>
         </div>
       </div>
-      <div crm-ui-wizard-step crm-title="ts('Content')">
-        <div>
-          Subject A: <input ng-model="abtest.mailings.a.subject"/>
+      <div crm-ui-wizard-step="20" crm-title="ts('Compose')" ng-if="criteriaName != 'Two different emails'">
+        <div crm-ui-tab-set>
+          <div crm-ui-tab id="tab-mailing" crm-title="ts('Mailing')">
+            <div
+              ng-if="criteriaName == 'From names'"
+              crm-mailing-ab-block-mailing="{
+                msg_template_id: 1,
+                fromAddressA: 1,
+                fromAddressB: 1,
+                replyTo: 1,
+                subject: 1
+                }"
+              crm-abtest="abtest"></div>
+            <div
+              ng-if="criteriaName == 'Subject lines'"
+              crm-mailing-ab-block-mailing="{
+                msg_template_id: 1,
+                fromAddress: 1,
+                replyTo: 1,
+                subjectA: 1,
+                subjectB: 1
+                }"
+              crm-abtest="abtest"></div>
+            <div crm-ui-accordion crm-title="ts('HTML')">
+              <div crm-mailing-body-html crm-mailing="abtest.mailings.a"/>
+            </div>
+            <div crm-ui-accordion crm-title="ts('Plain Text')" crm-collapsed='true'>
+              <div crm-mailing-body-text crm-mailing="abtest.mailings.a"/>
+            </div>
+          </div>
+          <!--
+          <div crm-ui-tab id="tab-attachment" crm-title="ts('Attachments')">
+            <div crm-attachments="attachments"/>
+          </div>
+          -->
+          <div crm-ui-tab id="tab-header" crm-title="ts('Header and Footer')">
+            <div crm-mailing-block-header-footer crm-mailing="abtest.mailings.a"/>
+          </div>
+          <div crm-ui-tab id="tab-pub" crm-title="ts('Publication')">
+            <div crm-mailing-block-publication crm-mailing="abtest.mailings.a"/>
+          </div>
+          <div crm-ui-tab id="tab-response" crm-title="ts('Responses')">
+            <div crm-mailing-block-responses crm-mailing="abtest.mailings.a"/>
+          </div>
+        </div>
+        <div crm-ui-accordion crm-title="ts('Preview (A)')">
+          <div crm-mailing-block-preview crm-mailing="abtest.mailings.a"/>
         </div>
-        <div>
-          Subject B: <input ng-model="abtest.mailings.b.subject"/>
+        <div crm-ui-accordion crm-title="ts('Preview (B)')">
+          <div crm-mailing-block-preview crm-mailing="abtest.mailings.b"/>
         </div>
       </div>
-    <span crm-ui-wizard-buttons style="float:right;">
-      <button
-        crm-confirm="{title:ts('Delete Draft?'), message:ts('Are you sure you want to delete the draft mailing?')}"
-        on-yes="delete()">{{ts('Delete Draft')}}
-      </button>
-      <button ng-click="save()">{{ts('Save Draft')}}</button>
-    </span>
+      <div crm-ui-wizard-step="30" crm-title="ts('Compose (A)')" ng-if="criteriaName == 'Two different emails'">
+        A
+      </div>
+      <div crm-ui-wizard-step="40" crm-title="ts('Compose (B)')" ng-if="criteriaName == 'Two different emails'">
+        B
+      </div>
+      <span crm-ui-wizard-buttons style="float:right;">
+        <button
+          crm-confirm="{title:ts('Delete Draft?'), message:ts('Are you sure you want to delete the draft mailing?')}"
+          on-yes="delete()">{{ts('Delete Draft')}}
+        </button>
+        <button ng-click="save()">{{ts('Save Draft')}}</button>
+      </span>
     </div>
-  </div>
 </form>
diff --git a/partials/crmMailingAB2/joint-mailing.html b/partials/crmMailingAB2/joint-mailing.html
new file mode 100644 (file)
index 0000000..18be406
--- /dev/null
@@ -0,0 +1,145 @@
+<!--
+Required vars: abtest
+Note: Much of this file is duplicated in crmMailing and crmMailingAB with variations on placement/title/binding.
+It could perhaps be thinned by 30-60% by making more directives.
+-->
+<div class="crm-block" ng-form="subform" crm-ui-id-scope>
+  <div class="crm-group">
+    <div crm-ui-field="subform.msg_template_id" crm-title="ts('Template')" style="background: #bbf; width:100%; padding: 0.1em;" ng-if="fields.msg_template_id">
+      <div ng-controller="MsgTemplateCtrl">
+        <select
+          crm-ui-id="subform.msg_template_id"
+          name="msg_template_id"
+          crm-ui-select="{dropdownAutoWidth : true, allowClear: true, placeholder: ts('Message Template')}"
+          ng-model="abtest.mailings.a.msg_template_id"
+          ng-change="loadTemplate(abtest.mailings.a, abtest.mailings.a.msg_template_id)"
+          >
+          <option value=""></option>
+          <option ng-repeat="frm in crmMsgTemplates.getAll() | orderBy:'msg_title'" ng-value="frm.id">{{frm.msg_title}}</option>
+        </select>
+        <a ng-click="saveTemplate(abtest.mailings.a)" class="crm-hover-button action-item" title="{{ts('Save As')}}"><span class="icon ui-icon-disk"></span></a>
+      </div>
+    </div>
+    <div crm-ui-field="subform.fromAddress" crm-title="ts('From')" ng-if="fields.fromAddress">
+      <span ng-controller="EmailAddrCtrl" crm-mailing-from-address="fromPlaceholder" crm-mailing="abtest.mailings.a">
+        <select
+          crm-ui-id="subform.fromAddress"
+          name="fromAddress"
+          ui-jq="select2"
+          ui-options="{dropdownAutoWidth : true, allowClear: false, placeholder: ts('Email address')}"
+          ng-model="fromPlaceholder.label"
+          ng-options="frm.label as frm.label for frm in crmFromAddresses.getAll() | filter:{is_active:1} | orderBy:'weight'"
+          required>
+          <option value=""></option>
+        </select>
+      </span>
+    </div>
+    <div crm-ui-field="subform.fromAddressA" crm-title="ts('From (A)')" ng-if="fields.fromAddressA">
+      <span ng-controller="EmailAddrCtrl" crm-mailing-from-address="fromPlaceholder" crm-mailing="abtest.mailings.a">
+        <select
+          crm-ui-id="subform.fromAddressA"
+          name="fromAddressA"
+          ui-jq="select2"
+          ui-options="{dropdownAutoWidth : true, allowClear: false, placeholder: ts('Email address')}"
+          ng-model="fromPlaceholder.label"
+          ng-options="frm.label as frm.label for frm in crmFromAddresses.getAll() | filter:{is_active:1} | orderBy:'weight'"
+          required>
+          <option value=""></option>
+        </select>
+      </span>
+    </div>
+    <div crm-ui-field="subform.fromAddressB" crm-title="ts('From (B)')" ng-if="fields.fromAddressB">
+      <span ng-controller="EmailAddrCtrl" crm-mailing-from-address="fromPlaceholder" crm-mailing="abtest.mailings.b">
+        <select
+          crm-ui-id="subform.fromAddressB"
+          name="fromAddressB"
+          ui-jq="select2"
+          ui-options="{dropdownAutoWidth : true, allowClear: false, placeholder: ts('Email address')}"
+          ng-model="fromPlaceholder.label"
+          ng-options="frm.label as frm.label for frm in crmFromAddresses.getAll() | filter:{is_active:1} | orderBy:'weight'"
+          required>
+          <option value=""></option>
+        </select>
+      </span>
+    </div>
+    <div crm-ui-field="subform.replyTo" crm-title="ts('Reply-To')" ng-show="crmMailingConst.enableReplyTo" ng-if="fields.replyTo">
+      <span ng-controller="EmailAddrCtrl">
+        <select
+          crm-ui-id="subform.replyTo"
+          name="replyTo"
+          ui-jq="select2"
+          ui-options="{dropdownAutoWidth : true, allowClear: true, placeholder: ts('Email address')}"
+          ng-model="abtest.mailings.a.replyto_email"
+          ng-options="frm.label as frm.label for frm in crmFromAddresses.getAll() | filter:{is_active:1} | orderBy:'weight'"
+          >
+          <option value=""></option>
+        </select>
+      </span>
+    </div>
+    <!--
+    <div crm-ui-field="subform.recipients" crm-title="ts('Recipients')">
+      <div ng-controller="EditRecipCtrl">
+        <div style="float: right;">
+          <div class='crmMailing2-recip-est'>
+            <a href="" ng-click="previewRecipients()">{{getRecipientsEstimate()}}</a>
+          </div>
+          <div>
+            <input name='dedupe_email' type='checkbox' ng-model='abtest.mailings.a.dedupe_email'  ng-true-value="1" ng-false-value="0" id="recipients-dedupe-email" />
+            <label for="recipients-dedupe-email">
+              {{ts('Dedupe')}}
+            </label>
+          </div>
+        </div>
+        <select crm-mailing-recipients
+                crm-mailing="mailing"
+                crm-avail-groups="crmMailingConst.groupNames | filter:{visibility:'Public pages'}"
+                crm-avail-mailings="crmMailingConst.civiMails | filter:{is_completed:1}"
+                name="recipients"
+                crm-ui-id="subform.recipients"
+                required
+                multiple>
+        </select>
+      </div>
+    </div>
+    -->
+    <div crm-ui-field="subform.subject" crm-title="ts('Subject')" ng-if="fields.subject">
+      <div style="float: right;">
+        <input crm-mailing-token crm-for="subject" />
+      </div>
+      <input
+        crm-ui-id="subform.subject"
+        type="text"
+        class="crm-form-text"
+        ng-model="abtest.mailings.a.subject"
+        required
+        placeholder="Subject"
+        name="subject" />
+    </div>
+    <div crm-ui-field="subform.subjectA" crm-title="ts('Subject (A)')" ng-if="fields.subjectA">
+      <div style="float: right;">
+        <input crm-mailing-token crm-for="subjectA" />
+      </div>
+      <input
+        crm-ui-id="subform.subjectA"
+        type="text"
+        class="crm-form-text"
+        ng-model="abtest.mailings.a.subject"
+        required
+        placeholder="Subject"
+        name="subjectA" />
+    </div>
+    <div crm-ui-field="subform.subjectB" crm-title="ts('Subject (B)')" ng-if="fields.subjectB">
+      <div style="float: right;">
+        <input crm-mailing-token crm-for="subjectB" />
+      </div>
+      <input
+        crm-ui-id="subform.subjectB"
+        type="text"
+        class="crm-form-text"
+        ng-model="abtest.mailings.b.subject"
+        required
+        placeholder="Subject"
+        name="subjectB" />
+    </div>
+  </div>
+</div>