CRM-15172 - Refactor forms to use addChainSelect method
[civicrm-core.git] / CRM / Core / Payment / Form.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2014
32 * $Id$
33 *
34 */
35 class CRM_Core_Payment_Form {
36
37 /**
38 * Add payment fields are depending on payment type
39 *
40 * @param int $type eg CRM_Core_Payment::PAYMENT_TYPE_DIRECT_DEBIT
41 * @param CRM_Core_Form $form
42 */
43 static public function setPaymentFieldsByType($type, &$form) {
44 if ($type & CRM_Core_Payment::PAYMENT_TYPE_DIRECT_DEBIT) {
45 CRM_Core_Payment_Form::setDirectDebitFields($form);
46 }
47 else {
48 CRM_Core_Payment_Form::setCreditCardFields($form);
49 }
50 }
51
52 /**
53 * create all common fields needed for a credit card or direct debit transaction
54 *
55 * @param $form
56 *
57 * @return void
58 * @access protected
59 */
60 static protected function _setPaymentFields(&$form) {
61 $bltID = $form->_bltID;
62
63 $form->_paymentFields['billing_first_name'] = array(
64 'htmlType' => 'text',
65 'name' => 'billing_first_name',
66 'title' => ts('Billing First Name'),
67 'cc_field' => TRUE,
68 'attributes' => array('size' => 30, 'maxlength' => 60, 'autocomplete' => 'off'),
69 'is_required' => TRUE,
70 );
71
72 $form->_paymentFields['billing_middle_name'] = array(
73 'htmlType' => 'text',
74 'name' => 'billing_middle_name',
75 'title' => ts('Billing Middle Name'),
76 'cc_field' => TRUE,
77 'attributes' => array('size' => 30, 'maxlength' => 60, 'autocomplete' => 'off'),
78 'is_required' => FALSE,
79 );
80
81 $form->_paymentFields['billing_last_name'] = array(
82 'htmlType' => 'text',
83 'name' => 'billing_last_name',
84 'title' => ts('Billing Last Name'),
85 'cc_field' => TRUE,
86 'attributes' => array('size' => 30, 'maxlength' => 60, 'autocomplete' => 'off'),
87 'is_required' => TRUE,
88 );
89
90 $form->_paymentFields["billing_street_address-{$bltID}"] = array(
91 'htmlType' => 'text',
92 'name' => "billing_street_address-{$bltID}",
93 'title' => ts('Street Address'),
94 'cc_field' => TRUE,
95 'attributes' => array('size' => 30, 'maxlength' => 60, 'autocomplete' => 'off'),
96 'is_required' => TRUE,
97 );
98
99 $form->_paymentFields["billing_city-{$bltID}"] = array(
100 'htmlType' => 'text',
101 'name' => "billing_city-{$bltID}",
102 'title' => ts('City'),
103 'cc_field' => TRUE,
104 'attributes' => array('size' => 30, 'maxlength' => 60, 'autocomplete' => 'off'),
105 'is_required' => TRUE,
106 );
107
108 $form->_paymentFields["billing_state_province_id-{$bltID}"] = array(
109 'htmlType' => 'chainSelect',
110 'name' => "billing_state_province_id-{$bltID}",
111 'cc_field' => TRUE,
112 'is_required' => self::checkRequiredStateProvince($form, "billing_country_id-{$bltID}"),
113 );
114
115 $form->_paymentFields["billing_postal_code-{$bltID}"] = array(
116 'htmlType' => 'text',
117 'name' => "billing_postal_code-{$bltID}",
118 'title' => ts('Postal Code'),
119 'cc_field' => TRUE,
120 'attributes' => array('size' => 30, 'maxlength' => 60, 'autocomplete' => 'off'),
121 'is_required' => TRUE,
122 );
123
124 $form->_paymentFields["billing_country_id-{$bltID}"] = array(
125 'htmlType' => 'select',
126 'name' => "billing_country_id-{$bltID}",
127 'title' => ts('Country'),
128 'cc_field' => TRUE,
129 'attributes' => array(
130 '' => ts('- select -')) +
131 CRM_Core_PseudoConstant::country(),
132 'is_required' => TRUE,
133 );
134 }
135
136 /**
137 * create all fields needed for a credit card transaction
138 *
139 * @param $form
140 *
141 * @return void
142 * @access public
143 */
144 static function setCreditCardFields(&$form) {
145 CRM_Core_Payment_Form::_setPaymentFields($form);
146
147 $form->_paymentFields['credit_card_number'] = array(
148 'htmlType' => 'text',
149 'name' => 'credit_card_number',
150 'title' => ts('Card Number'),
151 'cc_field' => TRUE,
152 'attributes' => array('size' => 20, 'maxlength' => 20, 'autocomplete' => 'off'),
153 'is_required' => TRUE,
154 );
155
156 $form->_paymentFields['cvv2'] = array(
157 'htmlType' => 'text',
158 'name' => 'cvv2',
159 'title' => ts('Security Code'),
160 'cc_field' => TRUE,
161 'attributes' => array('size' => 5, 'maxlength' => 10, 'autocomplete' => 'off'),
162 'is_required' => CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME,
163 'cvv_backoffice_required',
164 NULL
165 ,1
166 ),
167 );
168
169 $form->_paymentFields['credit_card_exp_date'] = array(
170 'htmlType' => 'date',
171 'name' => 'credit_card_exp_date',
172 'title' => ts('Expiration Date'),
173 'cc_field' => TRUE,
174 'attributes' => CRM_Core_SelectValues::date('creditCard'),
175 'is_required' => TRUE,
176 );
177
178 $creditCardType = array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::creditCard();
179 $form->_paymentFields['credit_card_type'] = array(
180 'htmlType' => 'select',
181 'name' => 'credit_card_type',
182 'title' => ts('Card Type'),
183 'cc_field' => TRUE,
184 'attributes' => $creditCardType,
185 'is_required' => FALSE,
186 );
187 }
188
189 /**
190 * create all fields needed for direct debit transaction
191 *
192 * @param $form
193 *
194 * @return void
195 * @access public
196 */
197 static function setDirectDebitFields(&$form) {
198 CRM_Core_Payment_Form::_setPaymentFields($form);
199
200 $form->_paymentFields['account_holder'] = array(
201 'htmlType' => 'text',
202 'name' => 'account_holder',
203 'title' => ts('Account Holder'),
204 'cc_field' => TRUE,
205 'attributes' => array('size' => 20, 'maxlength' => 34, 'autocomplete' => 'on'),
206 'is_required' => TRUE,
207 );
208
209 //e.g. IBAN can have maxlength of 34 digits
210 $form->_paymentFields['bank_account_number'] = array(
211 'htmlType' => 'text',
212 'name' => 'bank_account_number',
213 'title' => ts('Bank Account Number'),
214 'cc_field' => TRUE,
215 'attributes' => array('size' => 20, 'maxlength' => 34, 'autocomplete' => 'off'),
216 'is_required' => TRUE,
217 );
218
219 //e.g. SWIFT-BIC can have maxlength of 11 digits
220 $form->_paymentFields['bank_identification_number'] = array(
221 'htmlType' => 'text',
222 'name' => 'bank_identification_number',
223 'title' => ts('Bank Identification Number'),
224 'cc_field' => TRUE,
225 'attributes' => array('size' => 20, 'maxlength' => 11, 'autocomplete' => 'off'),
226 'is_required' => TRUE,
227 );
228
229 $form->_paymentFields['bank_name'] = array(
230 'htmlType' => 'text',
231 'name' => 'bank_name',
232 'title' => ts('Bank Name'),
233 'cc_field' => TRUE,
234 'attributes' => array('size' => 20, 'maxlength' => 64, 'autocomplete' => 'off'),
235 'is_required' => TRUE,
236 );
237 }
238
239 /**
240 * Function to add all the credit card fields
241 *
242 * @param $form
243 * @param bool $useRequired
244 *
245 * @return void
246 * @access public
247 */
248 static function buildCreditCard(&$form, $useRequired = FALSE) {
249 if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_FORM) {
250 self::setCreditCardFields($form);
251 foreach ($form->_paymentFields as $name => $field) {
252 if (!empty($field['cc_field'])) {
253 if ($field['htmlType'] == 'chainSelect') {
254 $form->addChainSelect($field['name'], array('required' => $useRequired && $field['is_required']));
255 }
256 else {
257 $form->add($field['htmlType'],
258 $field['name'],
259 $field['title'],
260 $field['attributes'],
261 $useRequired ? $field['is_required'] : FALSE
262 );
263 }
264 }
265 }
266
267 $form->addRule('cvv2',
268 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.'),
269 'integer'
270 );
271
272 $form->addRule('credit_card_exp_date',
273 ts('Card expiration date cannot be a past date.'),
274 'currentDate', TRUE
275 );
276
277 }
278
279 if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON) {
280 $form->_expressButtonName = $form->getButtonName('upload', 'express');
281 $form->assign('expressButtonName', $form->_expressButtonName);
282 $form->add('image',
283 $form->_expressButtonName,
284 $form->_paymentProcessor['url_button'],
285 array('class' => 'crm-form-submit')
286 );
287 }
288 }
289
290 /**
291 * The credit card pseudo constant results only the CC label, not the key ID
292 * So we normalize the name to use it as a CSS class.
293 */
294 static function getCreditCardCSSNames() {
295 $creditCardTypes = array();
296 foreach (CRM_Contribute_PseudoConstant::creditCard() as $key => $name) {
297 // Replace anything not css-friendly by an underscore
298 // Non-latin names will not like this, but so many things are wrong with
299 // the credit-card type configurations already.
300 $key = str_replace(' ', '', $key);
301 $key = preg_replace('/[^a-zA-Z0-9]/', '_', $key);
302 $key = strtolower($key);
303 $creditCardTypes[$key] = $name;
304 }
305 return $creditCardTypes;
306 }
307
308 /**
309 * Function to add all the direct debit fields
310 *
311 * @param $form
312 * @param bool $useRequired
313 * @return void
314 * @access public
315 */
316 static function buildDirectDebit(&$form, $useRequired = FALSE) {
317 if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_FORM) {
318 self::setDirectDebitFields($form);
319 foreach ($form->_paymentFields as $name => $field) {
320 if (isset($field['cc_field']) &&
321 $field['cc_field']
322 ) {
323 $form->add($field['htmlType'],
324 $field['name'],
325 $field['title'],
326 $field['attributes'],
327 $useRequired ? $field['is_required'] : FALSE
328 );
329 }
330 }
331
332 $form->addRule('bank_identification_number',
333 ts('Please enter a valid Bank Identification Number (value must not contain punctuation characters).'),
334 'nopunctuation'
335 );
336
337 $form->addRule('bank_account_number',
338 ts('Please enter a valid Bank Account Number (value must not contain punctuation characters).'),
339 'nopunctuation'
340 );
341 }
342
343 if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON) {
344 $form->_expressButtonName = $form->getButtonName($form->buttonType(), 'express');
345 $form->add('image',
346 $form->_expressButtonName,
347 $form->_paymentProcessor['url_button'],
348 array('class' => 'crm-form-submit')
349 );
350 }
351 }
352
353 /**
354 * Make sure that credit card number and cvv are valid
355 * Called within the scope of a QF formRule function
356 */
357 static function validateCreditCard($values, &$errors) {
358 if (!empty($values['credit_card_type'])) {
359 if (!empty($values['credit_card_number']) &&
360 !CRM_Utils_Rule::creditCardNumber($values['credit_card_number'], $values['credit_card_type'])
361 ) {
362 $errors['credit_card_number'] = ts('Please enter a valid Card Number');
363 }
364 if (!empty($values['cvv2']) &&
365 !CRM_Utils_Rule::cvv($values['cvv2'], $values['credit_card_type'])
366 ) {
367 $errors['cvv2'] = ts('Please enter a valid Card Verification Number');
368 }
369 }
370 elseif (!empty($values['credit_card_number'])) {
371 $errors['credit_card_number'] = ts('Please enter a valid Card Number');
372 }
373 }
374
375 /**
376 * function to map address fields
377 *
378 * @param $id
379 * @param $src
380 * @param $dst
381 * @param bool $reverse
382 *
383 * @return void
384 * @static
385 */
386 static function mapParams($id, &$src, &$dst, $reverse = FALSE) {
387 static $map = NULL;
388 if (!$map) {
389 $map = array(
390 'first_name' => 'billing_first_name',
391 'middle_name' => 'billing_middle_name',
392 'last_name' => 'billing_last_name',
393 'email' => "email-$id",
394 'street_address' => "billing_street_address-$id",
395 'supplemental_address_1' => "billing_supplemental_address_1-$id",
396 'city' => "billing_city-$id",
397 'state_province' => "billing_state_province-$id",
398 'postal_code' => "billing_postal_code-$id",
399 'country' => "billing_country-$id",
400 );
401 }
402
403 foreach ($map as $n => $v) {
404 if (!$reverse) {
405 if (isset($src[$n])) {
406 $dst[$v] = $src[$n];
407 }
408 }
409 else {
410 if (isset($src[$v])) {
411 $dst[$n] = $src[$v];
412 }
413 }
414 }
415 }
416
417 /**
418 * function to get the credit card expiration month
419 * The date format for this field should typically be "M Y" (ex: Feb 2011) or "m Y" (02 2011)
420 * See CRM-9017
421 *
422 * @param $src
423 *
424 * @return int
425 * @static
426 */
427 static function getCreditCardExpirationMonth($src) {
428 if ($month = CRM_Utils_Array::value('M', $src['credit_card_exp_date'])) {
429 return $month;
430 }
431
432 return CRM_Utils_Array::value('m', $src['credit_card_exp_date']);
433 }
434
435 /**
436 * function to get the credit card expiration year
437 * The date format for this field should typically be "M Y" (ex: Feb 2011) or "m Y" (02 2011)
438 * This function exists only to make it consistant with getCreditCardExpirationMonth
439 *
440 * @param $src
441 *
442 * @return int
443 * @static
444 */
445 static function getCreditCardExpirationYear($src) {
446 return CRM_Utils_Array::value('Y', $src['credit_card_exp_date']);
447 }
448
449 /**
450 * function to return state/province is_required = true/false
451 *
452 * @param obj $form: Form object
453 * @param string $name: Country index name on $_submitValues array
454 * @param bool $onBehalf: Is 'On Behalf Of' profile?
455 *
456 * @return bool
457 * TRUE/FALSE for is_required if country consist/not consist of state/province respectively
458 * @static
459 */
460 static function checkRequiredStateProvince($form, $name, $onBehalf = FALSE) {
461 // If selected country has possible values for state/province mark the
462 // state/province field as required.
463 $config = CRM_Core_Config::singleton();
464 $stateProvince = new CRM_Core_DAO_StateProvince();
465
466 if ($onBehalf) {
467 $stateProvince->country_id = CRM_Utils_Array::value($name, $form->_submitValues['onbehalf']);
468 }
469 else {
470 $stateProvince->country_id = CRM_Utils_Array::value($name, $form->_submitValues);
471 }
472
473 $limitCountryId = $stateProvince->country_id;
474
475 if ($stateProvince->count() > 0) {
476 // check that the state/province data is not excluded by a
477 // limitation in the localisation settings.
478 $countryIsoCodes = CRM_Core_PseudoConstant::countryIsoCode();
479 $limitCodes = $config->provinceLimit();
480 $limitIds = array();
481 foreach ($limitCodes as $code) {
482 $limitIds = array_merge($limitIds, array_keys($countryIsoCodes, $code));
483 }
484
485 if ($limitCountryId && in_array($limitCountryId, $limitIds)) {
486 return TRUE;
487 }
488 return FALSE;
489 }
490 return FALSE;
491 }
492 }
493