Merge pull request #5967 from colemanw/CRM-16632
[civicrm-core.git] / CRM / Core / Form.php
index b62ce0277ac259aba68049bd4ffbfb17e5e0cec5..76cc6ba2d1047fb2ab5c8114dcc5993c4d12ae4a 100644 (file)
@@ -3,7 +3,7 @@
  +--------------------------------------------------------------------+
  | CiviCRM version 4.6                                                |
  +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC (c) 2004-2014                                |
+ | Copyright CiviCRM LLC (c) 2004-2015                                |
  +--------------------------------------------------------------------+
  | This file is a part of CiviCRM.                                    |
  |                                                                    |
@@ -23,7 +23,7 @@
  | GNU Affero General Public License or the licensing of CiviCRM,     |
  | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
  +--------------------------------------------------------------------+
-*/
+ */
 
 /**
  * This is our base form. It is part of the Form/Controller/StateMachine
@@ -31,7 +31,7 @@
  * machine. Each form can also operate in various modes
  *
  * @package CRM
- * @copyright CiviCRM LLC (c) 2004-2014
+ * @copyright CiviCRM LLC (c) 2004-2015
  * $Id$
  *
  */
@@ -152,7 +152,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   private $_chainSelectFields = array();
 
   /**
-   * Constructor for the basic form page
+   * Constructor for the basic form page.
    *
    * We should not use QuickForm directly. This class provides a lot
    * of default convenient functions, rules and buttons
@@ -167,7 +167,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
    *
    * @return \CRM_Core_Form
    */
