Merge pull request #5499 from colemanw/CRM-16155
authorTim Otten <totten@civicrm.org>
Mon, 30 Mar 2015 22:03:04 +0000 (15:03 -0700)
committerTim Otten <totten@civicrm.org>
Mon, 30 Mar 2015 22:03:04 +0000 (15:03 -0700)
CRM-16155 - Improve scalability of CiviMail UI

CRM/Mailing/Info.php
js/angular-crm-ui.js
partials/crmMailing/summary.html
partials/crmMailingAB/setup.html

index 718209f92428ab721841050beb3b5cdad3f04457..b3eef8170db47cf4e1c60d1ef3894649f72aa7d9 100644 (file)
@@ -58,7 +58,7 @@ class CRM_Mailing_Info extends CRM_Core_Component_Info {
   }
 
   /**
-   * EXPERIMENTAL: Get a list of AngularJS modules
+   * Get AngularJS modules and their dependencies
    *
    * @return array
    *   list of modules; same format as CRM_Utils_Hook::angularModules(&$angularModules)
@@ -92,63 +92,89 @@ class CRM_Mailing_Info extends CRM_Core_Component_Info {
       ),
     );
 
+    $config = CRM_Core_Config::singleton();
     $session = CRM_Core_Session::singleton();
     $contactID = $session->get('userID');
 
-    $params = array('options' => array('limit' => 0));
-    $civiMails = civicrm_api3('Mailing', 'get', $params + array(
+    // Get past mailings
+    // CRM-16155 - Limit to a reasonable number
+    $civiMails = civicrm_api3('Mailing', 'get', array(
       'is_completed' => 1,
+      'mailing_type' => array('IN' => array('standalone', 'winner')),
       'return' => array('id', 'name', 'scheduled_date'),
+      'sequential' => 1,
+      'options' => array(
+        'limit' => 500,
+        'sort' => 'is_archived asc, scheduled_date desc',
+      ),
+    ));
+    // Generic params
+    $params = array(
+      'options' => array('limit' => 0),
+      'sequential' => 1,
+    );
+
+    $groupNames = civicrm_api3('Group', 'get', $params + array(
+      'is_active' => 1,
+      'return' => array('title', 'visibility', 'group_type', 'is_hidden'),
+    ));
+    $headerfooterList = civicrm_api3('MailingComponent', 'get', $params + array(
+      'is_active' => 1,
+      'return' => array('name', 'component_type', 'is_default'),
     ));
-    $campNames = civicrm_api3('Campaign', 'get', $params);
-    $groupNames = civicrm_api3('Group', 'get', $params);
-    $headerfooterList = civicrm_api3('MailingComponent', 'get', $params);
 
     $emailAdd = civicrm_api3('Email', 'get', array(
       'sequential' => 1,
       'return' => "email",
       'contact_id' => $contactID,
     ));
+
+    // FIXME: Loading the contents of every template into the dom does not scale well
     $mesTemplate = civicrm_api3('MessageTemplate', 'get', $params + array(
       'sequential' => 1,
+      'is_active' => 1,
       'return' => array("msg_html", "id", "msg_title", "msg_subject", "msg_text"),
       'workflow_id' => array('IS NULL' => ""),
     ));
     $mailGrp = civicrm_api3('MailingGroup', 'get', $params);
-    $mailTokens = civicrm_api3('Mailing', 'gettokens', array('entity' => array('contact', 'mailing'), 'sequential' => 1));
+    $mailTokens = civicrm_api3('Mailing', 'gettokens', array(
+      'entity' => array('contact', 'mailing'),
+      'sequential' => 1,
+    ));
     $fromAddress = civicrm_api3('OptionValue', 'get', $params + array(
       'option_group_id' => "from_email_address",
     ));
-    CRM_Core_Resources::singleton()->addSetting(array(
-      'crmMailing' => array(
-        'civiMails' => array_values($civiMails['values']),
-        'campNames' => array_values($campNames['values']),
-        'groupNames' => array_values($groupNames['values']),
-        'headerfooterList' => array_values($headerfooterList['values']),
-        'mesTemplate' => array_values($mesTemplate['values']),
-        'emailAdd' => array_values($emailAdd['values']),
-        'mailGrp' => array_values($mailGrp['values']),
-        'mailTokens' => $mailTokens['values'],
-        'contactid' => $contactID,
-        'requiredTokens' => CRM_Utils_Token::getRequiredTokens(),
-        'enableReplyTo' => (int) CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, 'replyTo'),
-        'fromAddress' => array_values($fromAddress['values']),
-        'defaultTestEmail' => civicrm_api3('Contact', 'getvalue', array(
-            'id' => 'user_contact_id',
-            'return' => 'email',
-          )),
-        'visibility' => CRM_Utils_Array::makeNonAssociative(CRM_Core_SelectValues::groupVisibility()),
-        'workflowEnabled' => CRM_Mailing_Info::workflowEnabled(),
-      ),
-    ));
-    CRM_Core_Resources::singleton()->addPermissions(array(
-      'view all contacts',
-      'access CiviMail',
-      'create mailings',
-      'schedule mailings',
-      'approve mailings',
-      'delete in CiviMail',
-    ));
+    CRM_Core_Resources::singleton()
+      ->addSetting(array(
+        'crmMailing' => array(
+          'civiMails' => $civiMails['values'],
+          'campaignEnabled' => in_array('CiviCampaign', $config->enableComponents),
+          'groupNames' => $groupNames['values'],
+          'headerfooterList' => $headerfooterList['values'],
+          'mesTemplate' => $mesTemplate['values'],
+          'emailAdd' => $emailAdd['values'],
+          'mailGrp' => $mailGrp['values'],
+          'mailTokens' => $mailTokens['values'],
+          'contactid' => $contactID,
+          'requiredTokens' => CRM_Utils_Token::getRequiredTokens(),
+          'enableReplyTo' => (int) CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, 'replyTo'),
+          'fromAddress' => $fromAddress['values'],
+          'defaultTestEmail' => civicrm_api3('Contact', 'getvalue', array(
+              'id' => 'user_contact_id',
+              'return' => 'email',
+            )),
+          'visibility' => CRM_Utils_Array::makeNonAssociative(CRM_Core_SelectValues::groupVisibility()),
+          'workflowEnabled' => CRM_Mailing_Info::workflowEnabled(),
+        ),
+      ))
+      ->addPermissions(array(
+        'view all contacts',
+        'access CiviMail',
+        'create mailings',
+        'schedule mailings',
+        'approve mailings',
+        'delete in CiviMail',
+      ));
 
     return $result;
   }
index f055ca6c7d6c3dd7d4c8caafebb3962af24e8ded..3e821bb7e56fdc13f3cf22642ed993cecc7ca7f5 100644 (file)
       };
     })
 
+    // Render a crmEntityRef widget
+    // usage: <input crm-entityref="{entity: 'Contact', select: {allowClear:true}}" ng-model="myobj.field" />
+    .directive('crmEntityref', function ($parse, $timeout) {
+      return {
+        require: '?ngModel',
+        scope: {
+          crmEntityref: '='
+        },
+        link: function (scope, element, attrs, ngModel) {
+          // In cases where UI initiates update, there may be an extra
+          // call to refreshUI, but it doesn't create a cycle.
+
+          ngModel.$render = function () {
+            $timeout(function () {
+              // ex: msg_template_id adds new item then selects it; use $timeout to ensure that
+              // new item is added before selection is made
+              element.select2('val', ngModel.$viewValue);
+            });
+          };
+          function refreshModel() {
+            var oldValue = ngModel.$viewValue, newValue = element.select2('val');
+            if (oldValue != newValue) {
+              scope.$parent.$apply(function () {
+                ngModel.$setViewValue(newValue);
+              });
+            }
+          }
+
+          function init() {
+            // TODO watch options
+            // TODO can we infer "entity" from model?
+            element.crmEntityRef(scope.crmEntityref || {});
+            element.on('change', refreshModel);
+            $timeout(ngModel.$render);
+          }
+
+          init();
+        }
+      };
+    })
+
     // example <div crm-ui-tab crm-title="ts('My Title')">...content...</div>
     // WISHLIST: use a full Angular component instead of an incomplete jQuery wrapper
     .directive('crmUiTab', function($parse) {
index 5b8b8407861b5e0c3ee8a9fd11c8952a12c4c96c..6050e448a2c3a692ec7e8ed668baf02965a4f562 100644 (file)
@@ -17,17 +17,13 @@ FIXME: Don't hardcode table-based layout!
           name="mailingName" />
       </div>
     </div>
-    <div crm-ui-field="{name: 'subform.campaign', title: ts('Campaign'), help: hs({id: 'id-campaign_id', file: 'CRM/Campaign/Form/addCampaignToComponent'})}" ng-show="crmMailingConst.campNames.length > 0">
-      <select
+    <div crm-ui-field="{name: 'subform.campaign', title: ts('Campaign'), help: hs({id: 'id-campaign_id', file: 'CRM/Campaign/Form/addCampaignToComponent'})}" ng-show="crmMailingConst.campaignEnabled">
+      <input
+        crm-entityref="{entity: 'Campaign', select: {allowClear: true, placeholder: ts('Select Campaign')}}"
         crm-ui-id="subform.campaign"
         name="campaign"
-        ui-jq="select2"
-        ui-options="{dropdownAutoWidth : true, allowClear: true, placeholder: ts('Select Campaign')}"
         ng-model="mailing.campaign_id"
-        ng-options="campaign.id as campaign.name for campaign in crmMailingConst.campNames|orderBy:'name'"
-        >
-        <option value=""></option>
-      </select>
+      />
     </div>
   </div>
 </div>
index 8a889e258603b2c0a71a7a8ff8df80b2adbeb3ac..bed1989d90393671d2d15fc21d01a8a416fd7dc3 100644 (file)
         placeholder="A/B Test Name"
         required/>
     </div>
-    <div crm-ui-field="{name: 'setupForm.campaign', title: ts('Campaign'), help: hs({id: 'id-campaign_id', file: 'CRM/Campaign/Form/addCampaignToComponent'})}" ng-show="crmMailingConst.campNames.length > 0"
+    <div crm-ui-field="{name: 'setupForm.campaign', title: ts('Campaign'), help: hs({id: 'id-campaign_id', file: 'CRM/Campaign/Form/addCampaignToComponent'})}" ng-show="crmMailingConst.campaignEnabled"
          ng-if="fields.campaign">
-      <select
+      <input
+        crm-entityref="{entity: 'Campaign', select: {allowClear: true, placeholder: ts('Select Campaign')}}"
         crm-ui-id="setupForm.campaign"
         name="campaign"
-        ui-jq="select2"
-        ui-options="{dropdownAutoWidth : true, allowClear: true, placeholder: ts('Select Campaign')}"
         ng-model="mailing.campaign_id"
-        ng-options="campaign.id as campaign.name for campaign in crmMailingConst.campNames|orderBy:'name'"
-        >
-        <option value=""></option>
-      </select>
+      />
     </div>
     <div crm-ui-field="{title: ts('Test Type')}" ng-if="fields.testing_criteria">
       <div ng-repeat="criteria in crmMailingABCriteria.getAll()">