Completed
Push — master ( 2005c2...d9c87d )
by Aimeos
06:04
created
client/html/src/Client/Html/Checkout/Standard/Address/Billing/Standard.php 1 patch
Indentation   +825 added lines, -825 removed lines patch added patch discarded remove patch
@@ -19,830 +19,830 @@
 block discarded – undo
19 19
  * @subpackage Html
20 20
  */
21 21
 class Standard
22
-	extends \Aimeos\Client\Html\Common\Client\Factory\Base
23
-	implements \Aimeos\Client\Html\Common\Client\Factory\Iface
22
+    extends \Aimeos\Client\Html\Common\Client\Factory\Base
23
+    implements \Aimeos\Client\Html\Common\Client\Factory\Iface
24 24
 {
25
-	/** client/html/checkout/standard/address/billing/standard/subparts
26
-	 * List of HTML sub-clients rendered within the checkout standard address billing section
27
-	 *
28
-	 * The output of the frontend is composed of the code generated by the HTML
29
-	 * clients. Each HTML client can consist of serveral (or none) sub-clients
30
-	 * that are responsible for rendering certain sub-parts of the output. The
31
-	 * sub-clients can contain HTML clients themselves and therefore a
32
-	 * hierarchical tree of HTML clients is composed. Each HTML client creates
33
-	 * the output that is placed inside the container of its parent.
34
-	 *
35
-	 * At first, always the HTML code generated by the parent is printed, then
36
-	 * the HTML code of its sub-clients. The order of the HTML sub-clients
37
-	 * determines the order of the output of these sub-clients inside the parent
38
-	 * container. If the configured list of clients is
39
-	 *
40
-	 *  array( "subclient1", "subclient2" )
41
-	 *
42
-	 * you can easily change the order of the output by reordering the subparts:
43
-	 *
44
-	 *  client/html/<clients>/subparts = array( "subclient1", "subclient2" )
45
-	 *
46
-	 * You can also remove one or more parts if they shouldn't be rendered:
47
-	 *
48
-	 *  client/html/<clients>/subparts = array( "subclient1" )
49
-	 *
50
-	 * As the clients only generates structural HTML, the layout defined via CSS
51
-	 * should support adding, removing or reordering content by a fluid like
52
-	 * design.
53
-	 *
54
-	 * @param array List of sub-client names
55
-	 * @since 2014.03
56
-	 * @category Developer
57
-	 */
58
-	private $subPartPath = 'client/html/checkout/standard/address/billing/standard/subparts';
59
-	private $subPartNames = array();
60
-	private $cache;
61
-
62
-	private $mandatory = array(
63
-		'order.base.address.salutation',
64
-		'order.base.address.firstname',
65
-		'order.base.address.lastname',
66
-		'order.base.address.address1',
67
-		'order.base.address.postal',
68
-		'order.base.address.city',
69
-		'order.base.address.languageid',
70
-		'order.base.address.email'
71
-	);
72
-
73
-	private $optional = array(
74
-		'order.base.address.company',
75
-		'order.base.address.vatid',
76
-		'order.base.address.address2',
77
-		'order.base.address.countryid',
78
-		'order.base.address.state',
79
-	);
80
-
81
-
82
-	/**
83
-	 * Returns the HTML code for insertion into the body.
84
-	 *
85
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
86
-	 * @param array &$tags Result array for the list of tags that are associated to the output
87
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
88
-	 * @return string HTML code
89
-	 */
90
-	public function getBody( $uid = '', array &$tags = array(), &$expire = null )
91
-	{
92
-		$view = $this->setViewParams( $this->getView(), $tags, $expire );
93
-
94
-		$html = '';
95
-		foreach( $this->getSubClients() as $subclient ) {
96
-			$html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
97
-		}
98
-		$view->billingBody = $html;
99
-
100
-		/** client/html/checkout/standard/address/billing/standard/template-body
101
-		 * Relative path to the HTML body template of the checkout standard address billing client.
102
-		 *
103
-		 * The template file contains the HTML code and processing instructions
104
-		 * to generate the result shown in the body of the frontend. The
105
-		 * configuration string is the path to the template file relative
106
-		 * to the templates directory (usually in client/html/templates).
107
-		 *
108
-		 * You can overwrite the template file configuration in extensions and
109
-		 * provide alternative templates. These alternative templates should be
110
-		 * named like the default one but with the string "standard" replaced by
111
-		 * an unique name. You may use the name of your project for this. If
112
-		 * you've implemented an alternative client class as well, "standard"
113
-		 * should be replaced by the name of the new class.
114
-		 *
115
-		 * @param string Relative path to the template creating code for the HTML page body
116
-		 * @since 2014.03
117
-		 * @category Developer
118
-		 * @see client/html/checkout/standard/address/billing/standard/template-header
119
-		 */
120
-		$tplconf = 'client/html/checkout/standard/address/billing/standard/template-body';
121
-		$default = 'checkout/standard/address-billing-body-default.php';
122
-
123
-		return $view->render( $view->config( $tplconf, $default ) );
124
-	}
125
-
126
-
127
-	/**
128
-	 * Returns the HTML string for insertion into the header.
129
-	 *
130
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
131
-	 * @param array &$tags Result array for the list of tags that are associated to the output
132
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
133
-	 * @return string|null String including HTML tags for the header on error
134
-	 */
135
-	public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
136
-	{
137
-		$view = $this->setViewParams( $this->getView(), $tags, $expire );
138
-
139
-		$html = '';
140
-		foreach( $this->getSubClients() as $subclient ) {
141
-			$html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
142
-		}
143
-		$view->billingHeader = $html;
144
-
145
-		/** client/html/checkout/standard/address/billing/standard/template-header
146
-		 * Relative path to the HTML header template of the checkout standard address billing client.
147
-		 *
148
-		 * The template file contains the HTML code and processing instructions
149
-		 * to generate the HTML code that is inserted into the HTML page header
150
-		 * of the rendered page in the frontend. The configuration string is the
151
-		 * path to the template file relative to the templates directory (usually
152
-		 * in client/html/templates).
153
-		 *
154
-		 * You can overwrite the template file configuration in extensions and
155
-		 * provide alternative templates. These alternative templates should be
156
-		 * named like the default one but with the string "standard" replaced by
157
-		 * an unique name. You may use the name of your project for this. If
158
-		 * you've implemented an alternative client class as well, "standard"
159
-		 * should be replaced by the name of the new class.
160
-		 *
161
-		 * @param string Relative path to the template creating code for the HTML page head
162
-		 * @since 2014.03
163
-		 * @category Developer
164
-		 * @see client/html/checkout/standard/address/billing/standard/template-body
165
-		 */
166
-		$tplconf = 'client/html/checkout/standard/address/billing/standard/template-header';
167
-		$default = 'checkout/standard/address-billing-header-default.php';
168
-
169
-		return $view->render( $view->config( $tplconf, $default ) );
170
-	}
171
-
172
-
173
-	/**
174
-	 * Returns the sub-client given by its name.
175
-	 *
176
-	 * @param string $type Name of the client type
177
-	 * @param string|null $name Name of the sub-client (Default if null)
178
-	 * @return \Aimeos\Client\Html\Iface Sub-client object
179
-	 */
180
-	public function getSubClient( $type, $name = null )
181
-	{
182
-		/** client/html/checkout/standard/address/billing/decorators/excludes
183
-		 * Excludes decorators added by the "common" option from the checkout standard address billing html client
184
-		 *
185
-		 * Decorators extend the functionality of a class by adding new aspects
186
-		 * (e.g. log what is currently done), executing the methods of the underlying
187
-		 * class only in certain conditions (e.g. only for logged in users) or
188
-		 * modify what is returned to the caller.
189
-		 *
190
-		 * This option allows you to remove a decorator added via
191
-		 * "client/html/common/decorators/default" before they are wrapped
192
-		 * around the html client.
193
-		 *
194
-		 *  client/html/checkout/standard/address/billing/decorators/excludes = array( 'decorator1' )
195
-		 *
196
-		 * This would remove the decorator named "decorator1" from the list of
197
-		 * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
198
-		 * "client/html/common/decorators/default" to the html client.
199
-		 *
200
-		 * @param array List of decorator names
201
-		 * @since 2015.08
202
-		 * @category Developer
203
-		 * @see client/html/common/decorators/default
204
-		 * @see client/html/checkout/standard/address/billing/decorators/global
205
-		 * @see client/html/checkout/standard/address/billing/decorators/local
206
-		 */
207
-
208
-		/** client/html/checkout/standard/address/billing/decorators/global
209
-		 * Adds a list of globally available decorators only to the checkout standard address billing html client
210
-		 *
211
-		 * Decorators extend the functionality of a class by adding new aspects
212
-		 * (e.g. log what is currently done), executing the methods of the underlying
213
-		 * class only in certain conditions (e.g. only for logged in users) or
214
-		 * modify what is returned to the caller.
215
-		 *
216
-		 * This option allows you to wrap global decorators
217
-		 * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
218
-		 *
219
-		 *  client/html/checkout/standard/address/billing/decorators/global = array( 'decorator1' )
220
-		 *
221
-		 * This would add the decorator named "decorator1" defined by
222
-		 * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
223
-		 *
224
-		 * @param array List of decorator names
225
-		 * @since 2015.08
226
-		 * @category Developer
227
-		 * @see client/html/common/decorators/default
228
-		 * @see client/html/checkout/standard/address/billing/decorators/excludes
229
-		 * @see client/html/checkout/standard/address/billing/decorators/local
230
-		 */
231
-
232
-		/** client/html/checkout/standard/address/billing/decorators/local
233
-		 * Adds a list of local decorators only to the checkout standard address billing html client
234
-		 *
235
-		 * Decorators extend the functionality of a class by adding new aspects
236
-		 * (e.g. log what is currently done), executing the methods of the underlying
237
-		 * class only in certain conditions (e.g. only for logged in users) or
238
-		 * modify what is returned to the caller.
239
-		 *
240
-		 * This option allows you to wrap local decorators
241
-		 * ("\Aimeos\Client\Html\Checkout\Decorator\*") around the html client.
242
-		 *
243
-		 *  client/html/checkout/standard/address/billing/decorators/local = array( 'decorator2' )
244
-		 *
245
-		 * This would add the decorator named "decorator2" defined by
246
-		 * "\Aimeos\Client\Html\Checkout\Decorator\Decorator2" only to the html client.
247
-		 *
248
-		 * @param array List of decorator names
249
-		 * @since 2015.08
250
-		 * @category Developer
251
-		 * @see client/html/common/decorators/default
252
-		 * @see client/html/checkout/standard/address/billing/decorators/excludes
253
-		 * @see client/html/checkout/standard/address/billing/decorators/global
254
-		 */
255
-
256
-		return $this->createSubClient( 'checkout/standard/address/billing/' . $type, $name );
257
-	}
258
-
259
-
260
-	/**
261
-	 * Stores the given or fetched billing address in the basket.
262
-	 */
263
-	public function process()
264
-	{
265
-		$view = $this->getView();
266
-
267
-		try
268
-		{
269
-			// only start if there's something to do
270
-			if( $view->param( 'ca_billingoption', null ) === null ) {
271
-				return;
272
-			}
273
-
274
-			$this->setAddress( $view );
275
-
276
-			parent::process();
277
-		}
278
-		catch( \Aimeos\Controller\Frontend\Exception $e )
279
-		{
280
-			$view->billingError = $e->getErrorList();
281
-			throw $e;
282
-		}
283
-	}
284
-
285
-
286
-	/**
287
-	 * Checks the address fields for missing data and sanitizes the given parameter list.
288
-	 *
289
-	 * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
290
-	 * @return array List of missing field names
291
-	 */
292
-	protected function checkFields( array &$params )
293
-	{
294
-		$view = $this->getView();
295
-
296
-		/** client/html/checkout/standard/address/billing/mandatory
297
-		 * List of billing address input fields that are required
298
-		 *
299
-		 * You can configure the list of billing address fields that are
300
-		 * necessary and must be filled by the customer before he can
301
-		 * continue the checkout process. Available field keys are:
302
-		 * * order.base.address.company
303
-		 * * order.base.address.vatid
304
-		 * * order.base.address.salutation
305
-		 * * order.base.address.firstname
306
-		 * * order.base.address.lastname
307
-		 * * order.base.address.address1
308
-		 * * order.base.address.address2
309
-		 * * order.base.address.address3
310
-		 * * order.base.address.postal
311
-		 * * order.base.address.city
312
-		 * * order.base.address.state
313
-		 * * order.base.address.languageid
314
-		 * * order.base.address.countryid
315
-		 * * order.base.address.telephone
316
-		 * * order.base.address.telefax
317
-		 * * order.base.address.email
318
-		 * * order.base.address.website
319
-		 *
320
-		 * Until 2015-02, the configuration option was available as
321
-		 * "client/html/common/address/billing/mandatory" starting from 2014-03.
322
-		 *
323
-		 * @param array List of field keys
324
-		 * @since 2015.02
325
-		 * @category User
326
-		 * @category Developer
327
-		 * @see client/html/checkout/standard/address/billing/disable-new
328
-		 * @see client/html/checkout/standard/address/billing/salutations
329
-		 * @see client/html/checkout/standard/address/billing/optional
330
-		 * @see client/html/checkout/standard/address/billing/hidden
331
-		 * @see client/html/checkout/standard/address/countries
332
-		 * @see client/html/checkout/standard/address/validate
333
-		 */
334
-		$mandatory = $view->config( 'client/html/checkout/standard/address/billing/mandatory', $this->mandatory );
335
-
336
-		/** client/html/checkout/standard/address/billing/optional
337
-		 * List of billing address input fields that are optional
338
-		 *
339
-		 * You can configure the list of billing address fields that
340
-		 * customers can fill but don't have to before they can
341
-		 * continue the checkout process. Available field keys are:
342
-		 * * order.base.address.company
343
-		 * * order.base.address.vatid
344
-		 * * order.base.address.salutation
345
-		 * * order.base.address.firstname
346
-		 * * order.base.address.lastname
347
-		 * * order.base.address.address1
348
-		 * * order.base.address.address2
349
-		 * * order.base.address.address3
350
-		 * * order.base.address.postal
351
-		 * * order.base.address.city
352
-		 * * order.base.address.state
353
-		 * * order.base.address.languageid
354
-		 * * order.base.address.countryid
355
-		 * * order.base.address.telephone
356
-		 * * order.base.address.telefax
357
-		 * * order.base.address.email
358
-		 * * order.base.address.website
359
-		 *
360
-		 * Until 2015-02, the configuration option was available as
361
-		 * "client/html/common/address/billing/optional" starting from 2014-03.
362
-		 *
363
-		 * @param array List of field keys
364
-		 * @since 2015.02
365
-		 * @category User
366
-		 * @category Developer
367
-		 * @see client/html/checkout/standard/address/billing/disable-new
368
-		 * @see client/html/checkout/standard/address/billing/salutations
369
-		 * @see client/html/checkout/standard/address/billing/mandatory
370
-		 * @see client/html/checkout/standard/address/billing/hidden
371
-		 * @see client/html/checkout/standard/address/countries
372
-		 * @see client/html/checkout/standard/address/validate
373
-		 */
374
-		$optional = $view->config( 'client/html/checkout/standard/address/billing/optional', $this->optional );
375
-
376
-		/** client/html/checkout/standard/address/validate
377
-		 * List of regular expressions to validate the data of the address fields
378
-		 *
379
-		 * To validate the address input data of the customer, an individual
380
-		 * {@link http://php.net/manual/en/pcre.pattern.php Perl compatible regular expression}
381
-		 * can be applied to each field. Available fields are:
382
-		 * * company
383
-		 * * vatid
384
-		 * * salutation
385
-		 * * firstname
386
-		 * * lastname
387
-		 * * address1
388
-		 * * address2
389
-		 * * address3
390
-		 * * postal
391
-		 * * city
392
-		 * * state
393
-		 * * languageid
394
-		 * * countryid
395
-		 * * telephone
396
-		 * * telefax
397
-		 * * email
398
-		 * * website
399
-		 *
400
-		 * Some fields are validated automatically because they are not
401
-		 * dependent on a country specific rule. These fields are:
402
-		 * * salutation
403
-		 * * email
404
-		 * * website
405
-		 *
406
-		 * To validate e.g the postal/zip code, you can define a regular
407
-		 * expression like this if you want to allow only digits:
408
-		 *
409
-		 *  client/html/checkout/standard/address/validate/postal = '^[0-9]+$'
410
-		 *
411
-		 * Several regular expressions can be defined line this:
412
-		 *
413
-		 *  client/html/checkout/standard/address/validate = array(
414
-		 *      'postal' = '^[0-9]+$',
415
-		 *      'vatid' = '^[A-Z]{2}[0-9]{8}$',
416
-		 *  )
417
-		 *
418
-		 * Don't add any delimiting characters like slashes (/) to the beginning
419
-		 * or the end of the regular expression. They will be added automatically.
420
-		 * Any slashes inside the expression must be escaped by backlashes,
421
-		 * i.e. "\/".
422
-		 *
423
-		 * Until 2015-02, the configuration option was available as
424
-		 * "client/html/common/address/billing/validate" starting from 2014-09.
425
-		 *
426
-		 * @param array Associative list of field names and regular expressions
427
-		 * @since 2014.09
428
-		 * @category Developer
429
-		 * @see client/html/checkout/standard/address/billing/mandatory
430
-		 * @see client/html/checkout/standard/address/billing/optional
431
-		 */
432
-
433
-		$allFields = array_flip( array_merge( $mandatory, $optional ) );
434
-		$invalid = $this->validateFields( $params, $allFields );
435
-		$this->checkSalutation( $params, $mandatory );
436
-
437
-		foreach( $invalid as $key => $name )
438
-		{
439
-			$msg = $view->translate( 'client', 'Billing address part "%1$s" is invalid' );
440
-			$invalid[$key] = sprintf( $msg, $name );
441
-		}
442
-
443
-		foreach( $mandatory as $key )
444
-		{
445
-			if( !isset( $params[$key] ) || $params[$key] == '' )
446
-			{
447
-				$msg = $view->translate( 'client', 'Billing address part "%1$s" is missing' );
448
-				$invalid[$key] = sprintf( $msg, substr( $key, 19 ) );
449
-				unset( $params[$key] );
450
-			}
451
-		}
452
-
453
-		return $invalid;
454
-	}
455
-
456
-
457
-	/**
458
-	 * Additional checks for the salutation
459
-	 *
460
-	 * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
461
-	 * @param array &$mandatory List of mandatory field names
462
-	 * @since 2016.05
463
-	 */
464
-	protected function checkSalutation( array &$params, array &$mandatory )
465
-	{
466
-		if( isset( $params['order.base.address.salutation'] )
467
-			&& $params['order.base.address.salutation'] === \Aimeos\MShop\Common\Item\Address\Base::SALUTATION_COMPANY
468
-			&& in_array( 'order.base.address.company', $mandatory ) === false
469
-		) {
470
-			$mandatory[] = 'order.base.address.company';
471
-		} else {
472
-			$params['order.base.address.company'] = $params['order.base.address.vatid'] = '';
473
-		}
474
-	}
475
-
476
-
477
-	/**
478
-	 * Returns the customer item for the given ID
479
-	 *
480
-	 * @param string $id Unique customer ID
481
-	 * @return \Aimeos\MShop\Customer\Item\Iface Customer item
482
-	 * @throws \Aimeos\Client\Html\Exception If no customer item is available
483
-	 * @since 2016.05
484
-	 */
485
-	protected function getCustomerItem( $id )
486
-	{
487
-		$context = $this->getContext();
488
-		$customerManager = \Aimeos\MShop\Factory::createManager( $context, 'customer' );
489
-
490
-		$search = $customerManager->createSearch( true );
491
-		$expr = array(
492
-				$search->compare( '==', 'customer.id', $id ),
493
-				$search->getConditions(),
494
-		);
495
-		$search->setConditions( $search->combine( '&&', $expr ) );
496
-
497
-		$items = $customerManager->searchItems( $search );
498
-
499
-		if( ( $item = reset( $items ) ) === false || $id != $context->getUserId() ) {
500
-			throw new \Aimeos\Client\Html\Exception( sprintf( 'Customer with ID "%1$s" not found', $id ) );
501
-		}
502
-
503
-		return $item;
504
-	}
505
-
506
-
507
-	/**
508
-	 * Returns the list of sub-client names configured for the client.
509
-	 *
510
-	 * @return array List of HTML client names
511
-	 */
512
-	protected function getSubClientNames()
513
-	{
514
-		return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
515
-	}
516
-
517
-
518
-	/**
519
-	 * Sets the new address
520
-	 *
521
-	 * @param \Aimeos\MW\View\Iface $view View object
522
-	 * @throws \Aimeos\Client\Html\Exception If an error occurs
523
-	 * @since 2016.05
524
-	 */
525
-	protected function setAddress( \Aimeos\MW\View\Iface $view )
526
-	{
527
-		$context = $this->getContext();
528
-		$basketCtrl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
529
-
530
-
531
-		/** client/html/checkout/standard/address/billing/disable-new
532
-		 * Disables the option to enter a new billing address for an order
533
-		 *
534
-		 * Besides the main billing address, customers can usually enter a new
535
-		 * billing address as well. To suppress displaying the form fields for
536
-		 * a billing address, you can set this configuration option to "1".
537
-		 *
538
-		 * Until 2015-02, the configuration option was available as
539
-		 * "client/html/common/address/billing/disable-new" starting from 2014-03.
540
-		 *
541
-		 * @param boolean A value of "1" to disable, "0" enables the billing address form
542
-		 * @since 2015.02
543
-		 * @category User
544
-		 * @category Developer
545
-		 * @see client/html/checkout/standard/address/billing/salutations
546
-		 * @see client/html/checkout/standard/address/billing/mandatory
547
-		 * @see client/html/checkout/standard/address/billing/optional
548
-		 * @see client/html/checkout/standard/address/billing/hidden
549
-		*/
550
-		$disable = $view->config( 'client/html/checkout/standard/address/billing/disable-new', false );
551
-		$type = \Aimeos\MShop\Order\Item\Base\Address\Base::TYPE_PAYMENT;
552
-
553
-		if( ( $option = $view->param( 'ca_billingoption', 'null' ) ) === 'null' && $disable === false ) // new address
554
-		{
555
-			$params = $view->param( 'ca_billing', array() );
556
-			$invalid = $this->checkFields( $params );
557
-
558
-			if( count( $invalid ) > 0 )
559
-			{
560
-				$view->billingError = $invalid;
561
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'At least one billing address part is missing or invalid' ) );
562
-			}
563
-
564
-			$basketCtrl->setAddress( $type, $params );
565
-		}
566
-		else // existing address
567
-		{
568
-			$item = $this->getCustomerItem( $option );
569
-			$customerManager = \Aimeos\MShop\Factory::createManager( $context, 'customer' );
570
-
571
-			$invalid = array();
572
-			$addr = $item->getPaymentAddress();
573
-			$params = $view->param( 'ca_billing_' . $option, array() );
574
-
575
-			if( !empty( $params ) )
576
-			{
577
-				$list = array();
578
-				$invalid = $this->checkFields( $params );
579
-
580
-				foreach( $params as $key => $value ) {
581
-					$list[str_replace( 'order.base', 'customer', $key )] = $value;
582
-				}
583
-
584
-				$addr->fromArray( $list );
585
-				$item->setPaymentAddress( $addr );
586
-
587
-				$customerManager->saveItem( $item );
588
-			}
589
-
590
-			if( count( $invalid ) > 0 )
591
-			{
592
-				$view->billingError = $invalid;
593
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'At least one billing address part is missing or invalid' ) );
594
-			}
595
-
596
-			$basketCtrl->setAddress( $type, $addr );
597
-		}
598
-	}
599
-
600
-
601
-	/**
602
-	 * Sets the necessary parameter values in the view.
603
-	 *
604
-	 * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
605
-	 * @param array &$tags Result array for the list of tags that are associated to the output
606
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
607
-	 * @return \Aimeos\MW\View\Iface Modified view object
608
-	 */
609
-	protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
610
-	{
611
-		if( !isset( $this->cache ) )
612
-		{
613
-			$context = $this->getContext();
614
-			$basketCntl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
615
-
616
-			try {
617
-				$langid = $basketCntl->get()->getAddress( 'payment' )->getLanguageId();
618
-			} catch( \Exception $e ) {
619
-				$langid = $view->param( 'ca_billing/order.base.address.languageid', $context->getLocale()->getLanguageId() );
620
-			}
621
-			$view->billingLanguage = $langid;
622
-
623
-			/** client/html/checkout/standard/address/billing/hidden
624
-			 * List of billing address input fields that are optional and should be hidden
625
-			 *
626
-			 * You can configure the list of billing address fields that
627
-			 * are hidden when a customer enters his new billing address.
628
-			 * Available field keys are:
629
-			 * * order.base.address.company
630
-			 * * order.base.address.vatid
631
-			 * * order.base.address.salutation
632
-			 * * order.base.address.firstname
633
-			 * * order.base.address.lastname
634
-			 * * order.base.address.address1
635
-			 * * order.base.address.address2
636
-			 * * order.base.address.address3
637
-			 * * order.base.address.postal
638
-			 * * order.base.address.city
639
-			 * * order.base.address.state
640
-			 * * order.base.address.languageid
641
-			 * * order.base.address.countryid
642
-			 * * order.base.address.telephone
643
-			 * * order.base.address.telefax
644
-			 * * order.base.address.email
645
-			 * * order.base.address.website
646
-			 *
647
-			 * Caution: Only hide fields that don't require any input
648
-			 *
649
-			 * Until 2015-02, the configuration option was available as
650
-			 * "client/html/common/address/billing/hidden" starting from 2014-03.
651
-			 *
652
-			 * @param array List of field keys
653
-			 * @since 2015.02
654
-			 * @category User
655
-			 * @category Developer
656
-			 * @see client/html/checkout/standard/address/billing/disable-new
657
-			 * @see client/html/checkout/standard/address/billing/salutations
658
-			 * @see client/html/checkout/standard/address/billing/mandatory
659
-			 * @see client/html/checkout/standard/address/billing/optional
660
-			 * @see client/html/checkout/standard/address/countries
661
-			 */
662
-			$hidden = $view->config( 'client/html/checkout/standard/address/billing/hidden', array() );
663
-
664
-			if( count( $view->get( 'addressLanguages', array() ) ) === 1 ) {
665
-				$hidden[] = 'order.base.address.languageid';
666
-			}
667
-
668
-			$salutations = array( 'company', 'mr', 'mrs' );
669
-
670
-			/** client/html/checkout/standard/address/billing/salutations
671
-			 * List of salutions the customer can select from for the billing address
672
-			 *
673
-			 * The following salutations are available:
674
-			 * * empty string for "unknown"
675
-			 * * company
676
-			 * * mr
677
-			 * * mrs
678
-			 * * miss
679
-			 *
680
-			 * You can modify the list of salutation codes and remove the ones
681
-			 * which shouldn't be used. Adding new salutations is a little bit
682
-			 * more difficult because you have to adapt a few areas in the source
683
-			 * code.
684
-			 *
685
-			 * Until 2015-02, the configuration option was available as
686
-			 * "client/html/common/address/billing/salutations" starting from 2014-03.
687
-			 *
688
-			 * @param array List of available salutation codes
689
-			 * @since 2015.02
690
-			 * @category User
691
-			 * @category Developer
692
-			 * @see client/html/checkout/standard/address/billing/disable-new
693
-			 * @see client/html/checkout/standard/address/billing/mandatory
694
-			 * @see client/html/checkout/standard/address/billing/optional
695
-			 * @see client/html/checkout/standard/address/billing/hidden
696
-			 * @see client/html/checkout/standard/address/countries
697
-			 */
698
-			$view->billingSalutations = $view->config( 'client/html/checkout/standard/address/billing/salutations', $salutations );
699
-
700
-			$view->billingMandatory = $view->config( 'client/html/checkout/standard/address/billing/mandatory', $this->mandatory );
701
-			$view->billingOptional = $view->config( 'client/html/checkout/standard/address/billing/optional', $this->optional );
702
-			$view->billingHidden = $hidden;
703
-
704
-			$this->cache = $view;
705
-		}
706
-
707
-		return $this->cache;
708
-	}
709
-
710
-
711
-	/**
712
-	 * Validate the address key/value pairs using regular expressions
713
-	 *
714
-	 * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
715
-	 * @param array $fields List of field names to validate
716
-	 * @return array List of invalid address keys
717
-	 * @since 2016.05
718
-	 */
719
-	protected function validateFields( array &$params, array $fields )
720
-	{
721
-		$config = $this->getContext()->getConfig();
722
-
723
-		/** client/html/checkout/standard/address/validate/company
724
-		 * Regular expression to check the "company" address value
725
-		 *
726
-		 * @see client/html/checkout/standard/address/validate
727
-		 */
728
-
729
-		/** client/html/checkout/standard/address/validate/vatid
730
-		 * Regular expression to check the "vatid" address value
731
-		 *
732
-		 * @see client/html/checkout/standard/address/validate
733
-		 */
734
-
735
-		/** client/html/checkout/standard/address/validate/salutation
736
-		 * Regular expression to check the "salutation" address value
737
-		 *
738
-		 * @see client/html/checkout/standard/address/validate
739
-		 */
740
-
741
-		/** client/html/checkout/standard/address/validate/firstname
742
-		 * Regular expression to check the "firstname" address value
743
-		 *
744
-		 * @see client/html/checkout/standard/address/validate
745
-		 */
746
-
747
-		/** client/html/checkout/standard/address/validate/lastname
748
-		 * Regular expression to check the "lastname" address value
749
-		 *
750
-		 * @see client/html/checkout/standard/address/validate
751
-		 */
752
-
753
-		/** client/html/checkout/standard/address/validate/address1
754
-		 * Regular expression to check the "address1" address value
755
-		 *
756
-		 * @see client/html/checkout/standard/address/validate
757
-		 */
758
-
759
-		/** client/html/checkout/standard/address/validate/address2
760
-		 * Regular expression to check the "address2" address value
761
-		 *
762
-		 * @see client/html/checkout/standard/address/validate
763
-		 */
764
-
765
-		/** client/html/checkout/standard/address/validate/address3
766
-		 * Regular expression to check the "address3" address value
767
-		 *
768
-		 * @see client/html/checkout/standard/address/validate
769
-		 */
770
-
771
-		/** client/html/checkout/standard/address/validate/postal
772
-		 * Regular expression to check the "postal" address value
773
-		 *
774
-		 * @see client/html/checkout/standard/address/validate
775
-		 */
776
-
777
-		/** client/html/checkout/standard/address/validate/city
778
-		 * Regular expression to check the "city" address value
779
-		 *
780
-		 * @see client/html/checkout/standard/address/validate
781
-		 */
782
-
783
-		/** client/html/checkout/standard/address/validate/state
784
-		 * Regular expression to check the "state" address value
785
-		 *
786
-		 * @see client/html/checkout/standard/address/validate
787
-		 */
788
-
789
-		/** client/html/checkout/standard/address/validate/languageid
790
-		 * Regular expression to check the "languageid" address value
791
-		 *
792
-		 * @see client/html/checkout/standard/address/validate
793
-		 */
794
-
795
-		/** client/html/checkout/standard/address/validate/countryid
796
-		 * Regular expression to check the "countryid" address value
797
-		 *
798
-		 * @see client/html/checkout/standard/address/validate
799
-		 */
800
-
801
-		/** client/html/checkout/standard/address/validate/telephone
802
-		 * Regular expression to check the "telephone" address value
803
-		 *
804
-		 * @see client/html/checkout/standard/address/validate
805
-		 */
806
-
807
-		/** client/html/checkout/standard/address/validate/telefax
808
-		 * Regular expression to check the "telefax" address value
809
-		 *
810
-		 * @see client/html/checkout/standard/address/validate
811
-		 */
812
-
813
-		/** client/html/checkout/standard/address/validate/email
814
-		 * Regular expression to check the "email" address value
815
-		 *
816
-		 * @see client/html/checkout/standard/address/validate
817
-		 */
818
-
819
-		/** client/html/checkout/standard/address/validate/website
820
-		 * Regular expression to check the "website" address value
821
-		 *
822
-		 * @see client/html/checkout/standard/address/validate
823
-		 */
824
-
825
-		$invalid = array();
826
-
827
-		foreach( $params as $key => $value )
828
-		{
829
-			if( isset( $fields[$key] ) )
830
-			{
831
-				$name = substr( $key, 19 );
832
-				$regex = $config->get( 'client/html/checkout/standard/address/validate/' . $name );
833
-
834
-				if( $regex && preg_match( '/' . $regex . '/', $value ) !== 1 )
835
-				{
836
-					$invalid[$key] = $name;
837
-					unset( $params[$key] );
838
-				}
839
-			}
840
-			else
841
-			{
842
-				unset( $params[$key] );
843
-			}
844
-		}
845
-
846
-		return $invalid;
847
-	}
25
+    /** client/html/checkout/standard/address/billing/standard/subparts
26
+     * List of HTML sub-clients rendered within the checkout standard address billing section
27
+     *
28
+     * The output of the frontend is composed of the code generated by the HTML
29
+     * clients. Each HTML client can consist of serveral (or none) sub-clients
30
+     * that are responsible for rendering certain sub-parts of the output. The
31
+     * sub-clients can contain HTML clients themselves and therefore a
32
+     * hierarchical tree of HTML clients is composed. Each HTML client creates
33
+     * the output that is placed inside the container of its parent.
34
+     *
35
+     * At first, always the HTML code generated by the parent is printed, then
36
+     * the HTML code of its sub-clients. The order of the HTML sub-clients
37
+     * determines the order of the output of these sub-clients inside the parent
38
+     * container. If the configured list of clients is
39
+     *
40
+     *  array( "subclient1", "subclient2" )
41
+     *
42
+     * you can easily change the order of the output by reordering the subparts:
43
+     *
44
+     *  client/html/<clients>/subparts = array( "subclient1", "subclient2" )
45
+     *
46
+     * You can also remove one or more parts if they shouldn't be rendered:
47
+     *
48
+     *  client/html/<clients>/subparts = array( "subclient1" )
49
+     *
50
+     * As the clients only generates structural HTML, the layout defined via CSS
51
+     * should support adding, removing or reordering content by a fluid like
52
+     * design.
53
+     *
54
+     * @param array List of sub-client names
55
+     * @since 2014.03
56
+     * @category Developer
57
+     */
58
+    private $subPartPath = 'client/html/checkout/standard/address/billing/standard/subparts';
59
+    private $subPartNames = array();
60
+    private $cache;
61
+
62
+    private $mandatory = array(
63
+        'order.base.address.salutation',
64
+        'order.base.address.firstname',
65
+        'order.base.address.lastname',
66
+        'order.base.address.address1',
67
+        'order.base.address.postal',
68
+        'order.base.address.city',
69
+        'order.base.address.languageid',
70
+        'order.base.address.email'
71
+    );
72
+
73
+    private $optional = array(
74
+        'order.base.address.company',
75
+        'order.base.address.vatid',
76
+        'order.base.address.address2',
77
+        'order.base.address.countryid',
78
+        'order.base.address.state',
79
+    );
80
+
81
+
82
+    /**
83
+     * Returns the HTML code for insertion into the body.
84
+     *
85
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
86
+     * @param array &$tags Result array for the list of tags that are associated to the output
87
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
88
+     * @return string HTML code
89
+     */
90
+    public function getBody( $uid = '', array &$tags = array(), &$expire = null )
91
+    {
92
+        $view = $this->setViewParams( $this->getView(), $tags, $expire );
93
+
94
+        $html = '';
95
+        foreach( $this->getSubClients() as $subclient ) {
96
+            $html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
97
+        }
98
+        $view->billingBody = $html;
99
+
100
+        /** client/html/checkout/standard/address/billing/standard/template-body
101
+         * Relative path to the HTML body template of the checkout standard address billing client.
102
+         *
103
+         * The template file contains the HTML code and processing instructions
104
+         * to generate the result shown in the body of the frontend. The
105
+         * configuration string is the path to the template file relative
106
+         * to the templates directory (usually in client/html/templates).
107
+         *
108
+         * You can overwrite the template file configuration in extensions and
109
+         * provide alternative templates. These alternative templates should be
110
+         * named like the default one but with the string "standard" replaced by
111
+         * an unique name. You may use the name of your project for this. If
112
+         * you've implemented an alternative client class as well, "standard"
113
+         * should be replaced by the name of the new class.
114
+         *
115
+         * @param string Relative path to the template creating code for the HTML page body
116
+         * @since 2014.03
117
+         * @category Developer
118
+         * @see client/html/checkout/standard/address/billing/standard/template-header
119
+         */
120
+        $tplconf = 'client/html/checkout/standard/address/billing/standard/template-body';
121
+        $default = 'checkout/standard/address-billing-body-default.php';
122
+
123
+        return $view->render( $view->config( $tplconf, $default ) );
124
+    }
125
+
126
+
127
+    /**
128
+     * Returns the HTML string for insertion into the header.
129
+     *
130
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
131
+     * @param array &$tags Result array for the list of tags that are associated to the output
132
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
133
+     * @return string|null String including HTML tags for the header on error
134
+     */
135
+    public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
136
+    {
137
+        $view = $this->setViewParams( $this->getView(), $tags, $expire );
138
+
139
+        $html = '';
140
+        foreach( $this->getSubClients() as $subclient ) {
141
+            $html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
142
+        }
143
+        $view->billingHeader = $html;
144
+
145
+        /** client/html/checkout/standard/address/billing/standard/template-header
146
+         * Relative path to the HTML header template of the checkout standard address billing client.
147
+         *
148
+         * The template file contains the HTML code and processing instructions
149
+         * to generate the HTML code that is inserted into the HTML page header
150
+         * of the rendered page in the frontend. The configuration string is the
151
+         * path to the template file relative to the templates directory (usually
152
+         * in client/html/templates).
153
+         *
154
+         * You can overwrite the template file configuration in extensions and
155
+         * provide alternative templates. These alternative templates should be
156
+         * named like the default one but with the string "standard" replaced by
157
+         * an unique name. You may use the name of your project for this. If
158
+         * you've implemented an alternative client class as well, "standard"
159
+         * should be replaced by the name of the new class.
160
+         *
161
+         * @param string Relative path to the template creating code for the HTML page head
162
+         * @since 2014.03
163
+         * @category Developer
164
+         * @see client/html/checkout/standard/address/billing/standard/template-body
165
+         */
166
+        $tplconf = 'client/html/checkout/standard/address/billing/standard/template-header';
167
+        $default = 'checkout/standard/address-billing-header-default.php';
168
+
169
+        return $view->render( $view->config( $tplconf, $default ) );
170
+    }
171
+
172
+
173
+    /**
174
+     * Returns the sub-client given by its name.
175
+     *
176
+     * @param string $type Name of the client type
177
+     * @param string|null $name Name of the sub-client (Default if null)
178
+     * @return \Aimeos\Client\Html\Iface Sub-client object
179
+     */
180
+    public function getSubClient( $type, $name = null )
181
+    {
182
+        /** client/html/checkout/standard/address/billing/decorators/excludes
183
+         * Excludes decorators added by the "common" option from the checkout standard address billing html client
184
+         *
185
+         * Decorators extend the functionality of a class by adding new aspects
186
+         * (e.g. log what is currently done), executing the methods of the underlying
187
+         * class only in certain conditions (e.g. only for logged in users) or
188
+         * modify what is returned to the caller.
189
+         *
190
+         * This option allows you to remove a decorator added via
191
+         * "client/html/common/decorators/default" before they are wrapped
192
+         * around the html client.
193
+         *
194
+         *  client/html/checkout/standard/address/billing/decorators/excludes = array( 'decorator1' )
195
+         *
196
+         * This would remove the decorator named "decorator1" from the list of
197
+         * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
198
+         * "client/html/common/decorators/default" to the html client.
199
+         *
200
+         * @param array List of decorator names
201
+         * @since 2015.08
202
+         * @category Developer
203
+         * @see client/html/common/decorators/default
204
+         * @see client/html/checkout/standard/address/billing/decorators/global
205
+         * @see client/html/checkout/standard/address/billing/decorators/local
206
+         */
207
+
208
+        /** client/html/checkout/standard/address/billing/decorators/global
209
+         * Adds a list of globally available decorators only to the checkout standard address billing html client
210
+         *
211
+         * Decorators extend the functionality of a class by adding new aspects
212
+         * (e.g. log what is currently done), executing the methods of the underlying
213
+         * class only in certain conditions (e.g. only for logged in users) or
214
+         * modify what is returned to the caller.
215
+         *
216
+         * This option allows you to wrap global decorators
217
+         * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
218
+         *
219
+         *  client/html/checkout/standard/address/billing/decorators/global = array( 'decorator1' )
220
+         *
221
+         * This would add the decorator named "decorator1" defined by
222
+         * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
223
+         *
224
+         * @param array List of decorator names
225
+         * @since 2015.08
226
+         * @category Developer
227
+         * @see client/html/common/decorators/default
228
+         * @see client/html/checkout/standard/address/billing/decorators/excludes
229
+         * @see client/html/checkout/standard/address/billing/decorators/local
230
+         */
231
+
232
+        /** client/html/checkout/standard/address/billing/decorators/local
233
+         * Adds a list of local decorators only to the checkout standard address billing html client
234
+         *
235
+         * Decorators extend the functionality of a class by adding new aspects
236
+         * (e.g. log what is currently done), executing the methods of the underlying
237
+         * class only in certain conditions (e.g. only for logged in users) or
238
+         * modify what is returned to the caller.
239
+         *
240
+         * This option allows you to wrap local decorators
241
+         * ("\Aimeos\Client\Html\Checkout\Decorator\*") around the html client.
242
+         *
243
+         *  client/html/checkout/standard/address/billing/decorators/local = array( 'decorator2' )
244
+         *
245
+         * This would add the decorator named "decorator2" defined by
246
+         * "\Aimeos\Client\Html\Checkout\Decorator\Decorator2" only to the html client.
247
+         *
248
+         * @param array List of decorator names
249
+         * @since 2015.08
250
+         * @category Developer
251
+         * @see client/html/common/decorators/default
252
+         * @see client/html/checkout/standard/address/billing/decorators/excludes
253
+         * @see client/html/checkout/standard/address/billing/decorators/global
254
+         */
255
+
256
+        return $this->createSubClient( 'checkout/standard/address/billing/' . $type, $name );
257
+    }
258
+
259
+
260
+    /**
261
+     * Stores the given or fetched billing address in the basket.
262
+     */
263
+    public function process()
264
+    {
265
+        $view = $this->getView();
266
+
267
+        try
268
+        {
269
+            // only start if there's something to do
270
+            if( $view->param( 'ca_billingoption', null ) === null ) {
271
+                return;
272
+            }
273
+
274
+            $this->setAddress( $view );
275
+
276
+            parent::process();
277
+        }
278
+        catch( \Aimeos\Controller\Frontend\Exception $e )
279
+        {
280
+            $view->billingError = $e->getErrorList();
281
+            throw $e;
282
+        }
283
+    }
284
+
285
+
286
+    /**
287
+     * Checks the address fields for missing data and sanitizes the given parameter list.
288
+     *
289
+     * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
290
+     * @return array List of missing field names
291
+     */
292
+    protected function checkFields( array &$params )
293
+    {
294
+        $view = $this->getView();
295
+
296
+        /** client/html/checkout/standard/address/billing/mandatory
297
+         * List of billing address input fields that are required
298
+         *
299
+         * You can configure the list of billing address fields that are
300
+         * necessary and must be filled by the customer before he can
301
+         * continue the checkout process. Available field keys are:
302
+         * * order.base.address.company
303
+         * * order.base.address.vatid
304
+         * * order.base.address.salutation
305
+         * * order.base.address.firstname
306
+         * * order.base.address.lastname
307
+         * * order.base.address.address1
308
+         * * order.base.address.address2
309
+         * * order.base.address.address3
310
+         * * order.base.address.postal
311
+         * * order.base.address.city
312
+         * * order.base.address.state
313
+         * * order.base.address.languageid
314
+         * * order.base.address.countryid
315
+         * * order.base.address.telephone
316
+         * * order.base.address.telefax
317
+         * * order.base.address.email
318
+         * * order.base.address.website
319
+         *
320
+         * Until 2015-02, the configuration option was available as
321
+         * "client/html/common/address/billing/mandatory" starting from 2014-03.
322
+         *
323
+         * @param array List of field keys
324
+         * @since 2015.02
325
+         * @category User
326
+         * @category Developer
327
+         * @see client/html/checkout/standard/address/billing/disable-new
328
+         * @see client/html/checkout/standard/address/billing/salutations
329
+         * @see client/html/checkout/standard/address/billing/optional
330
+         * @see client/html/checkout/standard/address/billing/hidden
331
+         * @see client/html/checkout/standard/address/countries
332
+         * @see client/html/checkout/standard/address/validate
333
+         */
334
+        $mandatory = $view->config( 'client/html/checkout/standard/address/billing/mandatory', $this->mandatory );
335
+
336
+        /** client/html/checkout/standard/address/billing/optional
337
+         * List of billing address input fields that are optional
338
+         *
339
+         * You can configure the list of billing address fields that
340
+         * customers can fill but don't have to before they can
341
+         * continue the checkout process. Available field keys are:
342
+         * * order.base.address.company
343
+         * * order.base.address.vatid
344
+         * * order.base.address.salutation
345
+         * * order.base.address.firstname
346
+         * * order.base.address.lastname
347
+         * * order.base.address.address1
348
+         * * order.base.address.address2
349
+         * * order.base.address.address3
350
+         * * order.base.address.postal
351
+         * * order.base.address.city
352
+         * * order.base.address.state
353
+         * * order.base.address.languageid
354
+         * * order.base.address.countryid
355
+         * * order.base.address.telephone
356
+         * * order.base.address.telefax
357
+         * * order.base.address.email
358
+         * * order.base.address.website
359
+         *
360
+         * Until 2015-02, the configuration option was available as
361
+         * "client/html/common/address/billing/optional" starting from 2014-03.
362
+         *
363
+         * @param array List of field keys
364
+         * @since 2015.02
365
+         * @category User
366
+         * @category Developer
367
+         * @see client/html/checkout/standard/address/billing/disable-new
368
+         * @see client/html/checkout/standard/address/billing/salutations
369
+         * @see client/html/checkout/standard/address/billing/mandatory
370
+         * @see client/html/checkout/standard/address/billing/hidden
371
+         * @see client/html/checkout/standard/address/countries
372
+         * @see client/html/checkout/standard/address/validate
373
+         */
374
+        $optional = $view->config( 'client/html/checkout/standard/address/billing/optional', $this->optional );
375
+
376
+        /** client/html/checkout/standard/address/validate
377
+         * List of regular expressions to validate the data of the address fields
378
+         *
379
+         * To validate the address input data of the customer, an individual
380
+         * {@link http://php.net/manual/en/pcre.pattern.php Perl compatible regular expression}
381
+         * can be applied to each field. Available fields are:
382
+         * * company
383
+         * * vatid
384
+         * * salutation
385
+         * * firstname
386
+         * * lastname
387
+         * * address1
388
+         * * address2
389
+         * * address3
390
+         * * postal
391
+         * * city
392
+         * * state
393
+         * * languageid
394
+         * * countryid
395
+         * * telephone
396
+         * * telefax
397
+         * * email
398
+         * * website
399
+         *
400
+         * Some fields are validated automatically because they are not
401
+         * dependent on a country specific rule. These fields are:
402
+         * * salutation
403
+         * * email
404
+         * * website
405
+         *
406
+         * To validate e.g the postal/zip code, you can define a regular
407
+         * expression like this if you want to allow only digits:
408
+         *
409
+         *  client/html/checkout/standard/address/validate/postal = '^[0-9]+$'
410
+         *
411
+         * Several regular expressions can be defined line this:
412
+         *
413
+         *  client/html/checkout/standard/address/validate = array(
414
+         *      'postal' = '^[0-9]+$',
415
+         *      'vatid' = '^[A-Z]{2}[0-9]{8}$',
416
+         *  )
417
+         *
418
+         * Don't add any delimiting characters like slashes (/) to the beginning
419
+         * or the end of the regular expression. They will be added automatically.
420
+         * Any slashes inside the expression must be escaped by backlashes,
421
+         * i.e. "\/".
422
+         *
423
+         * Until 2015-02, the configuration option was available as
424
+         * "client/html/common/address/billing/validate" starting from 2014-09.
425
+         *
426
+         * @param array Associative list of field names and regular expressions
427
+         * @since 2014.09
428
+         * @category Developer
429
+         * @see client/html/checkout/standard/address/billing/mandatory
430
+         * @see client/html/checkout/standard/address/billing/optional
431
+         */
432
+
433
+        $allFields = array_flip( array_merge( $mandatory, $optional ) );
434
+        $invalid = $this->validateFields( $params, $allFields );
435
+        $this->checkSalutation( $params, $mandatory );
436
+
437
+        foreach( $invalid as $key => $name )
438
+        {
439
+            $msg = $view->translate( 'client', 'Billing address part "%1$s" is invalid' );
440
+            $invalid[$key] = sprintf( $msg, $name );
441
+        }
442
+
443
+        foreach( $mandatory as $key )
444
+        {
445
+            if( !isset( $params[$key] ) || $params[$key] == '' )
446
+            {
447
+                $msg = $view->translate( 'client', 'Billing address part "%1$s" is missing' );
448
+                $invalid[$key] = sprintf( $msg, substr( $key, 19 ) );
449
+                unset( $params[$key] );
450
+            }
451
+        }
452
+
453
+        return $invalid;
454
+    }
455
+
456
+
457
+    /**
458
+     * Additional checks for the salutation
459
+     *
460
+     * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
461
+     * @param array &$mandatory List of mandatory field names
462
+     * @since 2016.05
463
+     */
464
+    protected function checkSalutation( array &$params, array &$mandatory )
465
+    {
466
+        if( isset( $params['order.base.address.salutation'] )
467
+            && $params['order.base.address.salutation'] === \Aimeos\MShop\Common\Item\Address\Base::SALUTATION_COMPANY
468
+            && in_array( 'order.base.address.company', $mandatory ) === false
469
+        ) {
470
+            $mandatory[] = 'order.base.address.company';
471
+        } else {
472
+            $params['order.base.address.company'] = $params['order.base.address.vatid'] = '';
473
+        }
474
+    }
475
+
476
+
477
+    /**
478
+     * Returns the customer item for the given ID
479
+     *
480
+     * @param string $id Unique customer ID
481
+     * @return \Aimeos\MShop\Customer\Item\Iface Customer item
482
+     * @throws \Aimeos\Client\Html\Exception If no customer item is available
483
+     * @since 2016.05
484
+     */
485
+    protected function getCustomerItem( $id )
486
+    {
487
+        $context = $this->getContext();
488
+        $customerManager = \Aimeos\MShop\Factory::createManager( $context, 'customer' );
489
+
490
+        $search = $customerManager->createSearch( true );
491
+        $expr = array(
492
+                $search->compare( '==', 'customer.id', $id ),
493
+                $search->getConditions(),
494
+        );
495
+        $search->setConditions( $search->combine( '&&', $expr ) );
496
+
497
+        $items = $customerManager->searchItems( $search );
498
+
499
+        if( ( $item = reset( $items ) ) === false || $id != $context->getUserId() ) {
500
+            throw new \Aimeos\Client\Html\Exception( sprintf( 'Customer with ID "%1$s" not found', $id ) );
501
+        }
502
+
503
+        return $item;
504
+    }
505
+
506
+
507
+    /**
508
+     * Returns the list of sub-client names configured for the client.
509
+     *
510
+     * @return array List of HTML client names
511
+     */
512
+    protected function getSubClientNames()
513
+    {
514
+        return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
515
+    }
516
+
517
+
518
+    /**
519
+     * Sets the new address
520
+     *
521
+     * @param \Aimeos\MW\View\Iface $view View object
522
+     * @throws \Aimeos\Client\Html\Exception If an error occurs
523
+     * @since 2016.05
524
+     */
525
+    protected function setAddress( \Aimeos\MW\View\Iface $view )
526
+    {
527
+        $context = $this->getContext();
528
+        $basketCtrl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
529
+
530
+
531
+        /** client/html/checkout/standard/address/billing/disable-new
532
+         * Disables the option to enter a new billing address for an order
533
+         *
534
+         * Besides the main billing address, customers can usually enter a new
535
+         * billing address as well. To suppress displaying the form fields for
536
+         * a billing address, you can set this configuration option to "1".
537
+         *
538
+         * Until 2015-02, the configuration option was available as
539
+         * "client/html/common/address/billing/disable-new" starting from 2014-03.
540
+         *
541
+         * @param boolean A value of "1" to disable, "0" enables the billing address form
542
+         * @since 2015.02
543
+         * @category User
544
+         * @category Developer
545
+         * @see client/html/checkout/standard/address/billing/salutations
546
+         * @see client/html/checkout/standard/address/billing/mandatory
547
+         * @see client/html/checkout/standard/address/billing/optional
548
+         * @see client/html/checkout/standard/address/billing/hidden
549
+         */
550
+        $disable = $view->config( 'client/html/checkout/standard/address/billing/disable-new', false );
551
+        $type = \Aimeos\MShop\Order\Item\Base\Address\Base::TYPE_PAYMENT;
552
+
553
+        if( ( $option = $view->param( 'ca_billingoption', 'null' ) ) === 'null' && $disable === false ) // new address
554
+        {
555
+            $params = $view->param( 'ca_billing', array() );
556
+            $invalid = $this->checkFields( $params );
557
+
558
+            if( count( $invalid ) > 0 )
559
+            {
560
+                $view->billingError = $invalid;
561
+                throw new \Aimeos\Client\Html\Exception( sprintf( 'At least one billing address part is missing or invalid' ) );
562
+            }
563
+
564
+            $basketCtrl->setAddress( $type, $params );
565
+        }
566
+        else // existing address
567
+        {
568
+            $item = $this->getCustomerItem( $option );
569
+            $customerManager = \Aimeos\MShop\Factory::createManager( $context, 'customer' );
570
+
571
+            $invalid = array();
572
+            $addr = $item->getPaymentAddress();
573
+            $params = $view->param( 'ca_billing_' . $option, array() );
574
+
575
+            if( !empty( $params ) )
576
+            {
577
+                $list = array();
578
+                $invalid = $this->checkFields( $params );
579
+
580
+                foreach( $params as $key => $value ) {
581
+                    $list[str_replace( 'order.base', 'customer', $key )] = $value;
582
+                }
583
+
584
+                $addr->fromArray( $list );
585
+                $item->setPaymentAddress( $addr );
586
+
587
+                $customerManager->saveItem( $item );
588
+            }
589
+
590
+            if( count( $invalid ) > 0 )
591
+            {
592
+                $view->billingError = $invalid;
593
+                throw new \Aimeos\Client\Html\Exception( sprintf( 'At least one billing address part is missing or invalid' ) );
594
+            }
595
+
596
+            $basketCtrl->setAddress( $type, $addr );
597
+        }
598
+    }
599
+
600
+
601
+    /**
602
+     * Sets the necessary parameter values in the view.
603
+     *
604
+     * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
605
+     * @param array &$tags Result array for the list of tags that are associated to the output
606
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
607
+     * @return \Aimeos\MW\View\Iface Modified view object
608
+     */
609
+    protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
610
+    {
611
+        if( !isset( $this->cache ) )
612
+        {
613
+            $context = $this->getContext();
614
+            $basketCntl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
615
+
616
+            try {
617
+                $langid = $basketCntl->get()->getAddress( 'payment' )->getLanguageId();
618
+            } catch( \Exception $e ) {
619
+                $langid = $view->param( 'ca_billing/order.base.address.languageid', $context->getLocale()->getLanguageId() );
620
+            }
621
+            $view->billingLanguage = $langid;
622
+
623
+            /** client/html/checkout/standard/address/billing/hidden
624
+             * List of billing address input fields that are optional and should be hidden
625
+             *
626
+             * You can configure the list of billing address fields that
627
+             * are hidden when a customer enters his new billing address.
628
+             * Available field keys are:
629
+             * * order.base.address.company
630
+             * * order.base.address.vatid
631
+             * * order.base.address.salutation
632
+             * * order.base.address.firstname
633
+             * * order.base.address.lastname
634
+             * * order.base.address.address1
635
+             * * order.base.address.address2
636
+             * * order.base.address.address3
637
+             * * order.base.address.postal
638
+             * * order.base.address.city
639
+             * * order.base.address.state
640
+             * * order.base.address.languageid
641
+             * * order.base.address.countryid
642
+             * * order.base.address.telephone
643
+             * * order.base.address.telefax
644
+             * * order.base.address.email
645
+             * * order.base.address.website
646
+             *
647
+             * Caution: Only hide fields that don't require any input
648
+             *
649
+             * Until 2015-02, the configuration option was available as
650
+             * "client/html/common/address/billing/hidden" starting from 2014-03.
651
+             *
652
+             * @param array List of field keys
653
+             * @since 2015.02
654
+             * @category User
655
+             * @category Developer
656
+             * @see client/html/checkout/standard/address/billing/disable-new
657
+             * @see client/html/checkout/standard/address/billing/salutations
658
+             * @see client/html/checkout/standard/address/billing/mandatory
659
+             * @see client/html/checkout/standard/address/billing/optional
660
+             * @see client/html/checkout/standard/address/countries
661
+             */
662
+            $hidden = $view->config( 'client/html/checkout/standard/address/billing/hidden', array() );
663
+
664
+            if( count( $view->get( 'addressLanguages', array() ) ) === 1 ) {
665
+                $hidden[] = 'order.base.address.languageid';
666
+            }
667
+
668
+            $salutations = array( 'company', 'mr', 'mrs' );
669
+
670
+            /** client/html/checkout/standard/address/billing/salutations
671
+             * List of salutions the customer can select from for the billing address
672
+             *
673
+             * The following salutations are available:
674
+             * * empty string for "unknown"
675
+             * * company
676
+             * * mr
677
+             * * mrs
678
+             * * miss
679
+             *
680
+             * You can modify the list of salutation codes and remove the ones
681
+             * which shouldn't be used. Adding new salutations is a little bit
682
+             * more difficult because you have to adapt a few areas in the source
683
+             * code.
684
+             *
685
+             * Until 2015-02, the configuration option was available as
686
+             * "client/html/common/address/billing/salutations" starting from 2014-03.
687
+             *
688
+             * @param array List of available salutation codes
689
+             * @since 2015.02
690
+             * @category User
691
+             * @category Developer
692
+             * @see client/html/checkout/standard/address/billing/disable-new
693
+             * @see client/html/checkout/standard/address/billing/mandatory
694
+             * @see client/html/checkout/standard/address/billing/optional
695
+             * @see client/html/checkout/standard/address/billing/hidden
696
+             * @see client/html/checkout/standard/address/countries
697
+             */
698
+            $view->billingSalutations = $view->config( 'client/html/checkout/standard/address/billing/salutations', $salutations );
699
+
700
+            $view->billingMandatory = $view->config( 'client/html/checkout/standard/address/billing/mandatory', $this->mandatory );
701
+            $view->billingOptional = $view->config( 'client/html/checkout/standard/address/billing/optional', $this->optional );
702
+            $view->billingHidden = $hidden;
703
+
704
+            $this->cache = $view;
705
+        }
706
+
707
+        return $this->cache;
708
+    }
709
+
710
+
711
+    /**
712
+     * Validate the address key/value pairs using regular expressions
713
+     *
714
+     * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
715
+     * @param array $fields List of field names to validate
716
+     * @return array List of invalid address keys
717
+     * @since 2016.05
718
+     */
719
+    protected function validateFields( array &$params, array $fields )
720
+    {
721
+        $config = $this->getContext()->getConfig();
722
+
723
+        /** client/html/checkout/standard/address/validate/company
724
+         * Regular expression to check the "company" address value
725
+         *
726
+         * @see client/html/checkout/standard/address/validate
727
+         */
728
+
729
+        /** client/html/checkout/standard/address/validate/vatid
730
+         * Regular expression to check the "vatid" address value
731
+         *
732
+         * @see client/html/checkout/standard/address/validate
733
+         */
734
+
735
+        /** client/html/checkout/standard/address/validate/salutation
736
+         * Regular expression to check the "salutation" address value
737
+         *
738
+         * @see client/html/checkout/standard/address/validate
739
+         */
740
+
741
+        /** client/html/checkout/standard/address/validate/firstname
742
+         * Regular expression to check the "firstname" address value
743
+         *
744
+         * @see client/html/checkout/standard/address/validate
745
+         */
746
+
747
+        /** client/html/checkout/standard/address/validate/lastname
748
+         * Regular expression to check the "lastname" address value
749
+         *
750
+         * @see client/html/checkout/standard/address/validate
751
+         */
752
+
753
+        /** client/html/checkout/standard/address/validate/address1
754
+         * Regular expression to check the "address1" address value
755
+         *
756
+         * @see client/html/checkout/standard/address/validate
757
+         */
758
+
759
+        /** client/html/checkout/standard/address/validate/address2
760
+         * Regular expression to check the "address2" address value
761
+         *
762
+         * @see client/html/checkout/standard/address/validate
763
+         */
764
+
765
+        /** client/html/checkout/standard/address/validate/address3
766
+         * Regular expression to check the "address3" address value
767
+         *
768
+         * @see client/html/checkout/standard/address/validate
769
+         */
770
+
771
+        /** client/html/checkout/standard/address/validate/postal
772
+         * Regular expression to check the "postal" address value
773
+         *
774
+         * @see client/html/checkout/standard/address/validate
775
+         */
776
+
777
+        /** client/html/checkout/standard/address/validate/city
778
+         * Regular expression to check the "city" address value
779
+         *
780
+         * @see client/html/checkout/standard/address/validate
781
+         */
782
+
783
+        /** client/html/checkout/standard/address/validate/state
784
+         * Regular expression to check the "state" address value
785
+         *
786
+         * @see client/html/checkout/standard/address/validate
787
+         */
788
+
789
+        /** client/html/checkout/standard/address/validate/languageid
790
+         * Regular expression to check the "languageid" address value
791
+         *
792
+         * @see client/html/checkout/standard/address/validate
793
+         */
794
+
795
+        /** client/html/checkout/standard/address/validate/countryid
796
+         * Regular expression to check the "countryid" address value
797
+         *
798
+         * @see client/html/checkout/standard/address/validate
799
+         */
800
+
801
+        /** client/html/checkout/standard/address/validate/telephone
802
+         * Regular expression to check the "telephone" address value
803
+         *
804
+         * @see client/html/checkout/standard/address/validate
805
+         */
806
+
807
+        /** client/html/checkout/standard/address/validate/telefax
808
+         * Regular expression to check the "telefax" address value
809
+         *
810
+         * @see client/html/checkout/standard/address/validate
811
+         */
812
+
813
+        /** client/html/checkout/standard/address/validate/email
814
+         * Regular expression to check the "email" address value
815
+         *
816
+         * @see client/html/checkout/standard/address/validate
817
+         */
818
+
819
+        /** client/html/checkout/standard/address/validate/website
820
+         * Regular expression to check the "website" address value
821
+         *
822
+         * @see client/html/checkout/standard/address/validate
823
+         */
824
+
825
+        $invalid = array();
826
+
827
+        foreach( $params as $key => $value )
828
+        {
829
+            if( isset( $fields[$key] ) )
830
+            {
831
+                $name = substr( $key, 19 );
832
+                $regex = $config->get( 'client/html/checkout/standard/address/validate/' . $name );
833
+
834
+                if( $regex && preg_match( '/' . $regex . '/', $value ) !== 1 )
835
+                {
836
+                    $invalid[$key] = $name;
837
+                    unset( $params[$key] );
838
+                }
839
+            }
840
+            else
841
+            {
842
+                unset( $params[$key] );
843
+            }
844
+        }
845
+
846
+        return $invalid;
847
+    }
848 848
 }
849 849
\ No newline at end of file
Please login to merge, or discard this patch.