CRM-13966 - Add method to Core_Form for contactRef fields
authorColeman Watts <coleman@civicrm.org>
Sun, 2 Feb 2014 04:58:44 +0000 (20:58 -0800)
committerColeman Watts <coleman@civicrm.org>
Sun, 2 Feb 2014 04:58:44 +0000 (20:58 -0800)
CRM/Core/Form.php
js/Common.js

index a645499491f41e09f9efd1eed7445c3bc86e68b0..c604f97c563a6917c2acd0bd71824d33aedef71e 100644 (file)
@@ -105,6 +105,14 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
    */
   public $ajaxResponse = array();
 
+  /**
+   * Stores info about reference fields for preprocessing
+   * Public so that hooks can access it
+   *
+   * @var array
+   */
+  public $entityReferenceFields = array();
+
   /**
    * constants for attributes for various form elements
    * attempt to standardize on the number of variations that we
@@ -208,7 +216,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
    *               These are not default values
    * @param bool   is this a required field
    *
-   * @return object    html element, could be an error object
+   * @return HTML_QuickForm_Element could be an error object
    * @access public
    *
    */
@@ -399,6 +407,8 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     // the user can do both the form and set default values with this hook
     CRM_Utils_Hook::buildForm(get_class($this), $this);
 
+    $this->preprocessReferenceFields();
+
     $this->addRules();
   }
 
@@ -1196,6 +1206,88 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     $this->setDefaults(array($name => $defaultCurrency));
   }
 
+  /**
+   * Create a single or multiple contact ref field
+   * @param string $name
+   * @param string $label
+   * @param array $props mix of html and widget properties, including:
+   *   - required
+   *   - select - params to give to select2, notably "multiple"
+   *   - api - array of data for the api, keys include "entity", "action", "params", "search", "key", "label"
+   * @return HTML_QuickForm_Element
+   */
+  function addContactRef($name, $label, $props = array(), $required = FALSE) {
+    $props['class'] = isset($props['class']) ? $props['class'] . ' crm-select2' : 'crm-select2';
+
+    $props['select'] = CRM_Utils_Array::value('select', $props, array()) + array(
+      'minimumInputLength' => 1,
+      'multiple' => !empty($props['multiple']),
+      'placeholder' => CRM_Utils_Array::value('placeholder', $props, $required ? ts('- select -') : ts('- none -')),
+      'allowClear' => !$required,
+      // Disabled pending https://github.com/ivaynberg/select2/pull/2092
+      //'formatInputTooShort' => ts('Start typing a name or email address...'),
+      //'formatNoMatches' => ts('No contacts found.'),
+    );
+
+    $props['api'] = CRM_Utils_Array::value('api', $props, array()) + array(
+      'entity' => 'contact',
+      'action' => 'getquick',
+      'search' => 'name',
+      'label' => 'data',
+      'key' => 'id',
+    );
+
+    $this->entityReferenceFields[$name] = $props;
+    $this->formatReferenceFieldAttributes($props);
+    return $this->add('text', $name, $label, $props, $required);
+  }
+
+  /**
+   * @param $props
+   */
+  private function formatReferenceFieldAttributes(&$props) {
+    $props['data-select-params'] = json_encode($props['select']);
+    $props['data-api-params'] = json_encode($props['api']);
+    CRM_Utils_Array::remove($props, 'multiple', 'select', 'api');
+  }
+
+  private function preprocessReferenceFields() {
+    foreach ($this->entityReferenceFields as $name => $props) {
+      $val = $this->getElementValue($name);
+      $field = $this->getElement($name);
+      // Support array values
+      if (is_array($val)) {
+        $val = implode(',', $val);
+        $field->setValue($val);
+      }
+      if ($val) {
+        $data = $labels = array();
+        // Support serialized values
+        if (strpos($val, CRM_Core_DAO::VALUE_SEPARATOR) !== FALSE) {
+          $val = str_replace(CRM_Core_DAO::VALUE_SEPARATOR, ',', trim($val, CRM_Core_DAO::VALUE_SEPARATOR));
+          $field->setValue($val);
+        }
+        foreach (explode(',', $val) as $v) {
+          $result = civicrm_api3($props['api']['entity'], $props['api']['action'], array('sequential' => 1, $props['api']['key'] => $v));
+          if (!empty($result['values'])) {
+            $data[] = array('id' => $v, 'text' => $result['values'][0][$props['api']['label']]);
+            $labels[] = $result['values'][0][$props['api']['label']];
+          }
+        }
+        if ($field->isFrozen()) {
+          $field->removeAttribute('class');
+          $field->setValue(implode(', ', $labels));
+        }
+        elseif ($data) {
+          if (empty($props['select']['multiple'])) {
+            $data = $data[0];
+          }
+          $field->setAttribute('data-entity-value', json_encode($data));
+        }
+      }
+    }
+  }
+
   /**
    * Convert all date fields within the params to mysql date ready for the
    * BAO layer. In this case fields are checked against the $_datefields defined for the form
index 14a2c1d23abad8b1e963e46c5a4c9d92a1fe8973..0c0a047c5b6f1fcad590b155003dca1603b21bb4 100644 (file)
@@ -253,14 +253,14 @@ CRM.validate = CRM.validate || {
   functions: []
 };
 
-// Set select2 defaults
-$.fn.select2.defaults.minimumResultsForSearch = 10;
-// https://github.com/ivaynberg/select2/pull/2090
-$.fn.select2.defaults.width = 'resolve';
-
 (function ($, undefined) {
   "use strict";
 
+  // Set select2 defaults
+  $.fn.select2.defaults.minimumResultsForSearch = 10;
+  // https://github.com/ivaynberg/select2/pull/2090
+  $.fn.select2.defaults.width = 'resolve';
+
   // Initialize widgets
   $(document).on('crmLoad', function(e) {
     $('table.row-highlight', e.target)
@@ -284,7 +284,28 @@ $.fn.select2.defaults.width = 'resolve';
         $(this).nextUntil('option[value^=crm_optgroup]').wrapAll('<optgroup label="' + $(this).text() + '" />');
         $(this).remove();
       });
-      var options = $(this).data('select2') || {};
+      var options = $(this).data('select-params') || {};
+      // Api-based searching
+      if ($(this).data('api-params')) {
+        $(this).addClass('crm-ajax-select')
+        options.query = function(info) {
+          var api = $(info.element).data('api-params');
+          var params = api.params || {};
+          params[api.search] = info.term;
+          CRM.api3(api.entity, api.action, params).done(function(data) {
+            var results = {context: info.context, results: []};
+            if (typeof(data.values) === 'object') {
+              $.each(data.values, function(k, v) {
+                results.results.push({id: v[api.key], text: v[api.label]});
+              });
+            }
+            info.callback(results);
+          });
+        };
+        options.initSelection = function(el, callback) {
+          callback(el.data('entity-value'));
+        };
+      }
       $(this).select2(options).removeClass('crm-select2');
     });
   });