-  function __construct(
+  public function __construct(
     $state = NULL,
     $action = CRM_Core_Action::NONE,
     $method = 'post',
@@ -215,7 +215,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Register all the standard rules that most forms potentially use
+   * Register all the standard rules that most forms potentially use.
    *
    * @return void
    */
@@ -267,7 +267,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
    *
    * @return HTML_QuickForm_Element could be an error object
    */
-  function &add(
+  public function &add(
     $type, $name, $label = '',
     $attributes = '', $required = FALSE, $extra = NULL
   ) {
@@ -348,7 +348,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     // Respond with JSON if in AJAX context (also support legacy value '6')
     if ($allowAjax && !empty($_REQUEST['snippet']) && in_array($_REQUEST['snippet'], array(
           CRM_Core_Smarty::PRINT_JSON,
-          6
+          6,
         ))
     ) {
       $this->ajaxResponse['buttonName'] = str_replace('_qf_' . $this->getAttribute('id') . '_', '', $this->controller->getButtonName());
@@ -361,7 +361,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * The postProcess hook is typically called by the framework
+   * The postProcess hook is typically called by the framework.
    * However in a few cases, the form exits or redirects early in which
    * case it needs to call this function so other modules can do the needful
    * Calling this function directly should be avoided if possible. In general a
@@ -388,10 +388,11 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
    *
    * access        public
    *
-   * @return array
+   * @return array|NULL
    *   reference to the array of default values
    */
   public function setDefaultValues() {
+    return NULL;
   }
 
   /**
@@ -406,9 +407,9 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Performs the server side validation
+   * Performs the server side validation.
    * @since     1.0
-   * @return boolean
+   * @return bool
    *   true if no error found
    * @throws    HTML_QuickForm_Error
    */
@@ -501,10 +502,11 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   /**
    * Add default Next / Back buttons
    *
-   * @param array array of associative arrays in the order in which the buttons should be
-   *                displayed. The associate array has 3 fields: 'type', 'name' and 'isDefault'
-   *                The base form class will define a bunch of static arrays for commonly used
-   *                formats
+   * @param array $params
+   *   Array of associative arrays in the order in which the buttons should be
+   *   displayed. The associate array has 3 fields: 'type', 'name' and 'isDefault'
+   *   The base form class will define a bunch of static arrays for commonly used
+   *   formats.
    *
    * @return void
    */
@@ -513,6 +515,10 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     foreach ($params as $button) {
       $attrs = array('class' => 'crm-form-submit') + (array) CRM_Utils_Array::value('js', $button);
 
+      if (!empty($button['class'])) {
+        $attrs['class'] .= ' ' . $button['class'];
+      }
+
       if (!empty($button['isDefault'])) {
         $attrs['class'] .= ' default';
       }
@@ -569,7 +575,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Getter function for Name
+   * Getter function for Name.
    *
    * @return string
    */
@@ -578,7 +584,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Getter function for State
+   * Getter function for State.
    *
    * @return object
    */
@@ -587,7 +593,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Getter function for StateType
+   * Getter function for StateType.
    *
    * @return int
    */
@@ -617,9 +623,9 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Setter function for options
+   * Setter function for options.
    *
-   * @param mixed
+   * @param mixed $options
    *
    * @return void
    */
@@ -640,16 +646,16 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Boolean function to determine if this is a one form page
+   * Boolean function to determine if this is a one form page.
    *
-   * @return boolean
+   * @return bool
    */
   public function isSimpleForm() {
     return $this->_state->getType() & (CRM_Core_State::START | CRM_Core_State::FINISH);
   }
 
   /**
-   * Getter function for Form Action
+   * Getter function for Form Action.
    *
    * @return string
    */
@@ -658,9 +664,9 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Setter function for Form Action
+   * Setter function for Form Action.
    *
-   * @param string
+   * @param string $action
    *
    * @return void
    */
@@ -669,7 +675,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Render form and return contents
+   * Render form and return contents.
    *
    * @return string
    */
@@ -698,7 +704,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Use the form name to create the tpl file name
+   * Use the form name to create the tpl file name.
    *
    * @return string
    */
@@ -709,10 +715,13 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
       $tplname = $ext->getTemplatePath(CRM_Utils_System::getClassName($this)) . DIRECTORY_SEPARATOR . $filename;
     }
     else {
-      $tplname = str_replace('_',
-          DIRECTORY_SEPARATOR,
-          CRM_Utils_System::getClassName($this)
-        ) . '.tpl';
+      $tplname = strtr(
+        CRM_Utils_System::getClassName($this),
+        array(
+          '_' => DIRECTORY_SEPARATOR,
+          '\\' => DIRECTORY_SEPARATOR,
+        )
+      ) . '.tpl';
     }
     return $tplname;
   }
@@ -738,7 +747,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Error reporting mechanism
+   * Error reporting mechanism.
    *
    * @param string $message
    *   Error Message.
@@ -760,11 +769,12 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Store the variable with the value in the form scope
-   *
-   * @param string name : name of the variable
-   * @param mixed value : value of the variable
+   * Store the variable with the value in the form scope.
    *
+   * @param string $name
+   *   Name of the variable.
+   * @param mixed $value
+   *   Value of the variable.
    *
    * @return void
    */
@@ -773,10 +783,10 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Get the variable from the form scope
-   *
-   * @param string name : name of the variable
+   * Get the variable from the form scope.
    *
+   * @param string $name
+   *   Name of the variable
    *
    * @return mixed
    */
@@ -785,7 +795,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Getter for action
+   * Getter for action.
    *
    * @return int
    */
@@ -794,7 +804,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Setter for action
+   * Setter for action.
    *
    * @param int $action
    *   The mode we want to set the form.
@@ -806,7 +816,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Assign value to name in template
+   * Assign value to name in template.
    *
    * @param string $var
    *   Name of variable.
@@ -820,7 +830,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Assign value to name in template by reference
+   * Assign value to name in template by reference.
    *
    * @param string $var
    *   Name of variable.
@@ -834,7 +844,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Appends values to template variables
+   * Appends values to template variables.
    *
    * @param array|string $tpl_var the template variable name(s)
    * @param mixed $value
@@ -846,7 +856,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Returns an array containing template variables
+   * Returns an array containing template variables.
    *
    * @param string $name
    *
@@ -871,7 +881,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
     $attributes = $attributes ? $attributes : array();
     $allowClear = !empty($attributes['allowClear']);
     unset($attributes['allowClear']);
-    $attributes += array('id_suffix' => $name);
+    $attributes['id_suffix'] = $name;
     foreach ($values as $key => $var) {
       $options[] = $this->createElement('radio', NULL, NULL, $var, $key, $attributes);
     }
@@ -918,7 +928,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
    * @param string $separator
    * @param bool $flipValues
    */
-  function addCheckBox(
+  public function addCheckBox(
     $id, $title, $values, $other = NULL,
     $attributes = NULL, $required = NULL,
     $javascriptMethod = NULL,
@@ -1022,7 +1032,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Adds a select based on field metadata
+   * 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
@@ -1173,7 +1183,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   public function addCountry($id, $title, $required = NULL, $extra = NULL) {
     $this->addElement('select', $id, $title,
       array(
-        '' => ts('- select -')
+        '' => ts('- select -'),
       ) + CRM_Core_PseudoConstant::country(), $extra
     );
     if ($required) {
@@ -1275,21 +1285,24 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Add date
-   * @param string $name
-   *   Name of the element.
-   * @param string $label
-   *   Label of the element.
-   * @param array $attributes
-   *   Key / value pair.
+   * Add date.
    *
+   * @code
    * // if you need time
    * $attributes = array(
    *   'addTime' => true,
    *   'formatType' => 'relative' or 'birth' etc check advanced date settings
    * );
+   * @endcode
+   *
+   * @param string $name
+   *   Name of the element.
+   * @param string $label
+   *   Label of the element.
    * @param bool $required
    *   True if required.
+   * @param array $attributes
+   *   Key / value pair.
    */
   public function addDate($name, $label, $required = FALSE, $attributes = NULL) {
     if (!empty($attributes['formatType'])) {
@@ -1370,7 +1383,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   *  Function that will add date and time
+   *  Function that will add date and time.
    */
   public function addDateTime($name, $label, $required = FALSE, $attributes = NULL) {
     $addTime = array('addTime' => TRUE);
@@ -1385,9 +1398,9 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Add a currency and money element to the form
+   * Add a currency and money element to the form.
    */
-  function addMoney(
+  public function addMoney(
     $name,
     $label,
     $required = FALSE,
@@ -1408,9 +1421,9 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Add currency element to the form
+   * Add currency element to the form.
    */
-  function addCurrency(
+  public function addCurrency(
     $name = 'currency',
     $label = NULL,
     $required = TRUE,
@@ -1435,7 +1448,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Create a single or multiple entity ref field
+   * Create a single or multiple entity ref field.
    * @param string $name
    * @param string $label
    * @param array $props
@@ -1539,14 +1552,14 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Function that can be defined in Form to override or
+   * Function that can be defined in Form to override or.
    * perform specific action on cancel action
    */
   public function cancelAction() {
   }
 
   /**
-   * Helper function to verify that required fields have been filled
+   * Helper function to verify that required fields have been filled.
    * Typically called within the scope of a FormRule function
    */
   public static function validateMandatoryFields($fields, $values, &$errors) {
@@ -1566,7 +1579,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
    *   - cid from the url if the caller has ACL permission to view
    *   - fallback is logged in user (or ? NULL if no logged in user) (@todo wouldn't 0 be more intuitive?)
    *
-   * @return NULL|integer
+   * @return NULL|int
    */
   public function getContactID() {
     $tempID = CRM_Utils_Request::retrieve('cid', 'Positive', $this);
@@ -1585,13 +1598,13 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
       // 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;
+      return (int) $tempID;
     }
 
     $userID = $this->getLoggedInUserContactID();
 
-    if ($tempID == $userID) {
-      return $userID;
+    if (!is_null($tempID) && $tempID === $userID) {
+      return (int) $userID;
     }
 
     //check if this is a checksum authentication
@@ -1608,11 +1621,11 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
       return $tempID;
     }
 
-    return $userID;
+    return is_numeric($userID) ? $userID : NULL;
   }
 
   /**
-   * Get the contact id of the logged in user
+   * Get the contact id of the logged in user.
    */
   public function getLoggedInUserContactID() {
     // check if the user is logged in and has a contact ID
@@ -1655,7 +1668,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
       $this->assign('selectable', $autoCompleteField['id_field']);
       $this->addEntityRef($autoCompleteField['id_field'], NULL, array(
           'placeholder' => $autoCompleteField['placeholder'],
-          'api' => $autoCompleteField['api']
+          'api' => $autoCompleteField['api'],
         ));
 
       CRM_Core_Resources::singleton()->addScriptFile('civicrm', 'js/AlternateContactSelector.js', 1, 'html-header')
@@ -1683,6 +1696,8 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
    * that small pieces of duplication are not being refactored into separate functions because their only shared parent
    * is this form. Inserting a class FrontEndForm.php between the contribution & event & this class would allow functions like this
    * and a dozen other small ones to be refactored into a shared parent with the reduction of much code duplication
+   *
+   * @param $onlinePaymentProcessorEnabled
    */
   public function addCIDZeroOptions($onlinePaymentProcessorEnabled) {
     $this->assign('nocid', TRUE);
@@ -1726,7 +1741,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Sets form attribute
+   * Sets form attribute.
    * @see CRM.loadForm
    */
   public function preventAjaxSubmit() {
@@ -1734,7 +1749,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Sets form attribute
+   * Sets form attribute.
    * @see CRM.loadForm
    */
   public function allowAjaxSubmit() {
@@ -1742,7 +1757,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
   }
 
   /**
-   * Sets page title based on entity and action
+   * Sets page title based on entity and action.
    * @param string $entityLabel
    */
   public function setPageTitle($entityLabel) {
@@ -1780,7 +1795,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
           'country',
           'Country',
           'state_province',
-          'StateProvince'
+          'StateProvince',
         ), $elementName),
       'data-callback' => strpos($elementName, 'rovince') ? 'civicrm/ajax/jqState' : 'civicrm/ajax/jqCounty',
       'label' => strpos($elementName, 'rovince') ? ts('State/Province') : ts('County'),
@@ -1809,43 +1824,46 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
    */
   private function preProcessChainSelectFields() {
     foreach ($this->_chainSelectFields as $control => $target) {
-      $targetField = $this->getElement($target);
-      $targetType = $targetField->getAttribute('data-callback') == 'civicrm/ajax/jqCounty' ? 'county' : 'stateProvince';
-      $options = array();
-      // If the control field is on the form, setup chain-select and dynamically populate options
-      if ($this->elementExists($control)) {
-        $controlField = $this->getElement($control);
-        $controlType = $targetType == 'county' ? 'stateProvince' : 'country';
-
-        $targetField->setAttribute('class', $targetField->getAttribute('class') . ' crm-chain-select-target');
-
-        $css = (string) $controlField->getAttribute('class');
-        $controlField->updateAttributes(array(
-          'class' => ($css ? "$css " : 'crm-select2 ') . 'crm-chain-select-control',
-          'data-target' => $target,
-        ));
-        $controlValue = $controlField->getValue();
-        if ($controlValue) {
-          $options = CRM_Core_BAO_Location::getChainSelectValues($controlValue, $controlType, TRUE);
-          if (!$options) {
-            $targetField->setAttribute('placeholder', $targetField->getAttribute('data-none-prompt'));
+      // The 'target' might get missing if extensions do removeElement() in a form hook.
+      if ($this->elementExists($target)) {
+        $targetField = $this->getElement($target);
+        $targetType = $targetField->getAttribute('data-callback') == 'civicrm/ajax/jqCounty' ? 'county' : 'stateProvince';
+        $options = array();
+        // If the control field is on the form, setup chain-select and dynamically populate options
+        if ($this->elementExists($control)) {
+          $controlField = $this->getElement($control);
+          $controlType = $targetType == 'county' ? 'stateProvince' : 'country';
+
+          $targetField->setAttribute('class', $targetField->getAttribute('class') . ' crm-chain-select-target');
+
+          $css = (string) $controlField->getAttribute('class');
+          $controlField->updateAttributes(array(
+            'class' => ($css ? "$css " : 'crm-select2 ') . 'crm-chain-select-control',
+            'data-target' => $target,
+          ));
+          $controlValue = $controlField->getValue();
+          if ($controlValue) {
+            $options = CRM_Core_BAO_Location::getChainSelectValues($controlValue, $controlType, TRUE);
+            if (!$options) {
+              $targetField->setAttribute('placeholder', $targetField->getAttribute('data-none-prompt'));
+            }
+          }
+          else {
+            $targetField->setAttribute('placeholder', $targetField->getAttribute('data-empty-prompt'));
+            $targetField->setAttribute('disabled', 'disabled');
           }
         }
+        // Control field not present - fall back to loading default options
         else {
-          $targetField->setAttribute('placeholder', $targetField->getAttribute('data-empty-prompt'));
-          $targetField->setAttribute('disabled', 'disabled');
+          $options = CRM_Core_PseudoConstant::$targetType();
         }
+        if (!$targetField->getAttribute('multiple')) {
+          $options = array('' => $targetField->getAttribute('placeholder')) + $options;
+          $targetField->removeAttribute('placeholder');
+        }
+        $targetField->_options = array();
+        $targetField->loadArray($options);
       }
-      // Control field not present - fall back to loading default options
-      else {
-        $options = CRM_Core_PseudoConstant::$targetType();
-      }
-      if (!$targetField->getAttribute('multiple')) {
-        $options = array('' => $targetField->getAttribute('placeholder')) + $options;
-        $targetField->removeAttribute('placeholder');
-      }
-      $targetField->_options = array();
-      $targetField->loadArray($options);
     }
   }
 
@@ -1854,7 +1872,7 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
    */
   private function validateChainSelectFields() {
     foreach ($this->_chainSelectFields as $control => $target) {
-      if ($this->elementExists($control)) {
+      if ($this->elementExists($control) && $this->elementExists($target)) {
         $controlValue = (array) $this->getElementValue($control);
         $targetField = $this->getElement($target);
         $controlType = $targetField->getAttribute('data-callback') == 'civicrm/ajax/jqCounty' ? 'stateProvince' : 'country';
@@ -1873,4 +1891,5 @@ class CRM_Core_Form extends HTML_QuickForm_Page {
       }
     }
   }
+
 }