Merge pull request #2602 from pratik-joshi/CRM-13964-qa
[civicrm-core.git] / CRM / Core / Form.php
index 3319f2c4b85af57f3639d5684c563d6b5ab33405..7c069c95f39a9b4b43788e3342e5bd0d7b4e3711 100644 (file)
@@ -105,6 +105,13 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
    */
   public $ajaxResponse = array();
 
+  /**
+   * Url path used to reach this page
+   *
+   * @var array
+   */
+  public $urlPath = array();
+
   /**
    * constants for attributes for various form elements
    * attempt to standardize on the number of variations that we
@@ -208,7 +215,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
    *
    */
@@ -436,7 +443,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
         $prevnext[] = $this->createElement($button['type'], 'reset', $button['name'], $attrs);
       }
       else {
-        if (CRM_Utils_Array::value('subName', $button)) {
+        if (!empty($button['subName'])) {
           $buttonName = $this->getButtonName($button['type'], $button['subName']);
         }
         else {
@@ -449,7 +456,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
         }
         $prevnext[] = $this->createElement('submit', $buttonName, $button['name'], $attrs);
       }
-      if (CRM_Utils_Array::value('isDefault', $button)) {
+      if (!empty($button['isDefault'])) {
         $this->setDefaultAction($button['type']);
       }
 
@@ -763,14 +770,12 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     return self::$_template->get_template_vars($name);
   }
 
-  function &addRadio($name, $title, &$values, $attributes = NULL, $separator = NULL, $required = FALSE) {
+  function &addRadio($name, $title, $values, $attributes = array(), $separator = NULL, $required = FALSE) {
     $options = array();
-    if (empty($attributes)) {
-      $attributes = array('id_suffix' => $name);
-    }
-    else {
-      $attributes = array_merge($attributes, array('id_suffix' => $name));
-    }
+    $attributes = $attributes ? $attributes : array();
+    $unselectable = !empty($attributes['unselectable']);
+    unset($attributes['unselectable']);
+    $attributes += array('id_suffix' => $name);
     foreach ($values as $key => $var) {
       $options[] = $this->createElement('radio', NULL, NULL, $var, $key, $attributes);
     }
@@ -778,24 +783,22 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     if ($required) {
       $this->addRule($name, ts('%1 is a required field.', array(1 => $title)), 'required');
     }
+    if ($unselectable) {
+      $group->setAttribute('unselectable', TRUE);
+    }
     return $group;
   }
 
-  function addYesNo($id, $title, $dontKnow = NULL, $required = NULL, $attribute = NULL) {
-    if (empty($attribute)) {
-      $attribute = array('id_suffix' => $id);
-    }
-    else {
-      $attribute = array_merge($attribute, array('id_suffix' => $id));
-    }
+  function addYesNo($id, $title, $unselectable = FALSE, $required = NULL, $attributes = array()) {
+    $attributes += array('id_suffix' => $id);
     $choice   = array();
-    $choice[] = $this->createElement('radio', NULL, '11', ts('Yes'), '1', $attribute);
-    $choice[] = $this->createElement('radio', NULL, '11', ts('No'), '0', $attribute);
-    if ($dontKnow) {
-      $choice[] = $this->createElement('radio', NULL, '22', ts("Don't Know"), '2', $attribute);
-    }
-    $this->addGroup($choice, $id, $title);
+    $choice[] = $this->createElement('radio', NULL, '11', ts('Yes'), '1', $attributes);
+    $choice[] = $this->createElement('radio', NULL, '11', ts('No'), '0', $attributes);
 
+    $group = $this->addGroup($choice, $id, $title);
+    if ($unselectable) {
+      $group->setAttribute('unselectable', TRUE);
+    }
     if ($required) {
       $this->addRule($id, ts('%1 is a required field.', array(1 => $title)), 'required');
     }
@@ -891,25 +894,65 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     }
   }
 
-  function addSelect($name, $label, $prefix = NULL, $required = NULL, $extra = NULL, $select = '- select -') {
-    if ($prefix) {
-      $this->addElement('select', $name . '_id' . $prefix, $label,
-        array(
-          '' => $select) + CRM_Core_OptionGroup::values($name), $extra
-      );
-      if ($required) {
-        $this->addRule($name . '_id' . $prefix, ts('Please select %1', array(1 => $label)), 'required');
-      }
+  /**
+   * Adds a select based on field metadata
+   * TODO: This could be even more generic and widget type (select in this case) could also be read from metadata
+   * Perhaps a method like $form->bind($name) which would look up all metadata for named field
+   * @param $name - field name to go on the form
+   * @param array $props - mix of html attributes and special properties, namely
+   *   - entity (api entity name, can usually be inferred automatically from the form class)
+   *   - field (field name - only needed if different from name used on the form)
+   *   - option_url - path to edit this option list - usually retrieved automatically - set to NULL to disable link
+   *   - placeholder - set to NULL to disable
+   * @param bool $required
+   * @throws CRM_Core_Exception
+   * @return HTML_QuickForm_Element
+   */
+  function addSelect($name, $props = array(), $required = FALSE) {
+    if (!isset($props['entity'])) {
+      $props['entity'] = CRM_Utils_Api::getEntityName($this);
+    }
+    if (!isset($props['field'])) {
+      $props['field'] = strrpos($name, '[') ? rtrim(substr($name, 1 + strrpos($name, '[')), ']') : $name;
     }
+    $info = civicrm_api3($props['entity'], 'getoptions', array(
+        'field' => $props['field'],
+        'options' => array('metadata' => array('fields'))
+      )
+    );
+    $options = $info['values'];
+    if (!array_key_exists('placeholder', $props)) {
+      $props['placeholder'] = $required ? ts('- select -') : ts('- none -');
+    }
+    if ($props['placeholder'] !== NULL && empty($props['multiple'])) {
+      $options = array('' => '') + $options;
+    }
+    // Handle custom field
+    if (strpos($name, 'custom_') === 0 && is_numeric($name[7])) {
+      list(, $id) = explode('_', $name);
+      $label = isset($props['label']) ? $props['label'] : CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', 'label', $id);
+      $gid = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', 'option_group_id', $id);
+      $props['data-option-edit-path'] = array_key_exists('option_url', $props) ? $props['option_url'] : 'civicrm/admin/options/' . CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $gid);
+    }
+    // Core field
     else {
-      $this->addElement('select', $name . '_id', $label,
-        array(
-          '' => $select) + CRM_Core_OptionGroup::values($name), $extra
-      );
-      if ($required) {
-        $this->addRule($name . '_id', ts('Please select %1', array(1 => $label)), 'required');
+      foreach($info['metadata']['fields'] as $uniqueName => $fieldSpec) {
+        if (
+          $uniqueName === $props['field'] ||
+          CRM_Utils_Array::value('name', $fieldSpec) === $props['field'] ||
+          in_array($props['field'], CRM_Utils_Array::value('api.aliases', $fieldSpec, array()))
+        ) {
+          break;
+        }
       }
-    }
+      $label = isset($props['label']) ? $props['label'] : $fieldSpec['title'];
+      $props['data-option-edit-path'] = array_key_exists('option_url', $props) ? $props['option_url'] : $props['data-option-edit-path'] = CRM_Core_PseudoConstant::getOptionEditUrl($fieldSpec);
+    }
+    $props['class'] = (isset($props['class']) ? $props['class'] . ' ' : '') . "crm-select2";
+    $props['data-api-entity'] = $props['entity'];
+    $props['data-api-field'] = $props['field'];
+    CRM_Utils_Array::remove($props, 'label', 'entity', 'field', 'option_url');
+    return $this->add('select', $name, $label, $options, $required, $props);
   }
 
   /**
@@ -1057,7 +1100,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
    *
    */
   function addDate($name, $label, $required = FALSE, $attributes = NULL) {
-    if (CRM_Utils_Array::value('formatType', $attributes)) {
+    if (!empty($attributes['formatType'])) {
       // get actual format
       $params = array('name' => $attributes['formatType']);
       $values = array();
@@ -1065,7 +1108,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
       // cache date information
       static $dateFormat;
       $key = "dateFormat_" . str_replace(' ', '_', $attributes['formatType']);
-      if (!CRM_Utils_Array::value($key, $dateFormat)) {
+      if (empty($dateFormat[$key])) {
         CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_PreferencesDate', $params, $values);
         $dateFormat[$key] = $values;
       }
@@ -1077,7 +1120,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
         $attributes['format'] = $values['date_format'];
       }
 
-      if (CRM_Utils_Array::value('time_format', $values)) {
+      if (!empty($values['time_format'])) {
         $attributes['timeFormat'] = $values['time_format'];
       }
       $attributes['startOffset'] = $values['start'];
@@ -1085,7 +1128,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     }
 
     $config = CRM_Core_Config::singleton();
-    if (!CRM_Utils_Array::value('format', $attributes)) {
+    if (empty($attributes['format'])) {
       $attributes['format'] = $config->dateInputFormat;
     }
 
@@ -1099,9 +1142,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
 
     $this->add('text', $name, $label, $attributes);
 
-    if (CRM_Utils_Array::value('addTime', $attributes) ||
-      CRM_Utils_Array::value('timeFormat', $attributes)
-    ) {
+    if (!empty($attributes['addTime']) || !empty($attributes['timeFormat'])) {
 
       if (!isset($attributes['timeFormat'])) {
         $timeFormat = $config->timeInputFormat;
@@ -1130,7 +1171,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
 
     if ($required) {
       $this->addRule($name, ts('Please select %1', array(1 => $label)), 'required');
-      if (CRM_Utils_Array::value('addTime', $attributes) && CRM_Utils_Array::value('addTimeRequired', $attributes)) {
+      if (!empty($attributes['addTime']) && !empty($attributes['addTimeRequired'])) {
         $this->addRule($elementName, ts('Please enter a time.'), 'required');
       }
     }
@@ -1183,11 +1224,12 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     $freezeCurrency  = FALSE
   ) {
     $currencies = CRM_Core_OptionGroup::values('currencies_enabled');
+    $options = array('class' => 'crm-select2 eight');
     if (!$required) {
-      $currencies = array(
-        '' => ts('- select -')) + $currencies;
+      $currencies = array('' => '') + $currencies;
+      $options['placeholder'] = ts('- none -');
     }
-    $ele = $this->add('select', $name, $label, $currencies, $required);
+    $ele = $this->add('select', $name, $label, $currencies, $required, $options);
     if ($freezeCurrency) {
       $ele->freeze();
     }
@@ -1198,6 +1240,75 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     $this->setDefaults(array($name => $defaultCurrency));
   }
 
+  /**
+   * Create a single or multiple entity ref field
+   * @param string $name
+   * @param string $label
+   * @param array $props mix of html and widget properties, including:
+   *  - select - params to give to select2 widget
+   *  - entity - defaults to contact
+   *  - create - can the user create a new entity on-the-fly?
+   *             Set to TRUE if entity is contact and you want the default profiles,
+   *             or pass in your own set of links. @see CRM_Core_BAO_UFGroup::getCreateLinks for format
+   *  - api - array of settings for the getlist api
+   *  - placeholder - string
+   *  - multiple - bool
+   *  - class, etc. - other html properties
+   * @param bool $required
+   *
+   * @access public
+   * @return HTML_QuickForm_Element
+   */
+  function addEntityRef($name, $label = '', $props = array(), $required = FALSE) {
+    $config = CRM_Core_Config::singleton();
+    // Default properties
+    $props['api'] = CRM_Utils_Array::value('api', $props, array());
+    $props['entity'] = CRM_Utils_Array::value('entity', $props, 'contact');
+
+    $props['class'] = isset($props['class']) ? $props['class'] . ' ' : '';
+    $props['class'] .= "crm-select2 crm-form-entityref";
+
+    if ($props['entity'] == 'contact' && isset($props['create']) && !(CRM_Core_Permission::check('edit all contacts') || CRM_Core_Permission::check('add contacts'))) {
+      unset($props['create']);
+    }
+    // Convenient shortcut to passing in array create links
+    if ($props['entity'] == 'contact' && isset($props['create']) && $props['create'] === TRUE) {
+      if (empty($props['api']['params']['contact_type'])) {
+        $props['create'] = CRM_Core_BAO_UFGroup::getCreateLinks(array('new_individual', 'new_organization', 'new_household'));
+      }
+      else {
+        $props['create'] = CRM_Core_BAO_UFGroup::getCreateLinks('new_' . strtolower($props['api']['params']['contact_type']));
+      }
+    }
+
+    $defaults = array(
+      'minimumInputLength' => 1,
+      'multiple' => !empty($props['multiple']),
+      'placeholder' => CRM_Utils_Array::value('placeholder', $props, $required ? ts('- select %1 -', array(1 => ts($props['entity']))) : ts('- none -')),
+      'allowClear' => !$required,
+    );
+    if ($props['entity'] == 'contact') {
+      $defaults['formatInputTooShort'] = $config->includeEmailInName ? ts('Start typing a name or email...') : ts('Start typing a name...');
+    }
+    $props['select'] = CRM_Utils_Array::value('select', $props, array()) + $defaults;
+
+    $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'] = $props['api'] ? json_encode($props['api']) : NULL;
+    $props['data-api-entity'] = $props['entity'];
+    if (!empty($props['create'])) {
+      $props['data-create-links'] = json_encode($props['create']);
+    }
+    CRM_Utils_Array::remove($props, 'multiple', 'select', 'api', 'entity', 'placeholder', 'create');
+  }
+
   /**
    * 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
@@ -1280,8 +1391,14 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
       // event form stores as an indexed array, contribution form not so much...
       $tempID = $this->_params[0]['select_contact_id'];
     }
+
     // force to ignore the authenticated user
-    if ($tempID === '0') {
+    if ($tempID === '0' || $tempID === 0) {
+      // we set the cid on the form so that this will be retained for the Confirm page
+      // in the multi-page form & prevent us returning the $userID when this is called
+      // from that page
+      // we don't really need to set it when $tempID is set because the params have that stored
+      $this->set('cid', 0);
       return $tempID;
     }