CRM-15555 QA fix - ensure email always assigned
[civicrm-core.git] / CRM / Core / Payment / Form.php
index e54150e77d244ab4ad990816afa0625b73afd962..34cff2171afe6a21d28a39a0ccc3d01ee40ecb62 100644 (file)
  */
 class CRM_Core_Payment_Form {
 
+
   /**
-   * Add payment fields are depending on payment type
+   * Add payment fields are depending on payment processor
    *
-   * @param int $type eg CRM_Core_Payment::PAYMENT_TYPE_DIRECT_DEBIT
-   * @param CRM_Core_Form $form
+   * @param CRM_Contribute_Form_Contribution| CRM_Contribute_Form_Contribution_Main $form
+   * @todo - add other forms specifically to the definition - since we don't have for $form - since we aren't adding the property to
+   * CRM_Core_Form (don't suppose we should?)
+   * @param array $processor array of properties including 'object' as loaded from CRM_Financial_BAO_PaymentProcessor::getPaymentProcessors
    */
-  static public function setPaymentFieldsByType($type, &$form) {
-    if ($type & CRM_Core_Payment::PAYMENT_TYPE_DIRECT_DEBIT) {
-      CRM_Core_Payment_Form::setDirectDebitFields($form);
-    }
-    else {
-      CRM_Core_Payment_Form::setCreditCardFields($form);
+  static public function setPaymentFieldsByProcessor(&$form, $processor) {
+    $form->billingFieldSets = array();
+    $paymentFields = self::getPaymentFields($processor);
+    $paymentTypeName = self::getPaymentTypeName($processor);
+    $paymentTypeLabel = self::getPaymentTypeLabel($processor);
+    //@todo if we switch to iterating through the fieldsets we won't need to assign these directly
+    $form->assign('paymentTypeName', $paymentTypeName);
+    $form->assign('paymentTypeLabel', $paymentTypeLabel);
+
+    $form->billingFieldSets[$paymentTypeName]['fields'] = $form->_paymentFields = array_intersect_key(self::getPaymentFieldMetadata($processor), array_flip($paymentFields));
+    $form->billingPane = array($paymentTypeName => $paymentTypeLabel);
+    $form->assign('paymentFields', $paymentFields);
+    if ($processor['billing_mode'] != 4) {
+      //@todo setPaymentFields defines the billing fields - this should be moved to the processor class & renamed getBillingFields
+      // currently we just add the standard lot unless it's an off-site processor (& then add none)
+      //also set the billingFieldSet to hold all the details required to render the fieldset so we can iterate through the fieldset - making
+      // it easier to re-order. For not the billingFieldSets param is used to determine whether to show the billing pane
+      CRM_Core_Payment_Form::_setPaymentFields($form);
+      $form->billingFieldSets['billing_name_address-group']['fields'] = array();
     }
   }
 
   /**
-   * create all common fields needed for a credit card or direct debit transaction
+   * add general billing fields
+   * @todo set these like processor fields & let payment processors alter them
+   *
    *
    * @param $form
    *
@@ -106,14 +124,11 @@ class CRM_Core_Payment_Form {
     );
 
     $form->_paymentFields["billing_state_province_id-{$bltID}"] = array(
-      'htmlType' => 'select',
+      'htmlType' => 'chainSelect',
+      'title' => ts('State/Province'),
       'name' => "billing_state_province_id-{$bltID}",
-      'title' => ts('State / Province'),
       'cc_field' => TRUE,
-      'attributes' => array(
-        '' => ts('- select -')) +
-      CRM_Core_PseudoConstant::stateProvince(),
-      'is_required' => self::checkRequiredStateProvince($form, "billing_country_id-{$bltID}"),
+      'is_required' => TRUE,
     );
 
     $form->_paymentFields["billing_postal_code-{$bltID}"] = array(
@@ -135,12 +150,24 @@ class CRM_Core_Payment_Form {
       CRM_Core_PseudoConstant::country(),
       'is_required' => TRUE,
     );
+    //CRM-15509 working towards giving control over billing fields to payment processors. For now removing tpl hard-coding
+    $smarty = CRM_Core_Smarty::singleton();
+    $smarty->assign('billingDetailsFields', array(
+      'billing_first_name',
+      'billing_middle_name',
+      'billing_last_name',
+      "billing_street_address-{$bltID}",
+      "billing_city-{$bltID}",
+      "billing_country_id-{$bltID}",
+      "billing_state_province_id-{$bltID}",
+      "billing_postal_code-{$bltID}",
+    ));
   }
 
   /**
    * create all fields needed for a credit card transaction
-   *
-   * @param $form
+   * @deprecated  - use the setPaymentFieldsByProcessor which leverages the processor to determine the fields
+   * @param CRM_Core_Form $form
    *
    * @return void
    * @access public
@@ -188,11 +215,40 @@ class CRM_Core_Payment_Form {
       'attributes' => $creditCardType,
       'is_required' => FALSE,
     );
+    //CRM-15509 this is probably a temporary resting place for these form assignments but we are working towards putting this
+    // in an option group & having php / payment processors define the billing form rather than the tpl
+    $smarty = CRM_Core_Smarty::singleton();
+    $smarty->assign('paymentTypeName', 'credit_card');
+    $smarty->assign('paymentTypeLabel', ts('Credit Card Information'));
+    $smarty->assign('paymentFields', self::getPaymentFields($form->_paymentProcessor));
+  }
+
+  /**
+   * @param CRM_Core_Form $form
+   * @param bool $useRequired
+   * @param array $paymentFields
+   */
+  protected static function addCommonFields(&$form, $useRequired, $paymentFields) {
+    foreach ($paymentFields as $name => $field) {
+      if (!empty($field['cc_field'])) {
+        if ($field['htmlType'] == 'chainSelect') {
+          $form->addChainSelect($field['name'], array('required' => $useRequired && $field['is_required']));
+        }
+        else {
+          $form->add($field['htmlType'],
+            $field['name'],
+            $field['title'],
+            $field['attributes'],
+            $useRequired ? $field['is_required'] : FALSE
+          );
+        }
+      }
+    }
   }
 
   /**
    * create all fields needed for direct debit transaction
-   *
+   * @deprecated  - use the setPaymentFieldsByProcessor which leverages the processor to determine the fields
    * @param $form
    *
    * @return void
@@ -238,11 +294,112 @@ class CRM_Core_Payment_Form {
       'attributes' => array('size' => 20, 'maxlength' => 64, 'autocomplete' => 'off'),
       'is_required' => TRUE,
     );
+    //CRM-15509 this is probably a temporary resting place for these form assignments but we are working towards putting this
+    // in an option group & having php / payment processors define the billing form rather than the tpl
+    $smarty = CRM_Core_Smarty::singleton();
+    // replace these payment type names with an option group - moving name & label assumptions out of the tpl is a step towards that
+    $smarty->assign('paymentTypeName', 'direct_debit');
+    $smarty->assign('paymentTypeLabel', ts('Direct Debit Information'));
+    $smarty->assign('paymentFields', self::getPaymentFields($form->_paymentProcessor));
   }
 
   /**
-   * Function to add all the credit card fields
+   * @param array $paymentProcessor
+   * @todo it will be necessary to set details that affect it - mostly likely take Country as a param. Should we add generic
+   * setParams on processor class or just setCountry which we know we need?
+   *
+   * @return array
+   */
+  static function getPaymentFields($paymentProcessor) {
+    $paymentProcessorObject = CRM_Core_Payment::singleton(($paymentProcessor['is_test'] ? 'test' : 'live'), $paymentProcessor);
+    return $paymentProcessorObject->getPaymentFormFields();
+  }
+
+  /**
+   * @param array $paymentProcessor
+   *
+   * @return array
+   */
+  static function getPaymentFieldMetadata($paymentProcessor) {
+    $paymentProcessorObject = CRM_Core_Payment::singleton(($paymentProcessor['is_test'] ? 'test' : 'live'), $paymentProcessor);
+    return $paymentProcessorObject->getPaymentFormFieldsMetadata();
+  }
+
+  /**
+   * @param array $paymentProcessor
+   *
+   * @return string
+   */
+  static function getPaymentTypeName($paymentProcessor) {
+    $paymentProcessorObject = CRM_Core_Payment::singleton(($paymentProcessor['is_test'] ? 'test' : 'live'), $paymentProcessor);
+    return $paymentProcessorObject->getPaymentTypeName();
+  }
+
+  /**
+   * @param array $paymentProcessor
+   *
+   * @return string
+   */
+  static function getPaymentTypeLabel($paymentProcessor) {
+    $paymentProcessorObject = CRM_Core_Payment::singleton(($paymentProcessor['is_test'] ? 'test' : 'live'), $paymentProcessor);
+    return ts(($paymentProcessorObject->getPaymentTypeLabel()) . ' Information');
+  }
+
+  /**
+   * @param CRM_Contribute_Form_Contribution| CRM_Contribute_Form_Contribution_Main|CRM_Member_Form_Membership $form
+   * @param array $processor array of properties including 'object' as loaded from CRM_Financial_BAO_PaymentProcessor::getPaymentProcessors
+   * @param bool $isBillingDataOptional
    *
+   * @return bool
+   */
+  static function buildPaymentForm($form, $processor, $isBillingDataOptional){
+    self::setPaymentFieldsByProcessor($form, $processor);
+    self::addCommonFields($form, !$isBillingDataOptional, $form->_paymentFields);
+    self::addRules($form, $form->_paymentFields);
+    self::addPaypalExpressCode($form);
+    return (!empty($form->_paymentFields));
+  }
+
+  /**
+   * @param CRM_Core_Form $form
+   * @param array $paymentFields array of properties including 'object' as loaded from CRM_Financial_BAO_PaymentProcessor::getPaymentProcessors
+
+   * @param $paymentFields
+   */
+  protected static function addRules(&$form, $paymentFields) {
+    foreach ($paymentFields as $paymentField => $fieldSpecs) {
+      if (!empty($fieldSpecs['rules'])) {
+        foreach ($fieldSpecs['rules'] as $rule) {
+          $form->addRule($paymentField,
+            $rule['rule_message'],
+            $rule['rule_name'],
+            $rule['rule_parameters']
+          );
+        }
+      }
+    }
+  }
+
+  /**
+   * billing mode button is basically synonymous with paypal express  - this is probably a good example of 'odds & sods' code we
+   * need to find a way for the payment processor to assign. A tricky aspect is that the payment processor may need to set the order
+   *
+   * @param $form
+   */
+  protected static function addPaypalExpressCode(&$form) {
+    if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON) {
+      $form->_expressButtonName = $form->getButtonName('upload', 'express');
+      $form->assign('expressButtonName', $form->_expressButtonName);
+      $form->add('image',
+        $form->_expressButtonName,
+        $form->_paymentProcessor['url_button'],
+        array('class' => 'crm-form-submit')
+      );
+    }
+  }
+  /**
+   * Function to add all the credit card fields
+   * @deprecated Use BuildPaymentForm
    * @param $form
    * @param bool $useRequired
    *
@@ -252,18 +409,7 @@ class CRM_Core_Payment_Form {
   static function buildCreditCard(&$form, $useRequired = FALSE) {
     if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_FORM) {
       self::setCreditCardFields($form);
-      foreach ($form->_paymentFields as $name => $field) {
-        if (isset($field['cc_field']) &&
-          $field['cc_field']
-        ) {
-          $form->add($field['htmlType'],
-            $field['name'],
-            $field['title'],
-            $field['attributes'],
-            $useRequired ? $field['is_required'] : FALSE
-          );
-        }
-      }
+      self::addCommonFields($form, $useRequired, $form->_paymentFields);
 
       $form->addRule('cvv2',
         ts('Please enter a valid value for your card security code. This is usually the last 3-4 digits on the card\'s signature panel.'),
@@ -275,21 +421,16 @@ class CRM_Core_Payment_Form {
         'currentDate', TRUE
       );
 
-      // also take care of state country widget
-      $stateCountryMap = array(
-        1 => array('country' => "billing_country_id-{$form->_bltID}",
-          'state_province' => "billing_state_province_id-{$form->_bltID}",
-        ));
-      CRM_Core_BAO_Address::addStateCountryMap($stateCountryMap);
     }
 
+
     if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON) {
       $form->_expressButtonName = $form->getButtonName('upload', 'express');
       $form->assign('expressButtonName', $form->_expressButtonName);
       $form->add('image',
         $form->_expressButtonName,
         $form->_paymentProcessor['url_button'],
-        array('class' => 'form-submit')
+        array('class' => 'crm-form-submit')
       );
     }
   }
@@ -314,6 +455,7 @@ class CRM_Core_Payment_Form {
 
   /**
    * Function to add all the direct debit fields
+   * @deprecated use buildPaymentForm
    *
    * @param $form
    * @param bool $useRequired
@@ -323,18 +465,7 @@ class CRM_Core_Payment_Form {
   static function buildDirectDebit(&$form, $useRequired = FALSE) {
     if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_FORM) {
       self::setDirectDebitFields($form);
-      foreach ($form->_paymentFields as $name => $field) {
-        if (isset($field['cc_field']) &&
-          $field['cc_field']
-        ) {
-          $form->add($field['htmlType'],
-            $field['name'],
-            $field['title'],
-            $field['attributes'],
-            $useRequired ? $field['is_required'] : FALSE
-          );
-        }
-      }
+      self::addCommonFields($form, $useRequired, $form->_paymentFields);
 
       $form->addRule('bank_identification_number',
         ts('Please enter a valid Bank Identification Number (value must not contain punctuation characters).'),
@@ -346,15 +477,6 @@ class CRM_Core_Payment_Form {
         'nopunctuation'
       );
     }
-
-    if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON) {
-      $form->_expressButtonName = $form->getButtonName($form->buttonType(), 'express');
-      $form->add('image',
-        $form->_expressButtonName,
-        $form->_paymentProcessor['url_button'],
-        array('class' => 'form-submit')
-      );
-    }
   }
 
   /**
@@ -476,7 +598,7 @@ class CRM_Core_Payment_Form {
   /**
    * function to get the credit card expiration year
    * The date format for this field should typically be "M Y" (ex: Feb 2011) or "m Y" (02 2011)
-   * This function exists only to make it consistant with getCreditCardExpirationMonth
+   * This function exists only to make it consistent with getCreditCardExpirationMonth
    *
    * @param $src
    *
@@ -486,49 +608,4 @@ class CRM_Core_Payment_Form {
   static function getCreditCardExpirationYear($src) {
     return CRM_Utils_Array::value('Y', $src['credit_card_exp_date']);
   }
-
-  /**
-   * function to return state/province is_required = true/false
-   *
-   * @param obj     $form: Form object
-   * @param string  $name: Country index name on $_submitValues array
-   * @param bool    $onBehalf: Is 'On Behalf Of' profile?
-   *
-   * @return bool
-   *   TRUE/FALSE for is_required if country consist/not consist of state/province respectively
-   * @static
-   */
-  static function checkRequiredStateProvince($form, $name, $onBehalf = FALSE) {
-    // If selected country has possible values for state/province mark the
-    // state/province field as required.
-    $config = CRM_Core_Config::singleton();
-    $stateProvince = new CRM_Core_DAO_StateProvince();
-
-    if ($onBehalf) {
-      $stateProvince->country_id = CRM_Utils_Array::value($name, $form->_submitValues['onbehalf']);
-    }
-    else {
-      $stateProvince->country_id = CRM_Utils_Array::value($name, $form->_submitValues);
-    }
-
-    $limitCountryId = $stateProvince->country_id;
-
-    if ($stateProvince->count() > 0) {
-      // check that the state/province data is not excluded by a
-      // limitation in the localisation settings.
-      $countryIsoCodes = CRM_Core_PseudoConstant::countryIsoCode();
-      $limitCodes      = $config->provinceLimit();
-      $limitIds        = array();
-      foreach ($limitCodes as $code) {
-        $limitIds = array_merge($limitIds, array_keys($countryIsoCodes, $code));
-      }
-
-      if ($limitCountryId && in_array($limitCountryId, $limitIds)) {
-        return TRUE;
-      }
-      return FALSE;
-    }
-    return FALSE;
-  }
 }
-