CRM-15856 - Enforce required tokens in UI
authorTim Otten <totten@civicrm.org>
Thu, 12 Feb 2015 00:32:46 +0000 (16:32 -0800)
committerTim Otten <totten@civicrm.org>
Thu, 12 Feb 2015 19:59:17 +0000 (11:59 -0800)
js/angular-crmMailing.js
js/angular-crmMailing/services.js
partials/crmMailing/body_html.html
partials/crmMailing/body_text.html
partials/crmMailing/headerFooter.html

index ed5321d7b08a27c7621e8a36842577fb09d1ac6f..ea8733a8ed1bdfbd58ef46705f1a6b358dd65b47 100644 (file)
   angular.module('crmMailing').controller('EmailAddrCtrl', function EmailAddrCtrl($scope, crmFromAddresses) {
     $scope.crmFromAddresses = crmFromAddresses;
   });
+
+  var lastEmailTokenAlert = null;
+  angular.module('crmMailing').controller('EmailBodyCtrl', function EmailBodyCtrl($scope, crmMailingMgr) {
+    var ts = CRM.ts(null);
+
+    $scope.hasAllTokens = function hasMissingTokens(mailing, field) {
+      return _.isEmpty(crmMailingMgr.findMissingTokens(mailing, field));
+    };
+
+    $scope.checkTokens = function checkTokens(mailing) {
+      if (lastEmailTokenAlert) {
+        lastEmailTokenAlert.close();
+      }
+      var missing = angular.extend(
+        {},
+        crmMailingMgr.findMissingTokens(mailing, 'body_html'),
+        crmMailingMgr.findMissingTokens(mailing, 'body_text')
+      );
+      if (!_.isEmpty(missing) > 0) {
+        var buf = '<p>'
+          + ts('Before submitting this mailing, you must include an address token and an action token as part of the mailing body, mailing header, or mailing footer.')
+          + '</p><ul>';
+        angular.forEach(missing, function(msg, token) {
+          // FIXME LTR RTL
+          buf = buf + '<li>{' + token + '} - <em>' + msg + '</em></li>'
+        });
+        buf += '</ul>';
+        lastEmailTokenAlert = CRM.alert(buf, undefined, 'error');
+      }
+    }
+  });
 })(angular, CRM.$, CRM._);
index 01b04d3287b5c71d5c9f1c7f4ff980b0a5e8410d..3c43dcdaf8c4c6a1649c036319444beda7e6a4cc 100644 (file)
         }
       },
 
+      // Search the body, header, and footer for required tokens.
+      // ex: var msgs = findMissingTokens(mailing, 'body_html');
+      findMissingTokens: function(mailing, field) {
+        var missing = {};
+        if (!_.isEmpty(mailing[field])) {
+          var body = '';
+          if (mailing.footer_id) {
+            var footer = _.where(CRM.crmMailing.headerfooterList, {id: mailing.footer_id});
+            body = body + footer[0][field];
+
+          }
+          body = body + mailing[field];
+          if (mailing.header_id) {
+            var header = _.where(CRM.crmMailing.headerfooterList, {id: mailing.header_id});
+            body = body + header[0][field];
+          }
+
+          angular.forEach(CRM.crmMailing.requiredTokens, function(value, token) {
+            if (!_.isObject(value)) {
+              if (body.indexOf('{' + token + '}') < 0) {
+                missing[token] = value;
+              }
+            }
+            else {
+              var count = 0;
+              angular.forEach(value, function(nestedValue, nestedToken) {
+                if (body.indexOf('{' + nestedToken + '}') >= 0) {
+                  count++;
+                }
+              });
+              if (count == 0) {
+                angular.extend(missing, value);
+              }
+            }
+          });
+        }
+        return missing;
+      },
+
       // Copy all data fields in (mailingFrom) to (mailingTgt) -- except for (excludes)
       // ex: crmMailingMgr.mergeInto(newMailing, mailingTemplate, ['subject']);
       mergeInto: function mergeInto(mailingTgt, mailingFrom, excludes) {
index 6805929dad14489cc792266db306121ff1140ef9..1953d51291113ac43d349e83debaf0c9ea9c80b8 100644 (file)
@@ -6,5 +6,8 @@ Required vars: mailing
     <input crm-mailing-token crm-for="htmlForm.body_html"/>
   </div>
 
-  <textarea crm-ui-id="htmlForm.body_html" crm-ui-richtext name="body_html" ng-model="mailing.body_html"></textarea>
-</div>
\ No newline at end of file
+  <div ng-controller="EmailBodyCtrl">
+    <textarea crm-ui-id="htmlForm.body_html" crm-ui-richtext name="body_html" ng-model="mailing.body_html" ng-blur="checkTokens(mailing)"></textarea>
+    <span ng-model="body_html_tokens" crm-ui-validate="hasAllTokens(mailing, 'body_html')"></span>
+  </div>
+</div>
index 2b41e981bef045b208b3af4f8a8cc922089af026..2d4665c8dd97d312ea3dea159fcb3f11a60b8341 100644 (file)
@@ -5,5 +5,8 @@ Required vars: mailing, crmMailingConst
   <div style="float: right;">
     <input crm-mailing-token crm-for="textForm.body_text"/>
   </div>
-  <textarea crm-ui-id="textForm.body_text" name="body_text" ng-model="mailing.body_text"></textarea>
+  <div ng-controller="EmailBodyCtrl">
+    <textarea crm-ui-id="textForm.body_text" name="body_text" ng-model="mailing.body_text" ng-blur="checkTokens(mailing)"></textarea>
+    <span ng-model="body_text_tokens" crm-ui-validate="hasAllTokens(mailing, 'body_text')"></span>
+  </div>
 </div>
index bd1d1744ab5fffb71a68c07259998396bf002731..11a2910760cae933d46199f23ad66a65e011c89d 100644 (file)
@@ -3,13 +3,14 @@ Controller: EditMailingCtrl
 Required vars: mailing, crmMailingConst
 -->
 <div class="crm-block"  ng-form="subform" crm-ui-id-scope>
-  <div class="crm-group">
+  <div class="crm-group" ng-controller="EmailBodyCtrl">
     <div crm-ui-field="subform.header_id" crm-title="ts('Mailing Header')">
       <select
         crm-ui-id="subform.header_id"
         name="header_id"
         ui-jq="select2"
         ui-options="{dropdownAutoWidth : true, allowClear: true}"
+        ng-change="checkTokens(mailing)"
         ng-model="mailing.header_id"
         ng-options="mc.id as mc.name for mc in crmMailingConst.headerfooterList | filter:{component_type: 'Header'}">
         <option value=""></option>
@@ -21,6 +22,7 @@ Required vars: mailing, crmMailingConst
         name="footer_id"
         ui-jq="select2"
         ui-options="{dropdownAutoWidth : true, allowClear: true}"
+        ng-change="checkTokens(mailing)"
         ng-model="mailing.footer_id"
         ng-options="mc.id as mc.name for mc in crmMailingConst.headerfooterList | filter:{component_type: 'Footer'}">
         <option value=""></option>