Completed
Push — master ( 8b8334...2005c2 )
by Aimeos
07:11
created
client/html/src/Client/Html/Checkout/Standard/Standard.php 2 patches
Indentation   +593 added lines, -593 removed lines patch added patch discarded remove patch
@@ -19,598 +19,598 @@
 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/standard/subparts
26
-	 * List of HTML sub-clients rendered within the checkout standard 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/standard/subparts';
59
-
60
-	/** client/html/checkout/standard/address/name
61
-	 * Name of the address part used by the checkout standard client implementation
62
-	 *
63
-	 * Use "Myname" if your class is named "\Aimeos\Client\Html\Checkout\Standard\Address\Myname".
64
-	 * The name is case-sensitive and you should avoid camel case names like "MyName".
65
-	 *
66
-	 * @param string Last part of the client class name
67
-	 * @since 2014.03
68
-	 * @category Developer
69
-	 */
70
-
71
-	/** client/html/checkout/standard/delivery/name
72
-	 * Name of the delivery part used by the checkout standard client implementation
73
-	 *
74
-	 * Use "Myname" if your class is named "\Aimeos\Client\Html\Checkout\Standard\Delivery\Myname".
75
-	 * The name is case-sensitive and you should avoid camel case names like "MyName".
76
-	 *
77
-	 * @param string Last part of the client class name
78
-	 * @since 2014.03
79
-	 * @category Developer
80
-	 */
81
-
82
-	/** client/html/checkout/standard/payment/name
83
-	 * Name of the payment part used by the checkout standard client implementation
84
-	 *
85
-	 * Use "Myname" if your class is named "\Aimeos\Client\Html\Checkout\Standard\Payment\Myname".
86
-	 * The name is case-sensitive and you should avoid camel case names like "MyName".
87
-	 *
88
-	 * @param string Last part of the client class name
89
-	 * @since 2014.03
90
-	 * @category Developer
91
-	 */
92
-
93
-	/** client/html/checkout/standard/summary/name
94
-	 * Name of the summary part used by the checkout standard client implementation
95
-	 *
96
-	 * Use "Myname" if your class is named "\Aimeos\Client\Html\Checkout\Standard\Summary\Myname".
97
-	 * The name is case-sensitive and you should avoid camel case names like "MyName".
98
-	 *
99
-	 * @param string Last part of the client class name
100
-	 * @since 2014.03
101
-	 * @category Developer
102
-	 */
103
-
104
-	/** client/html/checkout/standard/order/name
105
-	 * Name of the order part used by the checkout standard client implementation
106
-	 *
107
-	 * Use "Myname" if your class is named "\Aimeos\Client\Html\Checkout\Standard\Order\Myname".
108
-	 * The name is case-sensitive and you should avoid camel case names like "MyName".
109
-	 *
110
-	 * @param string Last part of the client class name
111
-	 * @since 2014.03
112
-	 * @category Developer
113
-	 */
114
-
115
-	/** client/html/checkout/standard/process/name
116
-	 * Name of the process part used by the checkout standard client implementation
117
-	 *
118
-	 * Use "Myname" if your class is named "\Aimeos\Client\Html\Checkout\Standard\Process\Myname".
119
-	 * The name is case-sensitive and you should avoid camel case names like "MyName".
120
-	 *
121
-	 * @param string Last part of the client class name
122
-	 * @since 2015.07
123
-	 * @category Developer
124
-	 */
125
-	private $subPartNames = array( 'address', 'delivery', 'payment', 'summary', 'order', 'process' );
126
-	private $cache;
127
-
128
-
129
-	/**
130
-	 * Returns the HTML code for insertion into the body.
131
-	 *
132
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
133
-	 * @param array &$tags Result array for the list of tags that are associated to the output
134
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
135
-	 * @return string HTML code
136
-	 */
137
-	public function getBody( $uid = '', array &$tags = array(), &$expire = null )
138
-	{
139
-		$context = $this->getContext();
140
-		$view = $this->getView();
141
-
142
-		try
143
-		{
144
-			$view = $this->setViewParams( $view, $tags, $expire );
145
-
146
-			$html = '';
147
-			foreach( $this->getSubClients() as $subclient ) {
148
-				$html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
149
-			}
150
-			$view->standardBody = $html;
151
-		}
152
-		catch( \Aimeos\Client\Html\Exception $e )
153
-		{
154
-			$error = array( $this->getContext()->getI18n()->dt( 'client', $e->getMessage() ) );
155
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
156
-		}
157
-		catch( \Aimeos\Controller\Frontend\Exception $e )
158
-		{
159
-			$error = array( $this->getContext()->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
160
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
161
-		}
162
-		catch( \Aimeos\MShop\Exception $e )
163
-		{
164
-			$error = array( $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ) );
165
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
166
-		}
167
-		catch( \Exception $e )
168
-		{
169
-			$context->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
170
-
171
-			$error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
172
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
173
-		}
174
-
175
-		/** client/html/checkout/standard/standard/template-body
176
-		 * Relative path to the HTML body template of the checkout standard client.
177
-		 *
178
-		 * The template file contains the HTML code and processing instructions
179
-		 * to generate the result shown in the body of the frontend. The
180
-		 * configuration string is the path to the template file relative
181
-		 * to the templates directory (usually in client/html/templates).
182
-		 *
183
-		 * You can overwrite the template file configuration in extensions and
184
-		 * provide alternative templates. These alternative templates should be
185
-		 * named like the default one but with the string "standard" replaced by
186
-		 * an unique name. You may use the name of your project for this. If
187
-		 * you've implemented an alternative client class as well, "standard"
188
-		 * should be replaced by the name of the new class.
189
-		 *
190
-		 * @param string Relative path to the template creating code for the HTML page body
191
-		 * @since 2014.03
192
-		 * @category Developer
193
-		 * @see client/html/checkout/standard/standard/template-header
194
-		 */
195
-		$tplconf = 'client/html/checkout/standard/standard/template-body';
196
-		$default = 'checkout/standard/body-default.php';
197
-
198
-		return $view->render( $view->config( $tplconf, $default ) );
199
-	}
200
-
201
-
202
-	/**
203
-	 * Returns the HTML string for insertion into the header.
204
-	 *
205
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
206
-	 * @param array &$tags Result array for the list of tags that are associated to the output
207
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
208
-	 * @return string|null String including HTML tags for the header on error
209
-	 */
210
-	public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
211
-	{
212
-		try
213
-		{
214
-			$view = $this->setViewParams( $this->getView(), $tags, $expire );
215
-
216
-			$html = '';
217
-			foreach( $this->getSubClients() as $subclient ) {
218
-				$html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
219
-			}
220
-			$view->standardHeader = $html;
221
-
222
-			/** client/html/checkout/standard/standard/template-header
223
-			 * Relative path to the HTML header template of the checkout standard client.
224
-			 *
225
-			 * The template file contains the HTML code and processing instructions
226
-			 * to generate the HTML code that is inserted into the HTML page header
227
-			 * of the rendered page in the frontend. The configuration string is the
228
-			 * path to the template file relative to the templates directory (usually
229
-			 * in client/html/templates).
230
-			 *
231
-			 * You can overwrite the template file configuration in extensions and
232
-			 * provide alternative templates. These alternative templates should be
233
-			 * named like the default one but with the string "standard" replaced by
234
-			 * an unique name. You may use the name of your project for this. If
235
-			 * you've implemented an alternative client class as well, "standard"
236
-			 * should be replaced by the name of the new class.
237
-			 *
238
-			 * @param string Relative path to the template creating code for the HTML page head
239
-			 * @since 2014.03
240
-			 * @category Developer
241
-			 * @see client/html/checkout/standard/standard/template-body
242
-			 */
243
-			$tplconf = 'client/html/checkout/standard/standard/template-header';
244
-			$default = 'checkout/standard/header-default.php';
245
-
246
-			return $view->render( $view->config( $tplconf, $default ) );
247
-		}
248
-		catch( \Exception $e )
249
-		{
250
-			$this->getContext()->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
251
-		}
252
-	}
253
-
254
-
255
-	/**
256
-	 * Returns the sub-client given by its name.
257
-	 *
258
-	 * @param string $type Name of the client type
259
-	 * @param string|null $name Name of the sub-client (Default if null)
260
-	 * @return \Aimeos\Client\Html\Iface Sub-client object
261
-	 */
262
-	public function getSubClient( $type, $name = null )
263
-	{
264
-		/** client/html/checkout/standard/decorators/excludes
265
-		 * Excludes decorators added by the "common" option from the checkout standard html client
266
-		 *
267
-		 * Decorators extend the functionality of a class by adding new aspects
268
-		 * (e.g. log what is currently done), executing the methods of the underlying
269
-		 * class only in certain conditions (e.g. only for logged in users) or
270
-		 * modify what is returned to the caller.
271
-		 *
272
-		 * This option allows you to remove a decorator added via
273
-		 * "client/html/common/decorators/default" before they are wrapped
274
-		 * around the html client.
275
-		 *
276
-		 *  client/html/checkout/standard/decorators/excludes = array( 'decorator1' )
277
-		 *
278
-		 * This would remove the decorator named "decorator1" from the list of
279
-		 * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
280
-		 * "client/html/common/decorators/default" to the html client.
281
-		 *
282
-		 * @param array List of decorator names
283
-		 * @since 2014.05
284
-		 * @category Developer
285
-		 * @see client/html/common/decorators/default
286
-		 * @see client/html/checkout/standard/decorators/global
287
-		 * @see client/html/checkout/standard/decorators/local
288
-		 */
289
-
290
-		/** client/html/checkout/standard/decorators/global
291
-		 * Adds a list of globally available decorators only to the checkout standard html client
292
-		 *
293
-		 * Decorators extend the functionality of a class by adding new aspects
294
-		 * (e.g. log what is currently done), executing the methods of the underlying
295
-		 * class only in certain conditions (e.g. only for logged in users) or
296
-		 * modify what is returned to the caller.
297
-		 *
298
-		 * This option allows you to wrap global decorators
299
-		 * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
300
-		 *
301
-		 *  client/html/checkout/standard/decorators/global = array( 'decorator1' )
302
-		 *
303
-		 * This would add the decorator named "decorator1" defined by
304
-		 * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
305
-		 *
306
-		 * @param array List of decorator names
307
-		 * @since 2014.05
308
-		 * @category Developer
309
-		 * @see client/html/common/decorators/default
310
-		 * @see client/html/checkout/standard/decorators/excludes
311
-		 * @see client/html/checkout/standard/decorators/local
312
-		 */
313
-
314
-		/** client/html/checkout/standard/decorators/local
315
-		 * Adds a list of local decorators only to the checkout standard html client
316
-		 *
317
-		 * Decorators extend the functionality of a class by adding new aspects
318
-		 * (e.g. log what is currently done), executing the methods of the underlying
319
-		 * class only in certain conditions (e.g. only for logged in users) or
320
-		 * modify what is returned to the caller.
321
-		 *
322
-		 * This option allows you to wrap local decorators
323
-		 * ("\Aimeos\Client\Html\Checkout\Decorator\*") around the html client.
324
-		 *
325
-		 *  client/html/checkout/standard/decorators/local = array( 'decorator2' )
326
-		 *
327
-		 * This would add the decorator named "decorator2" defined by
328
-		 * "\Aimeos\Client\Html\Checkout\Decorator\Decorator2" only to the html client.
329
-		 *
330
-		 * @param array List of decorator names
331
-		 * @since 2014.05
332
-		 * @category Developer
333
-		 * @see client/html/common/decorators/default
334
-		 * @see client/html/checkout/standard/decorators/excludes
335
-		 * @see client/html/checkout/standard/decorators/global
336
-		 */
337
-
338
-		return $this->createSubClient( 'checkout/standard/' . $type, $name );
339
-	}
340
-
341
-
342
-	/**
343
-	 * Processes the input, e.g. store given values.
344
-	 * A view must be available and this method doesn't generate any output
345
-	 * besides setting view variables.
346
-	 */
347
-	public function process()
348
-	{
349
-		$view = $this->getView();
350
-		$context = $this->getContext();
351
-
352
-		try
353
-		{
354
-			parent::process();
355
-		}
356
-		catch( \Aimeos\Client\Html\Exception $e )
357
-		{
358
-			$error = array( $this->getContext()->getI18n()->dt( 'client', $e->getMessage() ) );
359
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
360
-		}
361
-		catch( \Aimeos\Controller\Frontend\Exception $e )
362
-		{
363
-			$error = array( $this->getContext()->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
364
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
365
-		}
366
-		catch( \Aimeos\MShop\Plugin\Provider\Exception $e )
367
-		{
368
-			$errors = array( $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ) );
369
-			$errors = array_merge( $errors, $this->translatePluginErrorCodes( $e->getErrorCodes() ) );
370
-
371
-			$view->summaryErrorCodes = $e->getErrorCodes();
372
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $errors;
373
-		}
374
-		catch( \Aimeos\MShop\Exception $e )
375
-		{
376
-			$error = array( $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ) );
377
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
378
-		}
379
-		catch( \Exception $e )
380
-		{
381
-			$context->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
382
-
383
-			$error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
384
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
385
-		}
386
-	}
387
-
388
-
389
-	/**
390
-	 * Returns the list of sub-client names configured for the client.
391
-	 *
392
-	 * @return array List of HTML client names
393
-	 */
394
-	protected function getSubClientNames()
395
-	{
396
-		return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
397
-	}
398
-
399
-
400
-	/**
401
-	 * Sets the necessary parameter values in the view.
402
-	 *
403
-	 * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
404
-	 * @param array &$tags Result array for the list of tags that are associated to the output
405
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
406
-	 * @return \Aimeos\MW\View\Iface Modified view object
407
-	 */
408
-	protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
409
-	{
410
-		if( !isset( $this->cache ) )
411
-		{
412
-			$context = $this->getContext();
413
-
414
-			$basketCntl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
415
-			$view->standardBasket = $basketCntl->get();
416
-
417
-
418
-			/** client/html/checkout/standard/url/step-active
419
-			 * Name of the checkout process step to jump to if no previous step requires attention
420
-			 *
421
-			 * The checkout process consists of several steps which are usually
422
-			 * displayed one by another to the customer. If the data of a step
423
-			 * is already available, then that step is skipped. The active step
424
-			 * is the one that is displayed if all other steps are skipped.
425
-			 *
426
-			 * If one of the previous steps misses some data the customer has
427
-			 * to enter, then this step is displayed first. After providing
428
-			 * the missing data, the whole series of steps are tested again
429
-			 * and if no other step requests attention, the configured active
430
-			 * step will be displayed.
431
-			 *
432
-			 * The order of the steps is determined by the order of sub-parts
433
-			 * that are configured for the checkout client.
434
-			 *
435
-			 * @param string Name of the confirm standard HTML client
436
-			 * @since 2014.07
437
-			 * @category Developer
438
-			 * @category User
439
-			 * @see client/html/checkout/standard/standard/subparts
440
-			 */
441
-			$default = $view->config( 'client/html/checkout/standard/url/step-active', 'summary' );
442
-
443
-			/** client/html/checkout/standard/onepage
444
-			 * Shows all named checkout subparts at once for a one page checkout
445
-			 *
446
-			 * Normally, the checkout process is divided into several steps for entering
447
-			 * addresses, select delivery and payment options as well as showing the
448
-			 * summary page. This enables dependencies between two steps like showing
449
-			 * delivery options based on the address entered by the customer. Furthermore,
450
-			 * this is good way to limit the amount of information displayed which is
451
-			 * preferred by mobile users.
452
-			 *
453
-			 * Contrary to that, a one page checkout displays all information on only
454
-			 * one page and customers get an immediate overview of which information
455
-			 * they have to enter and what options they can select from. This is an
456
-			 * advantage if only a very limited amount of information must be entered
457
-			 * or if there are almost no options to choose from and no dependencies
458
-			 * between exist.
459
-			 *
460
-			 * Using this config options, shop developers are able to define which
461
-			 * checkout subparts are combined to a one page view. Simply add the names
462
-			 * of all checkout subparts to the list. Available checkout subparts for
463
-			 * a one page checkout are:
464
-			 * * address
465
-			 * * delivery
466
-			 * * payment
467
-			 * * summary
468
-			 *
469
-			 * @param array List of checkout subparts name
470
-			 * @since 2015.05
471
-			 * @category Developer
472
-			 */
473
-			$onepage = $view->config( 'client/html/checkout/standard/onepage', array() );
474
-			$onestep = array_shift( $onepage ); // keep the first one page step
475
-
476
-			$steps = (array) $context->getConfig()->get( $this->subPartPath, $this->subPartNames );
477
-			$steps = array_diff( $steps, $onepage ); // remove all remaining steps in $onepage
478
-
479
-			// use first step if default step isn't available
480
-			$default = ( !in_array( $default, $steps ) ? reset( $steps ) : $default );
481
-
482
-			$current = $view->param( 'c_step', $default );
483
-			// use $onestep if current step isn't available due to one page layout
484
-			$current = ( !in_array( $current, $steps ) ? $onestep : $current );
485
-
486
-			$cpos = $cpos = array_search( $current, $steps );
487
-
488
-			if( !isset( $view->standardStepActive )
489
-				|| ( ( $apos = array_search( $view->standardStepActive, $steps ) ) !== false
490
-				&& $cpos !== false && $cpos < $apos )
491
-			) {
492
-				$view->standardStepActive = $current;
493
-			}
494
-
495
-			$view->standardSteps = $steps;
496
-
497
-
498
-			$this->cache = $this->addNavigationUrls( $view, $steps, $view->standardStepActive );
499
-		}
500
-
501
-		return $this->cache;
502
-	}
503
-
504
-
505
-	/**
506
-	 * Adds the "back" and "next" URLs to the view
507
-	 *
508
-	 * @param \Aimeos\MW\View\Iface $view View object
509
-	 * @param array $steps List of checkout step names
510
-	 * @param unknown $activeStep Name of the active step
511
-	 * @return \Aimeos\MW\View\Iface Enhanced view object
512
-	 * @since 2016.05
513
-	 */
514
-	protected function addNavigationUrls( \Aimeos\MW\View\Iface $view, array $steps, $activeStep )
515
-	{
516
-		/** client/html/checkout/standard/url/target
517
-		 * Destination of the URL where the controller specified in the URL is known
518
-		 *
519
-		 * The destination can be a page ID like in a content management system or the
520
-		 * module of a software development framework. This "target" must contain or know
521
-		 * the controller that should be called by the generated URL.
522
-		 *
523
-		 * @param string Destination of the URL
524
-		 * @since 2014.03
525
-		 * @category Developer
526
-		 * @see client/html/checkout/standard/url/controller
527
-		 * @see client/html/checkout/standard/url/action
528
-		 * @see client/html/checkout/standard/url/config
529
-		 */
530
-		$cTarget = $view->config( 'client/html/checkout/standard/url/target' );
531
-
532
-		/** client/html/checkout/standard/url/controller
533
-		 * Name of the controller whose action should be called
534
-		 *
535
-		 * In Model-View-Controller (MVC) applications, the controller contains the methods
536
-		 * that create parts of the output displayed in the generated HTML page. Controller
537
-		 * names are usually alpha-numeric.
538
-		 *
539
-		 * @param string Name of the controller
540
-		 * @since 2014.03
541
-		 * @category Developer
542
-		 * @see client/html/checkout/standard/url/target
543
-		 * @see client/html/checkout/standard/url/action
544
-		 * @see client/html/checkout/standard/url/config
545
-		*/
546
-		$cCntl = $view->config( 'client/html/checkout/standard/url/controller', 'checkout' );
547
-
548
-		/** client/html/checkout/standard/url/action
549
-		 * Name of the action that should create the output
550
-		 *
551
-		 * In Model-View-Controller (MVC) applications, actions are the methods of a
552
-		 * controller that create parts of the output displayed in the generated HTML page.
553
-		 * Action names are usually alpha-numeric.
554
-		 *
555
-		 * @param string Name of the action
556
-		 * @since 2014.03
557
-		 * @category Developer
558
-		 * @see client/html/checkout/standard/url/target
559
-		 * @see client/html/checkout/standard/url/controller
560
-		 * @see client/html/checkout/standard/url/config
561
-		*/
562
-		$cAction = $view->config( 'client/html/checkout/standard/url/action', 'index' );
563
-
564
-		/** client/html/checkout/standard/url/config
565
-		 * Associative list of configuration options used for generating the URL
566
-		 *
567
-		 * You can specify additional options as key/value pairs used when generating
568
-		 * the URLs, like
569
-		 *
570
-		 *  client/html/<clientname>/url/config = array( 'absoluteUri' => true )
571
-		 *
572
-		 * The available key/value pairs depend on the application that embeds the e-commerce
573
-		 * framework. This is because the infrastructure of the application is used for
574
-		 * generating the URLs. The full list of available config options is referenced
575
-		 * in the "see also" section of this page.
576
-		 *
577
-		 * @param string Associative list of configuration options
578
-		 * @since 2014.03
579
-		 * @category Developer
580
-		 * @see client/html/checkout/standard/url/target
581
-		 * @see client/html/checkout/standard/url/controller
582
-		 * @see client/html/checkout/standard/url/action
583
-		 * @see client/html/url/config
584
-		*/
585
-		$cConfig = $view->config( 'client/html/checkout/standard/url/config', array() );
586
-
587
-
588
-		$bTarget = $view->config( 'client/html/basket/standard/url/target' );
589
-		$bCntl = $view->config( 'client/html/basket/standard/url/controller', 'basket' );
590
-		$bAction = $view->config( 'client/html/basket/standard/url/action', 'index' );
591
-		$bConfig = $view->config( 'client/html/basket/standard/url/config', array() );
592
-
593
-
594
-		$step = null;
595
-		do {
596
-			$lastStep = $step;
597
-		}
598
-		while( ( $step = array_shift( $steps ) ) !== null && $step !== $activeStep );
599
-
600
-
601
-		if( $lastStep !== null ) {
602
-			$param = array( 'c_step' => $lastStep );
603
-			$view->standardUrlBack = $view->url( $cTarget, $cCntl, $cAction, $param, array(), $cConfig );
604
-		} else {
605
-			$view->standardUrlBack = $view->url( $bTarget, $bCntl, $bAction, array(), array(), $bConfig );
606
-		}
607
-
608
-		if( !isset( $view->standardUrlNext ) && ( $nextStep = array_shift( $steps ) ) !== null ) {
609
-			$param = array( 'c_step' => $nextStep );
610
-			$view->standardUrlNext = $view->url( $cTarget, $cCntl, $cAction, $param, array(), $cConfig );
611
-		}
612
-		// don't overwrite $view->standardUrlNext so order step URL is used
613
-
614
-		return $view;
615
-	}
25
+    /** client/html/checkout/standard/standard/subparts
26
+     * List of HTML sub-clients rendered within the checkout standard 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/standard/subparts';
59
+
60
+    /** client/html/checkout/standard/address/name
61
+     * Name of the address part used by the checkout standard client implementation
62
+     *
63
+     * Use "Myname" if your class is named "\Aimeos\Client\Html\Checkout\Standard\Address\Myname".
64
+     * The name is case-sensitive and you should avoid camel case names like "MyName".
65
+     *
66
+     * @param string Last part of the client class name
67
+     * @since 2014.03
68
+     * @category Developer
69
+     */
70
+
71
+    /** client/html/checkout/standard/delivery/name
72
+     * Name of the delivery part used by the checkout standard client implementation
73
+     *
74
+     * Use "Myname" if your class is named "\Aimeos\Client\Html\Checkout\Standard\Delivery\Myname".
75
+     * The name is case-sensitive and you should avoid camel case names like "MyName".
76
+     *
77
+     * @param string Last part of the client class name
78
+     * @since 2014.03
79
+     * @category Developer
80
+     */
81
+
82
+    /** client/html/checkout/standard/payment/name
83
+     * Name of the payment part used by the checkout standard client implementation
84
+     *
85
+     * Use "Myname" if your class is named "\Aimeos\Client\Html\Checkout\Standard\Payment\Myname".
86
+     * The name is case-sensitive and you should avoid camel case names like "MyName".
87
+     *
88
+     * @param string Last part of the client class name
89
+     * @since 2014.03
90
+     * @category Developer
91
+     */
92
+
93
+    /** client/html/checkout/standard/summary/name
94
+     * Name of the summary part used by the checkout standard client implementation
95
+     *
96
+     * Use "Myname" if your class is named "\Aimeos\Client\Html\Checkout\Standard\Summary\Myname".
97
+     * The name is case-sensitive and you should avoid camel case names like "MyName".
98
+     *
99
+     * @param string Last part of the client class name
100
+     * @since 2014.03
101
+     * @category Developer
102
+     */
103
+
104
+    /** client/html/checkout/standard/order/name
105
+     * Name of the order part used by the checkout standard client implementation
106
+     *
107
+     * Use "Myname" if your class is named "\Aimeos\Client\Html\Checkout\Standard\Order\Myname".
108
+     * The name is case-sensitive and you should avoid camel case names like "MyName".
109
+     *
110
+     * @param string Last part of the client class name
111
+     * @since 2014.03
112
+     * @category Developer
113
+     */
114
+
115
+    /** client/html/checkout/standard/process/name
116
+     * Name of the process part used by the checkout standard client implementation
117
+     *
118
+     * Use "Myname" if your class is named "\Aimeos\Client\Html\Checkout\Standard\Process\Myname".
119
+     * The name is case-sensitive and you should avoid camel case names like "MyName".
120
+     *
121
+     * @param string Last part of the client class name
122
+     * @since 2015.07
123
+     * @category Developer
124
+     */
125
+    private $subPartNames = array( 'address', 'delivery', 'payment', 'summary', 'order', 'process' );
126
+    private $cache;
127
+
128
+
129
+    /**
130
+     * Returns the HTML code for insertion into the body.
131
+     *
132
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
133
+     * @param array &$tags Result array for the list of tags that are associated to the output
134
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
135
+     * @return string HTML code
136
+     */
137
+    public function getBody( $uid = '', array &$tags = array(), &$expire = null )
138
+    {
139
+        $context = $this->getContext();
140
+        $view = $this->getView();
141
+
142
+        try
143
+        {
144
+            $view = $this->setViewParams( $view, $tags, $expire );
145
+
146
+            $html = '';
147
+            foreach( $this->getSubClients() as $subclient ) {
148
+                $html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
149
+            }
150
+            $view->standardBody = $html;
151
+        }
152
+        catch( \Aimeos\Client\Html\Exception $e )
153
+        {
154
+            $error = array( $this->getContext()->getI18n()->dt( 'client', $e->getMessage() ) );
155
+            $view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
156
+        }
157
+        catch( \Aimeos\Controller\Frontend\Exception $e )
158
+        {
159
+            $error = array( $this->getContext()->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
160
+            $view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
161
+        }
162
+        catch( \Aimeos\MShop\Exception $e )
163
+        {
164
+            $error = array( $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ) );
165
+            $view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
166
+        }
167
+        catch( \Exception $e )
168
+        {
169
+            $context->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
170
+
171
+            $error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
172
+            $view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
173
+        }
174
+
175
+        /** client/html/checkout/standard/standard/template-body
176
+         * Relative path to the HTML body template of the checkout standard client.
177
+         *
178
+         * The template file contains the HTML code and processing instructions
179
+         * to generate the result shown in the body of the frontend. The
180
+         * configuration string is the path to the template file relative
181
+         * to the templates directory (usually in client/html/templates).
182
+         *
183
+         * You can overwrite the template file configuration in extensions and
184
+         * provide alternative templates. These alternative templates should be
185
+         * named like the default one but with the string "standard" replaced by
186
+         * an unique name. You may use the name of your project for this. If
187
+         * you've implemented an alternative client class as well, "standard"
188
+         * should be replaced by the name of the new class.
189
+         *
190
+         * @param string Relative path to the template creating code for the HTML page body
191
+         * @since 2014.03
192
+         * @category Developer
193
+         * @see client/html/checkout/standard/standard/template-header
194
+         */
195
+        $tplconf = 'client/html/checkout/standard/standard/template-body';
196
+        $default = 'checkout/standard/body-default.php';
197
+
198
+        return $view->render( $view->config( $tplconf, $default ) );
199
+    }
200
+
201
+
202
+    /**
203
+     * Returns the HTML string for insertion into the header.
204
+     *
205
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
206
+     * @param array &$tags Result array for the list of tags that are associated to the output
207
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
208
+     * @return string|null String including HTML tags for the header on error
209
+     */
210
+    public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
211
+    {
212
+        try
213
+        {
214
+            $view = $this->setViewParams( $this->getView(), $tags, $expire );
215
+
216
+            $html = '';
217
+            foreach( $this->getSubClients() as $subclient ) {
218
+                $html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
219
+            }
220
+            $view->standardHeader = $html;
221
+
222
+            /** client/html/checkout/standard/standard/template-header
223
+             * Relative path to the HTML header template of the checkout standard client.
224
+             *
225
+             * The template file contains the HTML code and processing instructions
226
+             * to generate the HTML code that is inserted into the HTML page header
227
+             * of the rendered page in the frontend. The configuration string is the
228
+             * path to the template file relative to the templates directory (usually
229
+             * in client/html/templates).
230
+             *
231
+             * You can overwrite the template file configuration in extensions and
232
+             * provide alternative templates. These alternative templates should be
233
+             * named like the default one but with the string "standard" replaced by
234
+             * an unique name. You may use the name of your project for this. If
235
+             * you've implemented an alternative client class as well, "standard"
236
+             * should be replaced by the name of the new class.
237
+             *
238
+             * @param string Relative path to the template creating code for the HTML page head
239
+             * @since 2014.03
240
+             * @category Developer
241
+             * @see client/html/checkout/standard/standard/template-body
242
+             */
243
+            $tplconf = 'client/html/checkout/standard/standard/template-header';
244
+            $default = 'checkout/standard/header-default.php';
245
+
246
+            return $view->render( $view->config( $tplconf, $default ) );
247
+        }
248
+        catch( \Exception $e )
249
+        {
250
+            $this->getContext()->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
251
+        }
252
+    }
253
+
254
+
255
+    /**
256
+     * Returns the sub-client given by its name.
257
+     *
258
+     * @param string $type Name of the client type
259
+     * @param string|null $name Name of the sub-client (Default if null)
260
+     * @return \Aimeos\Client\Html\Iface Sub-client object
261
+     */
262
+    public function getSubClient( $type, $name = null )
263
+    {
264
+        /** client/html/checkout/standard/decorators/excludes
265
+         * Excludes decorators added by the "common" option from the checkout standard html client
266
+         *
267
+         * Decorators extend the functionality of a class by adding new aspects
268
+         * (e.g. log what is currently done), executing the methods of the underlying
269
+         * class only in certain conditions (e.g. only for logged in users) or
270
+         * modify what is returned to the caller.
271
+         *
272
+         * This option allows you to remove a decorator added via
273
+         * "client/html/common/decorators/default" before they are wrapped
274
+         * around the html client.
275
+         *
276
+         *  client/html/checkout/standard/decorators/excludes = array( 'decorator1' )
277
+         *
278
+         * This would remove the decorator named "decorator1" from the list of
279
+         * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
280
+         * "client/html/common/decorators/default" to the html client.
281
+         *
282
+         * @param array List of decorator names
283
+         * @since 2014.05
284
+         * @category Developer
285
+         * @see client/html/common/decorators/default
286
+         * @see client/html/checkout/standard/decorators/global
287
+         * @see client/html/checkout/standard/decorators/local
288
+         */
289
+
290
+        /** client/html/checkout/standard/decorators/global
291
+         * Adds a list of globally available decorators only to the checkout standard html client
292
+         *
293
+         * Decorators extend the functionality of a class by adding new aspects
294
+         * (e.g. log what is currently done), executing the methods of the underlying
295
+         * class only in certain conditions (e.g. only for logged in users) or
296
+         * modify what is returned to the caller.
297
+         *
298
+         * This option allows you to wrap global decorators
299
+         * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
300
+         *
301
+         *  client/html/checkout/standard/decorators/global = array( 'decorator1' )
302
+         *
303
+         * This would add the decorator named "decorator1" defined by
304
+         * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
305
+         *
306
+         * @param array List of decorator names
307
+         * @since 2014.05
308
+         * @category Developer
309
+         * @see client/html/common/decorators/default
310
+         * @see client/html/checkout/standard/decorators/excludes
311
+         * @see client/html/checkout/standard/decorators/local
312
+         */
313
+
314
+        /** client/html/checkout/standard/decorators/local
315
+         * Adds a list of local decorators only to the checkout standard html client
316
+         *
317
+         * Decorators extend the functionality of a class by adding new aspects
318
+         * (e.g. log what is currently done), executing the methods of the underlying
319
+         * class only in certain conditions (e.g. only for logged in users) or
320
+         * modify what is returned to the caller.
321
+         *
322
+         * This option allows you to wrap local decorators
323
+         * ("\Aimeos\Client\Html\Checkout\Decorator\*") around the html client.
324
+         *
325
+         *  client/html/checkout/standard/decorators/local = array( 'decorator2' )
326
+         *
327
+         * This would add the decorator named "decorator2" defined by
328
+         * "\Aimeos\Client\Html\Checkout\Decorator\Decorator2" only to the html client.
329
+         *
330
+         * @param array List of decorator names
331
+         * @since 2014.05
332
+         * @category Developer
333
+         * @see client/html/common/decorators/default
334
+         * @see client/html/checkout/standard/decorators/excludes
335
+         * @see client/html/checkout/standard/decorators/global
336
+         */
337
+
338
+        return $this->createSubClient( 'checkout/standard/' . $type, $name );
339
+    }
340
+
341
+
342
+    /**
343
+     * Processes the input, e.g. store given values.
344
+     * A view must be available and this method doesn't generate any output
345
+     * besides setting view variables.
346
+     */
347
+    public function process()
348
+    {
349
+        $view = $this->getView();
350
+        $context = $this->getContext();
351
+
352
+        try
353
+        {
354
+            parent::process();
355
+        }
356
+        catch( \Aimeos\Client\Html\Exception $e )
357
+        {
358
+            $error = array( $this->getContext()->getI18n()->dt( 'client', $e->getMessage() ) );
359
+            $view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
360
+        }
361
+        catch( \Aimeos\Controller\Frontend\Exception $e )
362
+        {
363
+            $error = array( $this->getContext()->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
364
+            $view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
365
+        }
366
+        catch( \Aimeos\MShop\Plugin\Provider\Exception $e )
367
+        {
368
+            $errors = array( $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ) );
369
+            $errors = array_merge( $errors, $this->translatePluginErrorCodes( $e->getErrorCodes() ) );
370
+
371
+            $view->summaryErrorCodes = $e->getErrorCodes();
372
+            $view->standardErrorList = $view->get( 'standardErrorList', array() ) + $errors;
373
+        }
374
+        catch( \Aimeos\MShop\Exception $e )
375
+        {
376
+            $error = array( $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ) );
377
+            $view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
378
+        }
379
+        catch( \Exception $e )
380
+        {
381
+            $context->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
382
+
383
+            $error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
384
+            $view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
385
+        }
386
+    }
387
+
388
+
389
+    /**
390
+     * Returns the list of sub-client names configured for the client.
391
+     *
392
+     * @return array List of HTML client names
393
+     */
394
+    protected function getSubClientNames()
395
+    {
396
+        return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
397
+    }
398
+
399
+
400
+    /**
401
+     * Sets the necessary parameter values in the view.
402
+     *
403
+     * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
404
+     * @param array &$tags Result array for the list of tags that are associated to the output
405
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
406
+     * @return \Aimeos\MW\View\Iface Modified view object
407
+     */
408
+    protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
409
+    {
410
+        if( !isset( $this->cache ) )
411
+        {
412
+            $context = $this->getContext();
413
+
414
+            $basketCntl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
415
+            $view->standardBasket = $basketCntl->get();
416
+
417
+
418
+            /** client/html/checkout/standard/url/step-active
419
+             * Name of the checkout process step to jump to if no previous step requires attention
420
+             *
421
+             * The checkout process consists of several steps which are usually
422
+             * displayed one by another to the customer. If the data of a step
423
+             * is already available, then that step is skipped. The active step
424
+             * is the one that is displayed if all other steps are skipped.
425
+             *
426
+             * If one of the previous steps misses some data the customer has
427
+             * to enter, then this step is displayed first. After providing
428
+             * the missing data, the whole series of steps are tested again
429
+             * and if no other step requests attention, the configured active
430
+             * step will be displayed.
431
+             *
432
+             * The order of the steps is determined by the order of sub-parts
433
+             * that are configured for the checkout client.
434
+             *
435
+             * @param string Name of the confirm standard HTML client
436
+             * @since 2014.07
437
+             * @category Developer
438
+             * @category User
439
+             * @see client/html/checkout/standard/standard/subparts
440
+             */
441
+            $default = $view->config( 'client/html/checkout/standard/url/step-active', 'summary' );
442
+
443
+            /** client/html/checkout/standard/onepage
444
+             * Shows all named checkout subparts at once for a one page checkout
445
+             *
446
+             * Normally, the checkout process is divided into several steps for entering
447
+             * addresses, select delivery and payment options as well as showing the
448
+             * summary page. This enables dependencies between two steps like showing
449
+             * delivery options based on the address entered by the customer. Furthermore,
450
+             * this is good way to limit the amount of information displayed which is
451
+             * preferred by mobile users.
452
+             *
453
+             * Contrary to that, a one page checkout displays all information on only
454
+             * one page and customers get an immediate overview of which information
455
+             * they have to enter and what options they can select from. This is an
456
+             * advantage if only a very limited amount of information must be entered
457
+             * or if there are almost no options to choose from and no dependencies
458
+             * between exist.
459
+             *
460
+             * Using this config options, shop developers are able to define which
461
+             * checkout subparts are combined to a one page view. Simply add the names
462
+             * of all checkout subparts to the list. Available checkout subparts for
463
+             * a one page checkout are:
464
+             * * address
465
+             * * delivery
466
+             * * payment
467
+             * * summary
468
+             *
469
+             * @param array List of checkout subparts name
470
+             * @since 2015.05
471
+             * @category Developer
472
+             */
473
+            $onepage = $view->config( 'client/html/checkout/standard/onepage', array() );
474
+            $onestep = array_shift( $onepage ); // keep the first one page step
475
+
476
+            $steps = (array) $context->getConfig()->get( $this->subPartPath, $this->subPartNames );
477
+            $steps = array_diff( $steps, $onepage ); // remove all remaining steps in $onepage
478
+
479
+            // use first step if default step isn't available
480
+            $default = ( !in_array( $default, $steps ) ? reset( $steps ) : $default );
481
+
482
+            $current = $view->param( 'c_step', $default );
483
+            // use $onestep if current step isn't available due to one page layout
484
+            $current = ( !in_array( $current, $steps ) ? $onestep : $current );
485
+
486
+            $cpos = $cpos = array_search( $current, $steps );
487
+
488
+            if( !isset( $view->standardStepActive )
489
+                || ( ( $apos = array_search( $view->standardStepActive, $steps ) ) !== false
490
+                && $cpos !== false && $cpos < $apos )
491
+            ) {
492
+                $view->standardStepActive = $current;
493
+            }
494
+
495
+            $view->standardSteps = $steps;
496
+
497
+
498
+            $this->cache = $this->addNavigationUrls( $view, $steps, $view->standardStepActive );
499
+        }
500
+
501
+        return $this->cache;
502
+    }
503
+
504
+
505
+    /**
506
+     * Adds the "back" and "next" URLs to the view
507
+     *
508
+     * @param \Aimeos\MW\View\Iface $view View object
509
+     * @param array $steps List of checkout step names
510
+     * @param unknown $activeStep Name of the active step
511
+     * @return \Aimeos\MW\View\Iface Enhanced view object
512
+     * @since 2016.05
513
+     */
514
+    protected function addNavigationUrls( \Aimeos\MW\View\Iface $view, array $steps, $activeStep )
515
+    {
516
+        /** client/html/checkout/standard/url/target
517
+         * Destination of the URL where the controller specified in the URL is known
518
+         *
519
+         * The destination can be a page ID like in a content management system or the
520
+         * module of a software development framework. This "target" must contain or know
521
+         * the controller that should be called by the generated URL.
522
+         *
523
+         * @param string Destination of the URL
524
+         * @since 2014.03
525
+         * @category Developer
526
+         * @see client/html/checkout/standard/url/controller
527
+         * @see client/html/checkout/standard/url/action
528
+         * @see client/html/checkout/standard/url/config
529
+         */
530
+        $cTarget = $view->config( 'client/html/checkout/standard/url/target' );
531
+
532
+        /** client/html/checkout/standard/url/controller
533
+         * Name of the controller whose action should be called
534
+         *
535
+         * In Model-View-Controller (MVC) applications, the controller contains the methods
536
+         * that create parts of the output displayed in the generated HTML page. Controller
537
+         * names are usually alpha-numeric.
538
+         *
539
+         * @param string Name of the controller
540
+         * @since 2014.03
541
+         * @category Developer
542
+         * @see client/html/checkout/standard/url/target
543
+         * @see client/html/checkout/standard/url/action
544
+         * @see client/html/checkout/standard/url/config
545
+         */
546
+        $cCntl = $view->config( 'client/html/checkout/standard/url/controller', 'checkout' );
547
+
548
+        /** client/html/checkout/standard/url/action
549
+         * Name of the action that should create the output
550
+         *
551
+         * In Model-View-Controller (MVC) applications, actions are the methods of a
552
+         * controller that create parts of the output displayed in the generated HTML page.
553
+         * Action names are usually alpha-numeric.
554
+         *
555
+         * @param string Name of the action
556
+         * @since 2014.03
557
+         * @category Developer
558
+         * @see client/html/checkout/standard/url/target
559
+         * @see client/html/checkout/standard/url/controller
560
+         * @see client/html/checkout/standard/url/config
561
+         */
562
+        $cAction = $view->config( 'client/html/checkout/standard/url/action', 'index' );
563
+
564
+        /** client/html/checkout/standard/url/config
565
+         * Associative list of configuration options used for generating the URL
566
+         *
567
+         * You can specify additional options as key/value pairs used when generating
568
+         * the URLs, like
569
+         *
570
+         *  client/html/<clientname>/url/config = array( 'absoluteUri' => true )
571
+         *
572
+         * The available key/value pairs depend on the application that embeds the e-commerce
573
+         * framework. This is because the infrastructure of the application is used for
574
+         * generating the URLs. The full list of available config options is referenced
575
+         * in the "see also" section of this page.
576
+         *
577
+         * @param string Associative list of configuration options
578
+         * @since 2014.03
579
+         * @category Developer
580
+         * @see client/html/checkout/standard/url/target
581
+         * @see client/html/checkout/standard/url/controller
582
+         * @see client/html/checkout/standard/url/action
583
+         * @see client/html/url/config
584
+         */
585
+        $cConfig = $view->config( 'client/html/checkout/standard/url/config', array() );
586
+
587
+
588
+        $bTarget = $view->config( 'client/html/basket/standard/url/target' );
589
+        $bCntl = $view->config( 'client/html/basket/standard/url/controller', 'basket' );
590
+        $bAction = $view->config( 'client/html/basket/standard/url/action', 'index' );
591
+        $bConfig = $view->config( 'client/html/basket/standard/url/config', array() );
592
+
593
+
594
+        $step = null;
595
+        do {
596
+            $lastStep = $step;
597
+        }
598
+        while( ( $step = array_shift( $steps ) ) !== null && $step !== $activeStep );
599
+
600
+
601
+        if( $lastStep !== null ) {
602
+            $param = array( 'c_step' => $lastStep );
603
+            $view->standardUrlBack = $view->url( $cTarget, $cCntl, $cAction, $param, array(), $cConfig );
604
+        } else {
605
+            $view->standardUrlBack = $view->url( $bTarget, $bCntl, $bAction, array(), array(), $bConfig );
606
+        }
607
+
608
+        if( !isset( $view->standardUrlNext ) && ( $nextStep = array_shift( $steps ) ) !== null ) {
609
+            $param = array( 'c_step' => $nextStep );
610
+            $view->standardUrlNext = $view->url( $cTarget, $cCntl, $cAction, $param, array(), $cConfig );
611
+        }
612
+        // don't overwrite $view->standardUrlNext so order step URL is used
613
+
614
+        return $view;
615
+    }
616 616
 }
617 617
\ No newline at end of file
Please login to merge, or discard this patch.
Spacing   +79 added lines, -79 removed lines patch added patch discarded remove patch
@@ -122,7 +122,7 @@  discard block
 block discarded – undo
122 122
 	 * @since 2015.07
123 123
 	 * @category Developer
124 124
 	 */
125
-	private $subPartNames = array( 'address', 'delivery', 'payment', 'summary', 'order', 'process' );
125
+	private $subPartNames = array('address', 'delivery', 'payment', 'summary', 'order', 'process');
126 126
 	private $cache;
127 127
 
128 128
 
@@ -134,42 +134,42 @@  discard block
 block discarded – undo
134 134
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
135 135
 	 * @return string HTML code
136 136
 	 */
137
-	public function getBody( $uid = '', array &$tags = array(), &$expire = null )
137
+	public function getBody($uid = '', array &$tags = array(), &$expire = null)
138 138
 	{
139 139
 		$context = $this->getContext();
140 140
 		$view = $this->getView();
141 141
 
142 142
 		try
143 143
 		{
144
-			$view = $this->setViewParams( $view, $tags, $expire );
144
+			$view = $this->setViewParams($view, $tags, $expire);
145 145
 
146 146
 			$html = '';
147
-			foreach( $this->getSubClients() as $subclient ) {
148
-				$html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
147
+			foreach ($this->getSubClients() as $subclient) {
148
+				$html .= $subclient->setView($view)->getBody($uid, $tags, $expire);
149 149
 			}
150 150
 			$view->standardBody = $html;
151 151
 		}
152
-		catch( \Aimeos\Client\Html\Exception $e )
152
+		catch (\Aimeos\Client\Html\Exception $e)
153 153
 		{
154
-			$error = array( $this->getContext()->getI18n()->dt( 'client', $e->getMessage() ) );
155
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
154
+			$error = array($this->getContext()->getI18n()->dt('client', $e->getMessage()));
155
+			$view->standardErrorList = $view->get('standardErrorList', array()) + $error;
156 156
 		}
157
-		catch( \Aimeos\Controller\Frontend\Exception $e )
157
+		catch (\Aimeos\Controller\Frontend\Exception $e)
158 158
 		{
159
-			$error = array( $this->getContext()->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
160
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
159
+			$error = array($this->getContext()->getI18n()->dt('controller/frontend', $e->getMessage()));
160
+			$view->standardErrorList = $view->get('standardErrorList', array()) + $error;
161 161
 		}
162
-		catch( \Aimeos\MShop\Exception $e )
162
+		catch (\Aimeos\MShop\Exception $e)
163 163
 		{
164
-			$error = array( $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ) );
165
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
164
+			$error = array($this->getContext()->getI18n()->dt('mshop', $e->getMessage()));
165
+			$view->standardErrorList = $view->get('standardErrorList', array()) + $error;
166 166
 		}
167
-		catch( \Exception $e )
167
+		catch (\Exception $e)
168 168
 		{
169
-			$context->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
169
+			$context->getLogger()->log($e->getMessage().PHP_EOL.$e->getTraceAsString());
170 170
 
171
-			$error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
172
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
171
+			$error = array($context->getI18n()->dt('client', 'A non-recoverable error occured'));
172
+			$view->standardErrorList = $view->get('standardErrorList', array()) + $error;
173 173
 		}
174 174
 
175 175
 		/** client/html/checkout/standard/standard/template-body
@@ -195,7 +195,7 @@  discard block
 block discarded – undo
195 195
 		$tplconf = 'client/html/checkout/standard/standard/template-body';
196 196
 		$default = 'checkout/standard/body-default.php';
197 197
 
198
-		return $view->render( $view->config( $tplconf, $default ) );
198
+		return $view->render($view->config($tplconf, $default));
199 199
 	}
200 200
 
201 201
 
@@ -207,15 +207,15 @@  discard block
 block discarded – undo
207 207
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
208 208
 	 * @return string|null String including HTML tags for the header on error
209 209
 	 */
210
-	public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
210
+	public function getHeader($uid = '', array &$tags = array(), &$expire = null)
211 211
 	{
212 212
 		try
213 213
 		{
214
-			$view = $this->setViewParams( $this->getView(), $tags, $expire );
214
+			$view = $this->setViewParams($this->getView(), $tags, $expire);
215 215
 
216 216
 			$html = '';
217
-			foreach( $this->getSubClients() as $subclient ) {
218
-				$html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
217
+			foreach ($this->getSubClients() as $subclient) {
218
+				$html .= $subclient->setView($view)->getHeader($uid, $tags, $expire);
219 219
 			}
220 220
 			$view->standardHeader = $html;
221 221
 
@@ -243,11 +243,11 @@  discard block
 block discarded – undo
243 243
 			$tplconf = 'client/html/checkout/standard/standard/template-header';
244 244
 			$default = 'checkout/standard/header-default.php';
245 245
 
246
-			return $view->render( $view->config( $tplconf, $default ) );
246
+			return $view->render($view->config($tplconf, $default));
247 247
 		}
248
-		catch( \Exception $e )
248
+		catch (\Exception $e)
249 249
 		{
250
-			$this->getContext()->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
250
+			$this->getContext()->getLogger()->log($e->getMessage().PHP_EOL.$e->getTraceAsString());
251 251
 		}
252 252
 	}
253 253
 
@@ -259,7 +259,7 @@  discard block
 block discarded – undo
259 259
 	 * @param string|null $name Name of the sub-client (Default if null)
260 260
 	 * @return \Aimeos\Client\Html\Iface Sub-client object
261 261
 	 */
262
-	public function getSubClient( $type, $name = null )
262
+	public function getSubClient($type, $name = null)
263 263
 	{
264 264
 		/** client/html/checkout/standard/decorators/excludes
265 265
 		 * Excludes decorators added by the "common" option from the checkout standard html client
@@ -335,7 +335,7 @@  discard block
 block discarded – undo
335 335
 		 * @see client/html/checkout/standard/decorators/global
336 336
 		 */
337 337
 
338
-		return $this->createSubClient( 'checkout/standard/' . $type, $name );
338
+		return $this->createSubClient('checkout/standard/'.$type, $name);
339 339
 	}
340 340
 
341 341
 
@@ -353,35 +353,35 @@  discard block
 block discarded – undo
353 353
 		{
354 354
 			parent::process();
355 355
 		}
356
-		catch( \Aimeos\Client\Html\Exception $e )
356
+		catch (\Aimeos\Client\Html\Exception $e)
357 357
 		{
358
-			$error = array( $this->getContext()->getI18n()->dt( 'client', $e->getMessage() ) );
359
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
358
+			$error = array($this->getContext()->getI18n()->dt('client', $e->getMessage()));
359
+			$view->standardErrorList = $view->get('standardErrorList', array()) + $error;
360 360
 		}
361
-		catch( \Aimeos\Controller\Frontend\Exception $e )
361
+		catch (\Aimeos\Controller\Frontend\Exception $e)
362 362
 		{
363
-			$error = array( $this->getContext()->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
364
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
363
+			$error = array($this->getContext()->getI18n()->dt('controller/frontend', $e->getMessage()));
364
+			$view->standardErrorList = $view->get('standardErrorList', array()) + $error;
365 365
 		}
366
-		catch( \Aimeos\MShop\Plugin\Provider\Exception $e )
366
+		catch (\Aimeos\MShop\Plugin\Provider\Exception $e)
367 367
 		{
368
-			$errors = array( $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ) );
369
-			$errors = array_merge( $errors, $this->translatePluginErrorCodes( $e->getErrorCodes() ) );
368
+			$errors = array($this->getContext()->getI18n()->dt('mshop', $e->getMessage()));
369
+			$errors = array_merge($errors, $this->translatePluginErrorCodes($e->getErrorCodes()));
370 370
 
371 371
 			$view->summaryErrorCodes = $e->getErrorCodes();
372
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $errors;
372
+			$view->standardErrorList = $view->get('standardErrorList', array()) + $errors;
373 373
 		}
374
-		catch( \Aimeos\MShop\Exception $e )
374
+		catch (\Aimeos\MShop\Exception $e)
375 375
 		{
376
-			$error = array( $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ) );
377
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
376
+			$error = array($this->getContext()->getI18n()->dt('mshop', $e->getMessage()));
377
+			$view->standardErrorList = $view->get('standardErrorList', array()) + $error;
378 378
 		}
379
-		catch( \Exception $e )
379
+		catch (\Exception $e)
380 380
 		{
381
-			$context->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
381
+			$context->getLogger()->log($e->getMessage().PHP_EOL.$e->getTraceAsString());
382 382
 
383
-			$error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
384
-			$view->standardErrorList = $view->get( 'standardErrorList', array() ) + $error;
383
+			$error = array($context->getI18n()->dt('client', 'A non-recoverable error occured'));
384
+			$view->standardErrorList = $view->get('standardErrorList', array()) + $error;
385 385
 		}
386 386
 	}
387 387
 
@@ -393,7 +393,7 @@  discard block
 block discarded – undo
393 393
 	 */
394 394
 	protected function getSubClientNames()
395 395
 	{
396
-		return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
396
+		return $this->getContext()->getConfig()->get($this->subPartPath, $this->subPartNames);
397 397
 	}
398 398
 
399 399
 
@@ -405,13 +405,13 @@  discard block
 block discarded – undo
405 405
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
406 406
 	 * @return \Aimeos\MW\View\Iface Modified view object
407 407
 	 */
408
-	protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
408
+	protected function setViewParams(\Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null)
409 409
 	{
410
-		if( !isset( $this->cache ) )
410
+		if (!isset($this->cache))
411 411
 		{
412 412
 			$context = $this->getContext();
413 413
 
414
-			$basketCntl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
414
+			$basketCntl = \Aimeos\Controller\Frontend\Factory::createController($context, 'basket');
415 415
 			$view->standardBasket = $basketCntl->get();
416 416
 
417 417
 
@@ -438,7 +438,7 @@  discard block
 block discarded – undo
438 438
 			 * @category User
439 439
 			 * @see client/html/checkout/standard/standard/subparts
440 440
 			 */
441
-			$default = $view->config( 'client/html/checkout/standard/url/step-active', 'summary' );
441
+			$default = $view->config('client/html/checkout/standard/url/step-active', 'summary');
442 442
 
443 443
 			/** client/html/checkout/standard/onepage
444 444
 			 * Shows all named checkout subparts at once for a one page checkout
@@ -470,24 +470,24 @@  discard block
 block discarded – undo
470 470
 			 * @since 2015.05
471 471
 			 * @category Developer
472 472
 			 */
473
-			$onepage = $view->config( 'client/html/checkout/standard/onepage', array() );
474
-			$onestep = array_shift( $onepage ); // keep the first one page step
473
+			$onepage = $view->config('client/html/checkout/standard/onepage', array());
474
+			$onestep = array_shift($onepage); // keep the first one page step
475 475
 
476
-			$steps = (array) $context->getConfig()->get( $this->subPartPath, $this->subPartNames );
477
-			$steps = array_diff( $steps, $onepage ); // remove all remaining steps in $onepage
476
+			$steps = (array) $context->getConfig()->get($this->subPartPath, $this->subPartNames);
477
+			$steps = array_diff($steps, $onepage); // remove all remaining steps in $onepage
478 478
 
479 479
 			// use first step if default step isn't available
480
-			$default = ( !in_array( $default, $steps ) ? reset( $steps ) : $default );
480
+			$default = (!in_array($default, $steps) ? reset($steps) : $default);
481 481
 
482
-			$current = $view->param( 'c_step', $default );
482
+			$current = $view->param('c_step', $default);
483 483
 			// use $onestep if current step isn't available due to one page layout
484
-			$current = ( !in_array( $current, $steps ) ? $onestep : $current );
484
+			$current = (!in_array($current, $steps) ? $onestep : $current);
485 485
 
486
-			$cpos = $cpos = array_search( $current, $steps );
486
+			$cpos = $cpos = array_search($current, $steps);
487 487
 
488
-			if( !isset( $view->standardStepActive )
489
-				|| ( ( $apos = array_search( $view->standardStepActive, $steps ) ) !== false
490
-				&& $cpos !== false && $cpos < $apos )
488
+			if (!isset($view->standardStepActive)
489
+				|| (($apos = array_search($view->standardStepActive, $steps)) !== false
490
+				&& $cpos !== false && $cpos < $apos)
491 491
 			) {
492 492
 				$view->standardStepActive = $current;
493 493
 			}
@@ -495,7 +495,7 @@  discard block
 block discarded – undo
495 495
 			$view->standardSteps = $steps;
496 496
 
497 497
 
498
-			$this->cache = $this->addNavigationUrls( $view, $steps, $view->standardStepActive );
498
+			$this->cache = $this->addNavigationUrls($view, $steps, $view->standardStepActive);
499 499
 		}
500 500
 
501 501
 		return $this->cache;
@@ -511,7 +511,7 @@  discard block
 block discarded – undo
511 511
 	 * @return \Aimeos\MW\View\Iface Enhanced view object
512 512
 	 * @since 2016.05
513 513
 	 */
514
-	protected function addNavigationUrls( \Aimeos\MW\View\Iface $view, array $steps, $activeStep )
514
+	protected function addNavigationUrls(\Aimeos\MW\View\Iface $view, array $steps, $activeStep)
515 515
 	{
516 516
 		/** client/html/checkout/standard/url/target
517 517
 		 * Destination of the URL where the controller specified in the URL is known
@@ -527,7 +527,7 @@  discard block
 block discarded – undo
527 527
 		 * @see client/html/checkout/standard/url/action
528 528
 		 * @see client/html/checkout/standard/url/config
529 529
 		 */
530
-		$cTarget = $view->config( 'client/html/checkout/standard/url/target' );
530
+		$cTarget = $view->config('client/html/checkout/standard/url/target');
531 531
 
532 532
 		/** client/html/checkout/standard/url/controller
533 533
 		 * Name of the controller whose action should be called
@@ -543,7 +543,7 @@  discard block
 block discarded – undo
543 543
 		 * @see client/html/checkout/standard/url/action
544 544
 		 * @see client/html/checkout/standard/url/config
545 545
 		*/
546
-		$cCntl = $view->config( 'client/html/checkout/standard/url/controller', 'checkout' );
546
+		$cCntl = $view->config('client/html/checkout/standard/url/controller', 'checkout');
547 547
 
548 548
 		/** client/html/checkout/standard/url/action
549 549
 		 * Name of the action that should create the output
@@ -559,7 +559,7 @@  discard block
 block discarded – undo
559 559
 		 * @see client/html/checkout/standard/url/controller
560 560
 		 * @see client/html/checkout/standard/url/config
561 561
 		*/
562
-		$cAction = $view->config( 'client/html/checkout/standard/url/action', 'index' );
562
+		$cAction = $view->config('client/html/checkout/standard/url/action', 'index');
563 563
 
564 564
 		/** client/html/checkout/standard/url/config
565 565
 		 * Associative list of configuration options used for generating the URL
@@ -582,32 +582,32 @@  discard block
 block discarded – undo
582 582
 		 * @see client/html/checkout/standard/url/action
583 583
 		 * @see client/html/url/config
584 584
 		*/
585
-		$cConfig = $view->config( 'client/html/checkout/standard/url/config', array() );
585
+		$cConfig = $view->config('client/html/checkout/standard/url/config', array());
586 586
 
587 587
 
588
-		$bTarget = $view->config( 'client/html/basket/standard/url/target' );
589
-		$bCntl = $view->config( 'client/html/basket/standard/url/controller', 'basket' );
590
-		$bAction = $view->config( 'client/html/basket/standard/url/action', 'index' );
591
-		$bConfig = $view->config( 'client/html/basket/standard/url/config', array() );
588
+		$bTarget = $view->config('client/html/basket/standard/url/target');
589
+		$bCntl = $view->config('client/html/basket/standard/url/controller', 'basket');
590
+		$bAction = $view->config('client/html/basket/standard/url/action', 'index');
591
+		$bConfig = $view->config('client/html/basket/standard/url/config', array());
592 592
 
593 593
 
594 594
 		$step = null;
595 595
 		do {
596 596
 			$lastStep = $step;
597 597
 		}
598
-		while( ( $step = array_shift( $steps ) ) !== null && $step !== $activeStep );
598
+		while (($step = array_shift($steps)) !== null && $step !== $activeStep);
599 599
 
600 600
 
601
-		if( $lastStep !== null ) {
602
-			$param = array( 'c_step' => $lastStep );
603
-			$view->standardUrlBack = $view->url( $cTarget, $cCntl, $cAction, $param, array(), $cConfig );
601
+		if ($lastStep !== null) {
602
+			$param = array('c_step' => $lastStep);
603
+			$view->standardUrlBack = $view->url($cTarget, $cCntl, $cAction, $param, array(), $cConfig);
604 604
 		} else {
605
-			$view->standardUrlBack = $view->url( $bTarget, $bCntl, $bAction, array(), array(), $bConfig );
605
+			$view->standardUrlBack = $view->url($bTarget, $bCntl, $bAction, array(), array(), $bConfig);
606 606
 		}
607 607
 
608
-		if( !isset( $view->standardUrlNext ) && ( $nextStep = array_shift( $steps ) ) !== null ) {
609
-			$param = array( 'c_step' => $nextStep );
610
-			$view->standardUrlNext = $view->url( $cTarget, $cCntl, $cAction, $param, array(), $cConfig );
608
+		if (!isset($view->standardUrlNext) && ($nextStep = array_shift($steps)) !== null) {
609
+			$param = array('c_step' => $nextStep);
610
+			$view->standardUrlNext = $view->url($cTarget, $cCntl, $cAction, $param, array(), $cConfig);
611 611
 		}
612 612
 		// don't overwrite $view->standardUrlNext so order step URL is used
613 613
 
Please login to merge, or discard this patch.
client/html/src/Client/Html/Checkout/Standard/Address/Delivery/Standard.php 3 patches
Indentation   +762 added lines, -762 removed lines patch added patch discarded remove patch
@@ -19,767 +19,767 @@
 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/delivery/standard/subparts
26
-	 * List of HTML sub-clients rendered within the checkout standard address delivery 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/delivery/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
-	);
71
-
72
-	private $optional = array(
73
-		'order.base.address.company',
74
-		'order.base.address.vatid',
75
-		'order.base.address.address2',
76
-		'order.base.address.countryid',
77
-		'order.base.address.state',
78
-	);
79
-
80
-
81
-	/**
82
-	 * Returns the HTML code for insertion into the body.
83
-	 *
84
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
85
-	 * @param array &$tags Result array for the list of tags that are associated to the output
86
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
87
-	 * @return string HTML code
88
-	 */
89
-	public function getBody( $uid = '', array &$tags = array(), &$expire = null )
90
-	{
91
-		$view = $this->setViewParams( $this->getView(), $tags, $expire );
92
-
93
-		$html = '';
94
-		foreach( $this->getSubClients() as $subclient ) {
95
-			$html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
96
-		}
97
-		$view->deliveryBody = $html;
98
-
99
-		/** client/html/checkout/standard/address/delivery/standard/template-body
100
-		 * Relative path to the HTML body template of the checkout standard address delivery client.
101
-		 *
102
-		 * The template file contains the HTML code and processing instructions
103
-		 * to generate the result shown in the body of the frontend. The
104
-		 * configuration string is the path to the template file relative
105
-		 * to the templates directory (usually in client/html/templates).
106
-		 *
107
-		 * You can overwrite the template file configuration in extensions and
108
-		 * provide alternative templates. These alternative templates should be
109
-		 * named like the default one but with the string "standard" replaced by
110
-		 * an unique name. You may use the name of your project for this. If
111
-		 * you've implemented an alternative client class as well, "standard"
112
-		 * should be replaced by the name of the new class.
113
-		 *
114
-		 * @param string Relative path to the template creating code for the HTML page body
115
-		 * @since 2014.03
116
-		 * @category Developer
117
-		 * @see client/html/checkout/standard/address/delivery/standard/template-header
118
-		 */
119
-		$tplconf = 'client/html/checkout/standard/address/delivery/standard/template-body';
120
-		$default = 'checkout/standard/address-delivery-body-default.php';
121
-
122
-		return $view->render( $view->config( $tplconf, $default ) );
123
-	}
124
-
125
-
126
-	/**
127
-	 * Returns the HTML string for insertion into the header.
128
-	 *
129
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
130
-	 * @param array &$tags Result array for the list of tags that are associated to the output
131
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
132
-	 * @return string|null String including HTML tags for the header on error
133
-	 */
134
-	public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
135
-	{
136
-		$view = $this->setViewParams( $this->getView(), $tags, $expire );
137
-
138
-		$html = '';
139
-		foreach( $this->getSubClients() as $subclient ) {
140
-			$html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
141
-		}
142
-		$view->deliveryHeader = $html;
143
-
144
-		/** client/html/checkout/standard/address/delivery/standard/template-header
145
-		 * Relative path to the HTML header template of the checkout standard address delivery client.
146
-		 *
147
-		 * The template file contains the HTML code and processing instructions
148
-		 * to generate the HTML code that is inserted into the HTML page header
149
-		 * of the rendered page in the frontend. The configuration string is the
150
-		 * path to the template file relative to the templates directory (usually
151
-		 * in client/html/templates).
152
-		 *
153
-		 * You can overwrite the template file configuration in extensions and
154
-		 * provide alternative templates. These alternative templates should be
155
-		 * named like the default one but with the string "standard" replaced by
156
-		 * an unique name. You may use the name of your project for this. If
157
-		 * you've implemented an alternative client class as well, "standard"
158
-		 * should be replaced by the name of the new class.
159
-		 *
160
-		 * @param string Relative path to the template creating code for the HTML page head
161
-		 * @since 2014.03
162
-		 * @category Developer
163
-		 * @see client/html/checkout/standard/address/delivery/standard/template-body
164
-		 */
165
-		$tplconf = 'client/html/checkout/standard/address/delivery/standard/template-header';
166
-		$default = 'checkout/standard/address-delivery-header-default.php';
167
-
168
-		return $view->render( $view->config( $tplconf, $default ) );
169
-	}
170
-
171
-
172
-	/**
173
-	 * Returns the sub-client given by its name.
174
-	 *
175
-	 * @param string $type Name of the client type
176
-	 * @param string|null $name Name of the sub-client (Default if null)
177
-	 * @return \Aimeos\Client\Html\Iface Sub-client object
178
-	 */
179
-	public function getSubClient( $type, $name = null )
180
-	{
181
-		/** client/html/checkout/standard/address/delivery/decorators/excludes
182
-		 * Excludes decorators added by the "common" option from the checkout standard address delivery html client
183
-		 *
184
-		 * Decorators extend the functionality of a class by adding new aspects
185
-		 * (e.g. log what is currently done), executing the methods of the underlying
186
-		 * class only in certain conditions (e.g. only for logged in users) or
187
-		 * modify what is returned to the caller.
188
-		 *
189
-		 * This option allows you to remove a decorator added via
190
-		 * "client/html/common/decorators/default" before they are wrapped
191
-		 * around the html client.
192
-		 *
193
-		 *  client/html/checkout/standard/address/delivery/decorators/excludes = array( 'decorator1' )
194
-		 *
195
-		 * This would remove the decorator named "decorator1" from the list of
196
-		 * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
197
-		 * "client/html/common/decorators/default" to the html client.
198
-		 *
199
-		 * @param array List of decorator names
200
-		 * @since 2015.08
201
-		 * @category Developer
202
-		 * @see client/html/common/decorators/default
203
-		 * @see client/html/checkout/standard/address/delivery/decorators/global
204
-		 * @see client/html/checkout/standard/address/delivery/decorators/local
205
-		 */
206
-
207
-		/** client/html/checkout/standard/address/delivery/decorators/global
208
-		 * Adds a list of globally available decorators only to the checkout standard address delivery html client
209
-		 *
210
-		 * Decorators extend the functionality of a class by adding new aspects
211
-		 * (e.g. log what is currently done), executing the methods of the underlying
212
-		 * class only in certain conditions (e.g. only for logged in users) or
213
-		 * modify what is returned to the caller.
214
-		 *
215
-		 * This option allows you to wrap global decorators
216
-		 * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
217
-		 *
218
-		 *  client/html/checkout/standard/address/delivery/decorators/global = array( 'decorator1' )
219
-		 *
220
-		 * This would add the decorator named "decorator1" defined by
221
-		 * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
222
-		 *
223
-		 * @param array List of decorator names
224
-		 * @since 2015.08
225
-		 * @category Developer
226
-		 * @see client/html/common/decorators/default
227
-		 * @see client/html/checkout/standard/address/delivery/decorators/excludes
228
-		 * @see client/html/checkout/standard/address/delivery/decorators/local
229
-		 */
230
-
231
-		/** client/html/checkout/standard/address/delivery/decorators/local
232
-		 * Adds a list of local decorators only to the checkout standard address delivery html client
233
-		 *
234
-		 * Decorators extend the functionality of a class by adding new aspects
235
-		 * (e.g. log what is currently done), executing the methods of the underlying
236
-		 * class only in certain conditions (e.g. only for logged in users) or
237
-		 * modify what is returned to the caller.
238
-		 *
239
-		 * This option allows you to wrap local decorators
240
-		 * ("\Aimeos\Client\Html\Checkout\Decorator\*") around the html client.
241
-		 *
242
-		 *  client/html/checkout/standard/address/delivery/decorators/local = array( 'decorator2' )
243
-		 *
244
-		 * This would add the decorator named "decorator2" defined by
245
-		 * "\Aimeos\Client\Html\Checkout\Decorator\Decorator2" only to the html client.
246
-		 *
247
-		 * @param array List of decorator names
248
-		 * @since 2015.08
249
-		 * @category Developer
250
-		 * @see client/html/common/decorators/default
251
-		 * @see client/html/checkout/standard/address/delivery/decorators/excludes
252
-		 * @see client/html/checkout/standard/address/delivery/decorators/global
253
-		 */
254
-
255
-		return $this->createSubClient( 'checkout/standard/address/delivery/' . $type, $name );
256
-	}
257
-
258
-
259
-	/**
260
-	 * Stores the given or fetched billing address in the basket.
261
-	 */
262
-	public function process()
263
-	{
264
-		$context = $this->getContext();
265
-		$view = $this->getView();
266
-
267
-		try
268
-		{
269
-			if( ( $id = $view->param( 'ca_delivery_delete', null ) ) !== null )
270
-			{
271
-				$customerAddressManager = \Aimeos\MShop\Factory::createManager( $context, 'customer/address' );
272
-				$address = $customerAddressManager->getItem( $id );
273
-
274
-				if( $address->getParentId() != $context->getUserId() ) {
275
-					throw new \Aimeos\Client\Html\Exception( sprintf( 'Address with ID "%1$s" not found', $id ) );
276
-				}
277
-
278
-				$customerAddressManager->deleteItem( $id );
279
-			}
280
-
281
-			// only start if there's something to do
282
-			if( $view->param( 'ca_deliveryoption', null ) === null ) {
283
-				return;
284
-			}
285
-
286
-			$this->setAddress( $view );
287
-
288
-			parent::process();
289
-		}
290
-		catch( \Aimeos\Controller\Frontend\Exception $e )
291
-		{
292
-			$view->deliveryError = $e->getErrorList();
293
-			throw $e;
294
-		}
295
-	}
296
-
297
-
298
-	/**
299
-	 * Checks the address fields for missing data and sanitizes the given parameter list.
300
-	 *
301
-	 * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
302
-	 * @return array List of missing field names
303
-	 */
304
-	protected function checkFields( array &$params )
305
-	{
306
-		$view = $this->getView();
307
-
308
-		/** client/html/checkout/standard/address/delivery/mandatory
309
-		 * List of delivery address input fields that are required
310
-		 *
311
-		 * You can configure the list of delivery address fields that are
312
-		 * necessary and must be filled by the customer before he can
313
-		 * continue the checkout process. Available field keys are:
314
-		 * * order.base.address.company
315
-		 * * order.base.address.vatid
316
-		 * * order.base.address.salutation
317
-		 * * order.base.address.firstname
318
-		 * * order.base.address.lastname
319
-		 * * order.base.address.address1
320
-		 * * order.base.address.address2
321
-		 * * order.base.address.address3
322
-		 * * order.base.address.postal
323
-		 * * order.base.address.city
324
-		 * * order.base.address.state
325
-		 * * order.base.address.languageid
326
-		 * * order.base.address.countryid
327
-		 * * order.base.address.telephone
328
-		 * * order.base.address.telefax
329
-		 * * order.base.address.email
330
-		 * * order.base.address.website
331
-		 *
332
-		 * Until 2015-02, the configuration option was available as
333
-		 * "client/html/common/address/delivery/mandatory" starting from 2014-03.
334
-		 *
335
-		 * @param array List of field keys
336
-		 * @since 2015.02
337
-		 * @category User
338
-		 * @category Developer
339
-		 * @see client/html/checkout/standard/address/delivery/disable-new
340
-		 * @see client/html/checkout/standard/address/delivery/salutations
341
-		 * @see client/html/checkout/standard/address/delivery/optional
342
-		 * @see client/html/checkout/standard/address/delivery/hidden
343
-		 * @see client/html/checkout/standard/address/countries
344
-		 * @see client/html/checkout/standard/address/validate
345
-		 */
346
-		$mandatory = $view->config( 'client/html/checkout/standard/address/delivery/mandatory', $this->mandatory );
347
-
348
-		/** client/html/checkout/standard/address/delivery/optional
349
-		 * List of delivery address input fields that are optional
350
-		 *
351
-		 * You can configure the list of delivery address fields that
352
-		 * customers can fill but don't have to before they can
353
-		 * continue the checkout process. Available field keys are:
354
-		 * * order.base.address.company
355
-		 * * order.base.address.vatid
356
-		 * * order.base.address.salutation
357
-		 * * order.base.address.firstname
358
-		 * * order.base.address.lastname
359
-		 * * order.base.address.address1
360
-		 * * order.base.address.address2
361
-		 * * order.base.address.address3
362
-		 * * order.base.address.postal
363
-		 * * order.base.address.city
364
-		 * * order.base.address.state
365
-		 * * order.base.address.languageid
366
-		 * * order.base.address.countryid
367
-		 * * order.base.address.telephone
368
-		 * * order.base.address.telefax
369
-		 * * order.base.address.email
370
-		 * * order.base.address.website
371
-		 *
372
-		 * Until 2015-02, the configuration option was available as
373
-		 * "client/html/common/address/delivery/optional" starting from 2014-03.
374
-		 *
375
-		 * @param array List of field keys
376
-		 * @since 2015.02
377
-		 * @category User
378
-		 * @category Developer
379
-		 * @see client/html/checkout/standard/address/delivery/disable-new
380
-		 * @see client/html/checkout/standard/address/delivery/salutations
381
-		 * @see client/html/checkout/standard/address/delivery/mandatory
382
-		 * @see client/html/checkout/standard/address/delivery/hidden
383
-		 * @see client/html/checkout/standard/address/countries
384
-		 * @see client/html/checkout/standard/address/validate
385
-		 */
386
-		$optional = $view->config( 'client/html/checkout/standard/address/delivery/optional', $this->optional );
387
-
388
-		/** client/html/checkout/standard/address/validate
389
-		 *
390
-		 * @see client/html/checkout/standard/address/delivery/mandatory
391
-		 * @see client/html/checkout/standard/address/delivery/optional
392
-		 */
393
-
394
-		$allFields = array_flip( array_merge( $mandatory, $optional ) );
395
-		$invalid = $this->validateFields( $params, $allFields );
396
-		$this->checkSalutation( $params, $mandatory );
397
-
398
-		foreach( $invalid as $key => $name )
399
-		{
400
-			$msg = $view->translate( 'client', 'Delivery address part "%1$s" is invalid' );
401
-			$invalid[$key] = sprintf( $msg, $name );
402
-		}
403
-
404
-		foreach( $mandatory as $key )
405
-		{
406
-			if( !isset( $params[$key] ) || $params[$key] == '' )
407
-			{
408
-				$msg = $view->translate( 'client', 'Delivery address part "%1$s" is missing' );
409
-				$invalid[$key] = sprintf( $msg, substr( $key, 19 ) );
410
-				unset( $params[$key] );
411
-			}
412
-		}
413
-
414
-		return $invalid;
415
-	}
416
-
417
-
418
-	/**
419
-	 * Additional checks for the salutation
420
-	 *
421
-	 * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
422
-	 * @param array &$mandatory List of mandatory field names
423
-	 * @return array Additional mandatory address keys
424
-	 * @since 2016.05
425
-	 */
426
-	protected function checkSalutation( array &$params, array &$mandatory )
427
-	{
428
-		if( isset( $params['order.base.address.salutation'] )
429
-				&& $params['order.base.address.salutation'] === \Aimeos\MShop\Common\Item\Address\Base::SALUTATION_COMPANY
430
-				&& in_array( 'order.base.address.company', $mandatory ) === false
431
-		) {
432
-			$mandatory[] = 'order.base.address.company';
433
-		} else {
434
-			$params['order.base.address.company'] = $params['order.base.address.vatid'] = '';
435
-		}
436
-	}
437
-
438
-
439
-	/**
440
-	 * Returns the list of sub-client names configured for the client.
441
-	 *
442
-	 * @return array List of HTML client names
443
-	 */
444
-	protected function getSubClientNames()
445
-	{
446
-		return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
447
-	}
448
-
449
-
450
-	/**
451
-	 * Sets the new address
452
-	 *
453
-	 * @param \Aimeos\MW\View\Iface $view View object
454
-	 * @throws \Aimeos\Client\Html\Exception If an error occurs
455
-	 * @since 2016.05
456
-	 */
457
-	protected function setAddress( \Aimeos\MW\View\Iface $view )
458
-	{
459
-		$context = $this->getContext();
460
-		$basketCtrl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
461
-
462
-		/** client/html/checkout/standard/address/delivery/disable-new
463
-		 * Disables the option to enter a different delivery address for an order
464
-		 *
465
-		 * Besides the billing address, customers can usually enter a different
466
-		 * delivery address as well. To suppress displaying the form fields for
467
-		 * a delivery address, you can set this configuration option to "1".
468
-		 *
469
-		 * Until 2015-02, the configuration option was available as
470
-		 * "client/html/common/address/delivery/disable-new" starting from 2014-03.
471
-		 *
472
-		 * @param boolean A value of "1" to disable, "0" enables the delivery address form
473
-		 * @since 2015.02
474
-		 * @category User
475
-		 * @category Developer
476
-		 * @see client/html/checkout/standard/address/delivery/salutations
477
-		 * @see client/html/checkout/standard/address/delivery/mandatory
478
-		 * @see client/html/checkout/standard/address/delivery/optional
479
-		 * @see client/html/checkout/standard/address/delivery/hidden
480
-		*/
481
-		$disable = $view->config( 'client/html/checkout/standard/address/delivery/disable-new', false );
482
-		$type = \Aimeos\MShop\Order\Item\Base\Address\Base::TYPE_DELIVERY;
483
-
484
-		if( ( $option = $view->param( 'ca_deliveryoption', 'null' ) ) === 'null' && $disable === false ) // new address
485
-		{
486
-			$params = $view->param( 'ca_delivery', array() );
487
-			$invalid = $this->checkFields( $params );
488
-
489
-			if( count( $invalid ) > 0 )
490
-			{
491
-				$view->deliveryError = $invalid;
492
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'At least one delivery address part is missing or invalid' ) );
493
-			}
494
-
495
-			$basketCtrl->setAddress( $type, $params );
496
-		}
497
-		else if( ( $option = $view->param( 'ca_deliveryoption', 'null' ) ) !== '-1' ) // existing address
498
-		{
499
-			$customerAddressManager = \Aimeos\MShop\Factory::createManager( $context, 'customer/address' );
500
-			$address = $customerAddressManager->getItem( $option );
501
-
502
-			if( $address->getParentId() != $context->getUserId() ) {
503
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'Address with ID "%1$s" not found', $option ) );
504
-			}
505
-
506
-			$invalid = array();
507
-			$params = $view->param( 'ca_delivery_' . $option, array() );
508
-
509
-			if( !empty( $params ) )
510
-			{
511
-				$list = array();
512
-				$invalid = $this->checkFields( $params );
513
-
514
-				foreach( $params as $key => $value ) {
515
-					$list[str_replace( 'order.base', 'customer', $key )] = $value;
516
-				}
517
-
518
-				$address->fromArray( $list );
519
-				$customerAddressManager->saveItem( $address );
520
-			}
521
-
522
-			if( count( $invalid ) > 0 )
523
-			{
524
-				$view->deliveryError = $invalid;
525
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'At least one delivery address part is missing or invalid' ) );
526
-			}
527
-
528
-			$basketCtrl->setAddress( $type, $address );
529
-		}
530
-		else
531
-		{
532
-			$basketCtrl->setAddress( $type, null );
533
-		}
534
-	}
535
-
536
-
537
-	/**
538
-	 * Sets the necessary parameter values in the view.
539
-	 *
540
-	 * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
541
-	 * @param array &$tags Result array for the list of tags that are associated to the output
542
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
543
-	 * @return \Aimeos\MW\View\Iface Modified view object
544
-	 */
545
-	protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
546
-	{
547
-		if( !isset( $this->cache ) )
548
-		{
549
-			$context = $this->getContext();
550
-			$basketCntl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
551
-
552
-			try {
553
-				$langid = $basketCntl->get()->getAddress( 'delivery' )->getLanguageId();
554
-			} catch( \Exception $e ) {
555
-				$langid = $view->param( 'ca_delivery/order.base.address.languageid', $context->getLocale()->getLanguageId() );
556
-			}
557
-			$view->deliveryLanguage = $langid;
558
-
559
-			/** client/html/checkout/standard/address/delivery/hidden
560
-			 * List of delivery address input fields that are optional
561
-			 *
562
-			 * You can configure the list of delivery address fields that
563
-			 * are hidden when a customer enters his delivery address.
564
-			 * Available field keys are:
565
-			 * * order.base.address.company
566
-			 * * order.base.address.vatid
567
-			 * * order.base.address.salutation
568
-			 * * order.base.address.firstname
569
-			 * * order.base.address.lastname
570
-			 * * order.base.address.address1
571
-			 * * order.base.address.address2
572
-			 * * order.base.address.address3
573
-			 * * order.base.address.postal
574
-			 * * order.base.address.city
575
-			 * * order.base.address.state
576
-			 * * order.base.address.languageid
577
-			 * * order.base.address.countryid
578
-			 * * order.base.address.telephone
579
-			 * * order.base.address.telefax
580
-			 * * order.base.address.email
581
-			 * * order.base.address.website
582
-			 *
583
-			 * Caution: Only hide fields that don't require any input
584
-			 *
585
-			 * Until 2015-02, the configuration option was available as
586
-			 * "client/html/common/address/delivery/hidden" starting from 2014-03.
587
-			 *
588
-			 * @param array List of field keys
589
-			 * @since 2015.02
590
-			 * @category User
591
-			 * @category Developer
592
-			 * @see client/html/checkout/standard/address/delivery/disable-new
593
-			 * @see client/html/checkout/standard/address/delivery/salutations
594
-			 * @see client/html/checkout/standard/address/delivery/mandatory
595
-			 * @see client/html/checkout/standard/address/delivery/optional
596
-			 * @see client/html/checkout/standard/address/countries
597
-			 */
598
-			$hidden = $view->config( 'client/html/checkout/standard/address/delivery/hidden', array() );
599
-
600
-			if( count( $view->get( 'addressLanguages', array() ) ) === 1 ) {
601
-				$hidden[] = 'order.base.address.languageid';
602
-			}
603
-
604
-			$salutations = array( 'company', 'mr', 'mrs' );
605
-
606
-			/** client/html/checkout/standard/address/delivery/salutations
607
-			 * List of salutions the customer can select from for the delivery address
608
-			 *
609
-			 * The following salutations are available:
610
-			 * * empty string for "unknown"
611
-			 * * company
612
-			 * * mr
613
-			 * * mrs
614
-			 * * miss
615
-			 *
616
-			 * You can modify the list of salutation codes and remove the ones
617
-			 * which shouldn't be used. Adding new salutations is a little bit
618
-			 * more difficult because you have to adapt a few areas in the source
619
-			 * code.
620
-			 *
621
-			 * Until 2015-02, the configuration option was available as
622
-			 * "client/html/common/address/delivery/salutations" starting from 2014-03.
623
-			 *
624
-			 * @param array List of available salutation codes
625
-			 * @since 2015.02
626
-			 * @category User
627
-			 * @category Developer
628
-			 * @see client/html/checkout/standard/address/delivery/disable-new
629
-			 * @see client/html/checkout/standard/address/delivery/mandatory
630
-			 * @see client/html/checkout/standard/address/delivery/optional
631
-			 * @see client/html/checkout/standard/address/delivery/hidden
632
-			 * @see client/html/checkout/standard/address/countries
633
-			 */
634
-			$view->deliverySalutations = $view->config( 'client/html/checkout/standard/address/delivery/salutations', $salutations );
635
-
636
-			$view->deliveryMandatory = $view->config( 'client/html/checkout/standard/address/delivery/mandatory', $this->mandatory );
637
-			$view->deliveryOptional = $view->config( 'client/html/checkout/standard/address/delivery/optional', $this->optional );
638
-			$view->deliveryHidden = $hidden;
639
-
640
-
641
-			$this->cache = $view;
642
-		}
643
-
644
-		return $this->cache;
645
-	}
646
-
647
-
648
-	/**
649
-	 * Validate the address key/value pairs using regular expressions
650
-	 *
651
-	 * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
652
-	 * @param array $fields List of field names to validate
653
-	 * @return array List of invalid address keys
654
-	 * @since 2016.05
655
-	 */
656
-	protected function validateFields( array &$params, array $fields )
657
-	{
658
-		$config = $this->getContext()->getConfig();
659
-
660
-		/** client/html/checkout/standard/address/validate/company
661
-		 * Regular expression to check the "company" address value
662
-		 *
663
-		 * @see client/html/checkout/standard/address/validate
664
-		 */
665
-
666
-		/** client/html/checkout/standard/address/validate/vatid
667
-		 * Regular expression to check the "vatid" address value
668
-		 *
669
-		 * @see client/html/checkout/standard/address/validate
670
-		 */
671
-
672
-		/** client/html/checkout/standard/address/validate/salutation
673
-		 * Regular expression to check the "salutation" address value
674
-		 *
675
-		 * @see client/html/checkout/standard/address/validate
676
-		 */
677
-
678
-		/** client/html/checkout/standard/address/validate/firstname
679
-		 * Regular expression to check the "firstname" address value
680
-		 *
681
-		 * @see client/html/checkout/standard/address/validate
682
-		 */
683
-
684
-		/** client/html/checkout/standard/address/validate/lastname
685
-		 * Regular expression to check the "lastname" address value
686
-		 *
687
-		 * @see client/html/checkout/standard/address/validate
688
-		 */
689
-
690
-		/** client/html/checkout/standard/address/validate/address1
691
-		 * Regular expression to check the "address1" address value
692
-		 *
693
-		 * @see client/html/checkout/standard/address/validate
694
-		 */
695
-
696
-		/** client/html/checkout/standard/address/validate/address2
697
-		 * Regular expression to check the "address2" address value
698
-		 *
699
-		 * @see client/html/checkout/standard/address/validate
700
-		 */
701
-
702
-		/** client/html/checkout/standard/address/validate/address3
703
-		 * Regular expression to check the "address3" address value
704
-		 *
705
-		 * @see client/html/checkout/standard/address/validate
706
-		 */
707
-
708
-		/** client/html/checkout/standard/address/validate/postal
709
-		 * Regular expression to check the "postal" address value
710
-		 *
711
-		 * @see client/html/checkout/standard/address/validate
712
-		 */
713
-
714
-		/** client/html/checkout/standard/address/validate/city
715
-		 * Regular expression to check the "city" address value
716
-		 *
717
-		 * @see client/html/checkout/standard/address/validate
718
-		 */
719
-
720
-		/** client/html/checkout/standard/address/validate/state
721
-		 * Regular expression to check the "state" address value
722
-		 *
723
-		 * @see client/html/checkout/standard/address/validate
724
-		 */
725
-
726
-		/** client/html/checkout/standard/address/validate/languageid
727
-		 * Regular expression to check the "languageid" address value
728
-		 *
729
-		 * @see client/html/checkout/standard/address/validate
730
-		 */
731
-
732
-		/** client/html/checkout/standard/address/validate/countryid
733
-		 * Regular expression to check the "countryid" address value
734
-		 *
735
-		 * @see client/html/checkout/standard/address/validate
736
-		 */
737
-
738
-		/** client/html/checkout/standard/address/validate/telephone
739
-		 * Regular expression to check the "telephone" address value
740
-		 *
741
-		 * @see client/html/checkout/standard/address/validate
742
-		 */
743
-
744
-		/** client/html/checkout/standard/address/validate/telefax
745
-		 * Regular expression to check the "telefax" address value
746
-		 *
747
-		 * @see client/html/checkout/standard/address/validate
748
-		 */
749
-
750
-		/** client/html/checkout/standard/address/validate/email
751
-		 * Regular expression to check the "email" address value
752
-		 *
753
-		 * @see client/html/checkout/standard/address/validate
754
-		 */
755
-
756
-		/** client/html/checkout/standard/address/validate/website
757
-		 * Regular expression to check the "website" address value
758
-		 *
759
-		 * @see client/html/checkout/standard/address/validate
760
-		 */
761
-
762
-		$invalid = array();
763
-
764
-		foreach( $params as $key => $value )
765
-		{
766
-			if( isset( $fields[$key] ) )
767
-			{
768
-				$name = substr( $key, 19 );
769
-				$regex = $config->get( 'client/html/checkout/standard/address/validate/' . $name );
770
-
771
-				if( $regex && preg_match( '/' . $regex . '/', $value ) !== 1 )
772
-				{
773
-					$invalid[$key] = $name;
774
-					unset( $params[$key] );
775
-				}
776
-			}
777
-			else
778
-			{
779
-				unset( $params[$key] );
780
-			}
781
-		}
782
-
783
-		return $invalid;
784
-	}
25
+    /** client/html/checkout/standard/address/delivery/standard/subparts
26
+     * List of HTML sub-clients rendered within the checkout standard address delivery 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/delivery/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
+    );
71
+
72
+    private $optional = array(
73
+        'order.base.address.company',
74
+        'order.base.address.vatid',
75
+        'order.base.address.address2',
76
+        'order.base.address.countryid',
77
+        'order.base.address.state',
78
+    );
79
+
80
+
81
+    /**
82
+     * Returns the HTML code for insertion into the body.
83
+     *
84
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
85
+     * @param array &$tags Result array for the list of tags that are associated to the output
86
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
87
+     * @return string HTML code
88
+     */
89
+    public function getBody( $uid = '', array &$tags = array(), &$expire = null )
90
+    {
91
+        $view = $this->setViewParams( $this->getView(), $tags, $expire );
92
+
93
+        $html = '';
94
+        foreach( $this->getSubClients() as $subclient ) {
95
+            $html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
96
+        }
97
+        $view->deliveryBody = $html;
98
+
99
+        /** client/html/checkout/standard/address/delivery/standard/template-body
100
+         * Relative path to the HTML body template of the checkout standard address delivery client.
101
+         *
102
+         * The template file contains the HTML code and processing instructions
103
+         * to generate the result shown in the body of the frontend. The
104
+         * configuration string is the path to the template file relative
105
+         * to the templates directory (usually in client/html/templates).
106
+         *
107
+         * You can overwrite the template file configuration in extensions and
108
+         * provide alternative templates. These alternative templates should be
109
+         * named like the default one but with the string "standard" replaced by
110
+         * an unique name. You may use the name of your project for this. If
111
+         * you've implemented an alternative client class as well, "standard"
112
+         * should be replaced by the name of the new class.
113
+         *
114
+         * @param string Relative path to the template creating code for the HTML page body
115
+         * @since 2014.03
116
+         * @category Developer
117
+         * @see client/html/checkout/standard/address/delivery/standard/template-header
118
+         */
119
+        $tplconf = 'client/html/checkout/standard/address/delivery/standard/template-body';
120
+        $default = 'checkout/standard/address-delivery-body-default.php';
121
+
122
+        return $view->render( $view->config( $tplconf, $default ) );
123
+    }
124
+
125
+
126
+    /**
127
+     * Returns the HTML string for insertion into the header.
128
+     *
129
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
130
+     * @param array &$tags Result array for the list of tags that are associated to the output
131
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
132
+     * @return string|null String including HTML tags for the header on error
133
+     */
134
+    public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
135
+    {
136
+        $view = $this->setViewParams( $this->getView(), $tags, $expire );
137
+
138
+        $html = '';
139
+        foreach( $this->getSubClients() as $subclient ) {
140
+            $html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
141
+        }
142
+        $view->deliveryHeader = $html;
143
+
144
+        /** client/html/checkout/standard/address/delivery/standard/template-header
145
+         * Relative path to the HTML header template of the checkout standard address delivery client.
146
+         *
147
+         * The template file contains the HTML code and processing instructions
148
+         * to generate the HTML code that is inserted into the HTML page header
149
+         * of the rendered page in the frontend. The configuration string is the
150
+         * path to the template file relative to the templates directory (usually
151
+         * in client/html/templates).
152
+         *
153
+         * You can overwrite the template file configuration in extensions and
154
+         * provide alternative templates. These alternative templates should be
155
+         * named like the default one but with the string "standard" replaced by
156
+         * an unique name. You may use the name of your project for this. If
157
+         * you've implemented an alternative client class as well, "standard"
158
+         * should be replaced by the name of the new class.
159
+         *
160
+         * @param string Relative path to the template creating code for the HTML page head
161
+         * @since 2014.03
162
+         * @category Developer
163
+         * @see client/html/checkout/standard/address/delivery/standard/template-body
164
+         */
165
+        $tplconf = 'client/html/checkout/standard/address/delivery/standard/template-header';
166
+        $default = 'checkout/standard/address-delivery-header-default.php';
167
+
168
+        return $view->render( $view->config( $tplconf, $default ) );
169
+    }
170
+
171
+
172
+    /**
173
+     * Returns the sub-client given by its name.
174
+     *
175
+     * @param string $type Name of the client type
176
+     * @param string|null $name Name of the sub-client (Default if null)
177
+     * @return \Aimeos\Client\Html\Iface Sub-client object
178
+     */
179
+    public function getSubClient( $type, $name = null )
180
+    {
181
+        /** client/html/checkout/standard/address/delivery/decorators/excludes
182
+         * Excludes decorators added by the "common" option from the checkout standard address delivery html client
183
+         *
184
+         * Decorators extend the functionality of a class by adding new aspects
185
+         * (e.g. log what is currently done), executing the methods of the underlying
186
+         * class only in certain conditions (e.g. only for logged in users) or
187
+         * modify what is returned to the caller.
188
+         *
189
+         * This option allows you to remove a decorator added via
190
+         * "client/html/common/decorators/default" before they are wrapped
191
+         * around the html client.
192
+         *
193
+         *  client/html/checkout/standard/address/delivery/decorators/excludes = array( 'decorator1' )
194
+         *
195
+         * This would remove the decorator named "decorator1" from the list of
196
+         * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
197
+         * "client/html/common/decorators/default" to the html client.
198
+         *
199
+         * @param array List of decorator names
200
+         * @since 2015.08
201
+         * @category Developer
202
+         * @see client/html/common/decorators/default
203
+         * @see client/html/checkout/standard/address/delivery/decorators/global
204
+         * @see client/html/checkout/standard/address/delivery/decorators/local
205
+         */
206
+
207
+        /** client/html/checkout/standard/address/delivery/decorators/global
208
+         * Adds a list of globally available decorators only to the checkout standard address delivery html client
209
+         *
210
+         * Decorators extend the functionality of a class by adding new aspects
211
+         * (e.g. log what is currently done), executing the methods of the underlying
212
+         * class only in certain conditions (e.g. only for logged in users) or
213
+         * modify what is returned to the caller.
214
+         *
215
+         * This option allows you to wrap global decorators
216
+         * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
217
+         *
218
+         *  client/html/checkout/standard/address/delivery/decorators/global = array( 'decorator1' )
219
+         *
220
+         * This would add the decorator named "decorator1" defined by
221
+         * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
222
+         *
223
+         * @param array List of decorator names
224
+         * @since 2015.08
225
+         * @category Developer
226
+         * @see client/html/common/decorators/default
227
+         * @see client/html/checkout/standard/address/delivery/decorators/excludes
228
+         * @see client/html/checkout/standard/address/delivery/decorators/local
229
+         */
230
+
231
+        /** client/html/checkout/standard/address/delivery/decorators/local
232
+         * Adds a list of local decorators only to the checkout standard address delivery html client
233
+         *
234
+         * Decorators extend the functionality of a class by adding new aspects
235
+         * (e.g. log what is currently done), executing the methods of the underlying
236
+         * class only in certain conditions (e.g. only for logged in users) or
237
+         * modify what is returned to the caller.
238
+         *
239
+         * This option allows you to wrap local decorators
240
+         * ("\Aimeos\Client\Html\Checkout\Decorator\*") around the html client.
241
+         *
242
+         *  client/html/checkout/standard/address/delivery/decorators/local = array( 'decorator2' )
243
+         *
244
+         * This would add the decorator named "decorator2" defined by
245
+         * "\Aimeos\Client\Html\Checkout\Decorator\Decorator2" only to the html client.
246
+         *
247
+         * @param array List of decorator names
248
+         * @since 2015.08
249
+         * @category Developer
250
+         * @see client/html/common/decorators/default
251
+         * @see client/html/checkout/standard/address/delivery/decorators/excludes
252
+         * @see client/html/checkout/standard/address/delivery/decorators/global
253
+         */
254
+
255
+        return $this->createSubClient( 'checkout/standard/address/delivery/' . $type, $name );
256
+    }
257
+
258
+
259
+    /**
260
+     * Stores the given or fetched billing address in the basket.
261
+     */
262
+    public function process()
263
+    {
264
+        $context = $this->getContext();
265
+        $view = $this->getView();
266
+
267
+        try
268
+        {
269
+            if( ( $id = $view->param( 'ca_delivery_delete', null ) ) !== null )
270
+            {
271
+                $customerAddressManager = \Aimeos\MShop\Factory::createManager( $context, 'customer/address' );
272
+                $address = $customerAddressManager->getItem( $id );
273
+
274
+                if( $address->getParentId() != $context->getUserId() ) {
275
+                    throw new \Aimeos\Client\Html\Exception( sprintf( 'Address with ID "%1$s" not found', $id ) );
276
+                }
277
+
278
+                $customerAddressManager->deleteItem( $id );
279
+            }
280
+
281
+            // only start if there's something to do
282
+            if( $view->param( 'ca_deliveryoption', null ) === null ) {
283
+                return;
284
+            }
285
+
286
+            $this->setAddress( $view );
287
+
288
+            parent::process();
289
+        }
290
+        catch( \Aimeos\Controller\Frontend\Exception $e )
291
+        {
292
+            $view->deliveryError = $e->getErrorList();
293
+            throw $e;
294
+        }
295
+    }
296
+
297
+
298
+    /**
299
+     * Checks the address fields for missing data and sanitizes the given parameter list.
300
+     *
301
+     * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
302
+     * @return array List of missing field names
303
+     */
304
+    protected function checkFields( array &$params )
305
+    {
306
+        $view = $this->getView();
307
+
308
+        /** client/html/checkout/standard/address/delivery/mandatory
309
+         * List of delivery address input fields that are required
310
+         *
311
+         * You can configure the list of delivery address fields that are
312
+         * necessary and must be filled by the customer before he can
313
+         * continue the checkout process. Available field keys are:
314
+         * * order.base.address.company
315
+         * * order.base.address.vatid
316
+         * * order.base.address.salutation
317
+         * * order.base.address.firstname
318
+         * * order.base.address.lastname
319
+         * * order.base.address.address1
320
+         * * order.base.address.address2
321
+         * * order.base.address.address3
322
+         * * order.base.address.postal
323
+         * * order.base.address.city
324
+         * * order.base.address.state
325
+         * * order.base.address.languageid
326
+         * * order.base.address.countryid
327
+         * * order.base.address.telephone
328
+         * * order.base.address.telefax
329
+         * * order.base.address.email
330
+         * * order.base.address.website
331
+         *
332
+         * Until 2015-02, the configuration option was available as
333
+         * "client/html/common/address/delivery/mandatory" starting from 2014-03.
334
+         *
335
+         * @param array List of field keys
336
+         * @since 2015.02
337
+         * @category User
338
+         * @category Developer
339
+         * @see client/html/checkout/standard/address/delivery/disable-new
340
+         * @see client/html/checkout/standard/address/delivery/salutations
341
+         * @see client/html/checkout/standard/address/delivery/optional
342
+         * @see client/html/checkout/standard/address/delivery/hidden
343
+         * @see client/html/checkout/standard/address/countries
344
+         * @see client/html/checkout/standard/address/validate
345
+         */
346
+        $mandatory = $view->config( 'client/html/checkout/standard/address/delivery/mandatory', $this->mandatory );
347
+
348
+        /** client/html/checkout/standard/address/delivery/optional
349
+         * List of delivery address input fields that are optional
350
+         *
351
+         * You can configure the list of delivery address fields that
352
+         * customers can fill but don't have to before they can
353
+         * continue the checkout process. Available field keys are:
354
+         * * order.base.address.company
355
+         * * order.base.address.vatid
356
+         * * order.base.address.salutation
357
+         * * order.base.address.firstname
358
+         * * order.base.address.lastname
359
+         * * order.base.address.address1
360
+         * * order.base.address.address2
361
+         * * order.base.address.address3
362
+         * * order.base.address.postal
363
+         * * order.base.address.city
364
+         * * order.base.address.state
365
+         * * order.base.address.languageid
366
+         * * order.base.address.countryid
367
+         * * order.base.address.telephone
368
+         * * order.base.address.telefax
369
+         * * order.base.address.email
370
+         * * order.base.address.website
371
+         *
372
+         * Until 2015-02, the configuration option was available as
373
+         * "client/html/common/address/delivery/optional" starting from 2014-03.
374
+         *
375
+         * @param array List of field keys
376
+         * @since 2015.02
377
+         * @category User
378
+         * @category Developer
379
+         * @see client/html/checkout/standard/address/delivery/disable-new
380
+         * @see client/html/checkout/standard/address/delivery/salutations
381
+         * @see client/html/checkout/standard/address/delivery/mandatory
382
+         * @see client/html/checkout/standard/address/delivery/hidden
383
+         * @see client/html/checkout/standard/address/countries
384
+         * @see client/html/checkout/standard/address/validate
385
+         */
386
+        $optional = $view->config( 'client/html/checkout/standard/address/delivery/optional', $this->optional );
387
+
388
+        /** client/html/checkout/standard/address/validate
389
+         *
390
+         * @see client/html/checkout/standard/address/delivery/mandatory
391
+         * @see client/html/checkout/standard/address/delivery/optional
392
+         */
393
+
394
+        $allFields = array_flip( array_merge( $mandatory, $optional ) );
395
+        $invalid = $this->validateFields( $params, $allFields );
396
+        $this->checkSalutation( $params, $mandatory );
397
+
398
+        foreach( $invalid as $key => $name )
399
+        {
400
+            $msg = $view->translate( 'client', 'Delivery address part "%1$s" is invalid' );
401
+            $invalid[$key] = sprintf( $msg, $name );
402
+        }
403
+
404
+        foreach( $mandatory as $key )
405
+        {
406
+            if( !isset( $params[$key] ) || $params[$key] == '' )
407
+            {
408
+                $msg = $view->translate( 'client', 'Delivery address part "%1$s" is missing' );
409
+                $invalid[$key] = sprintf( $msg, substr( $key, 19 ) );
410
+                unset( $params[$key] );
411
+            }
412
+        }
413
+
414
+        return $invalid;
415
+    }
416
+
417
+
418
+    /**
419
+     * Additional checks for the salutation
420
+     *
421
+     * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
422
+     * @param array &$mandatory List of mandatory field names
423
+     * @return array Additional mandatory address keys
424
+     * @since 2016.05
425
+     */
426
+    protected function checkSalutation( array &$params, array &$mandatory )
427
+    {
428
+        if( isset( $params['order.base.address.salutation'] )
429
+                && $params['order.base.address.salutation'] === \Aimeos\MShop\Common\Item\Address\Base::SALUTATION_COMPANY
430
+                && in_array( 'order.base.address.company', $mandatory ) === false
431
+        ) {
432
+            $mandatory[] = 'order.base.address.company';
433
+        } else {
434
+            $params['order.base.address.company'] = $params['order.base.address.vatid'] = '';
435
+        }
436
+    }
437
+
438
+
439
+    /**
440
+     * Returns the list of sub-client names configured for the client.
441
+     *
442
+     * @return array List of HTML client names
443
+     */
444
+    protected function getSubClientNames()
445
+    {
446
+        return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
447
+    }
448
+
449
+
450
+    /**
451
+     * Sets the new address
452
+     *
453
+     * @param \Aimeos\MW\View\Iface $view View object
454
+     * @throws \Aimeos\Client\Html\Exception If an error occurs
455
+     * @since 2016.05
456
+     */
457
+    protected function setAddress( \Aimeos\MW\View\Iface $view )
458
+    {
459
+        $context = $this->getContext();
460
+        $basketCtrl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
461
+
462
+        /** client/html/checkout/standard/address/delivery/disable-new
463
+         * Disables the option to enter a different delivery address for an order
464
+         *
465
+         * Besides the billing address, customers can usually enter a different
466
+         * delivery address as well. To suppress displaying the form fields for
467
+         * a delivery address, you can set this configuration option to "1".
468
+         *
469
+         * Until 2015-02, the configuration option was available as
470
+         * "client/html/common/address/delivery/disable-new" starting from 2014-03.
471
+         *
472
+         * @param boolean A value of "1" to disable, "0" enables the delivery address form
473
+         * @since 2015.02
474
+         * @category User
475
+         * @category Developer
476
+         * @see client/html/checkout/standard/address/delivery/salutations
477
+         * @see client/html/checkout/standard/address/delivery/mandatory
478
+         * @see client/html/checkout/standard/address/delivery/optional
479
+         * @see client/html/checkout/standard/address/delivery/hidden
480
+         */
481
+        $disable = $view->config( 'client/html/checkout/standard/address/delivery/disable-new', false );
482
+        $type = \Aimeos\MShop\Order\Item\Base\Address\Base::TYPE_DELIVERY;
483
+
484
+        if( ( $option = $view->param( 'ca_deliveryoption', 'null' ) ) === 'null' && $disable === false ) // new address
485
+        {
486
+            $params = $view->param( 'ca_delivery', array() );
487
+            $invalid = $this->checkFields( $params );
488
+
489
+            if( count( $invalid ) > 0 )
490
+            {
491
+                $view->deliveryError = $invalid;
492
+                throw new \Aimeos\Client\Html\Exception( sprintf( 'At least one delivery address part is missing or invalid' ) );
493
+            }
494
+
495
+            $basketCtrl->setAddress( $type, $params );
496
+        }
497
+        else if( ( $option = $view->param( 'ca_deliveryoption', 'null' ) ) !== '-1' ) // existing address
498
+        {
499
+            $customerAddressManager = \Aimeos\MShop\Factory::createManager( $context, 'customer/address' );
500
+            $address = $customerAddressManager->getItem( $option );
501
+
502
+            if( $address->getParentId() != $context->getUserId() ) {
503
+                throw new \Aimeos\Client\Html\Exception( sprintf( 'Address with ID "%1$s" not found', $option ) );
504
+            }
505
+
506
+            $invalid = array();
507
+            $params = $view->param( 'ca_delivery_' . $option, array() );
508
+
509
+            if( !empty( $params ) )
510
+            {
511
+                $list = array();
512
+                $invalid = $this->checkFields( $params );
513
+
514
+                foreach( $params as $key => $value ) {
515
+                    $list[str_replace( 'order.base', 'customer', $key )] = $value;
516
+                }
517
+
518
+                $address->fromArray( $list );
519
+                $customerAddressManager->saveItem( $address );
520
+            }
521
+
522
+            if( count( $invalid ) > 0 )
523
+            {
524
+                $view->deliveryError = $invalid;
525
+                throw new \Aimeos\Client\Html\Exception( sprintf( 'At least one delivery address part is missing or invalid' ) );
526
+            }
527
+
528
+            $basketCtrl->setAddress( $type, $address );
529
+        }
530
+        else
531
+        {
532
+            $basketCtrl->setAddress( $type, null );
533
+        }
534
+    }
535
+
536
+
537
+    /**
538
+     * Sets the necessary parameter values in the view.
539
+     *
540
+     * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
541
+     * @param array &$tags Result array for the list of tags that are associated to the output
542
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
543
+     * @return \Aimeos\MW\View\Iface Modified view object
544
+     */
545
+    protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
546
+    {
547
+        if( !isset( $this->cache ) )
548
+        {
549
+            $context = $this->getContext();
550
+            $basketCntl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
551
+
552
+            try {
553
+                $langid = $basketCntl->get()->getAddress( 'delivery' )->getLanguageId();
554
+            } catch( \Exception $e ) {
555
+                $langid = $view->param( 'ca_delivery/order.base.address.languageid', $context->getLocale()->getLanguageId() );
556
+            }
557
+            $view->deliveryLanguage = $langid;
558
+
559
+            /** client/html/checkout/standard/address/delivery/hidden
560
+             * List of delivery address input fields that are optional
561
+             *
562
+             * You can configure the list of delivery address fields that
563
+             * are hidden when a customer enters his delivery address.
564
+             * Available field keys are:
565
+             * * order.base.address.company
566
+             * * order.base.address.vatid
567
+             * * order.base.address.salutation
568
+             * * order.base.address.firstname
569
+             * * order.base.address.lastname
570
+             * * order.base.address.address1
571
+             * * order.base.address.address2
572
+             * * order.base.address.address3
573
+             * * order.base.address.postal
574
+             * * order.base.address.city
575
+             * * order.base.address.state
576
+             * * order.base.address.languageid
577
+             * * order.base.address.countryid
578
+             * * order.base.address.telephone
579
+             * * order.base.address.telefax
580
+             * * order.base.address.email
581
+             * * order.base.address.website
582
+             *
583
+             * Caution: Only hide fields that don't require any input
584
+             *
585
+             * Until 2015-02, the configuration option was available as
586
+             * "client/html/common/address/delivery/hidden" starting from 2014-03.
587
+             *
588
+             * @param array List of field keys
589
+             * @since 2015.02
590
+             * @category User
591
+             * @category Developer
592
+             * @see client/html/checkout/standard/address/delivery/disable-new
593
+             * @see client/html/checkout/standard/address/delivery/salutations
594
+             * @see client/html/checkout/standard/address/delivery/mandatory
595
+             * @see client/html/checkout/standard/address/delivery/optional
596
+             * @see client/html/checkout/standard/address/countries
597
+             */
598
+            $hidden = $view->config( 'client/html/checkout/standard/address/delivery/hidden', array() );
599
+
600
+            if( count( $view->get( 'addressLanguages', array() ) ) === 1 ) {
601
+                $hidden[] = 'order.base.address.languageid';
602
+            }
603
+
604
+            $salutations = array( 'company', 'mr', 'mrs' );
605
+
606
+            /** client/html/checkout/standard/address/delivery/salutations
607
+             * List of salutions the customer can select from for the delivery address
608
+             *
609
+             * The following salutations are available:
610
+             * * empty string for "unknown"
611
+             * * company
612
+             * * mr
613
+             * * mrs
614
+             * * miss
615
+             *
616
+             * You can modify the list of salutation codes and remove the ones
617
+             * which shouldn't be used. Adding new salutations is a little bit
618
+             * more difficult because you have to adapt a few areas in the source
619
+             * code.
620
+             *
621
+             * Until 2015-02, the configuration option was available as
622
+             * "client/html/common/address/delivery/salutations" starting from 2014-03.
623
+             *
624
+             * @param array List of available salutation codes
625
+             * @since 2015.02
626
+             * @category User
627
+             * @category Developer
628
+             * @see client/html/checkout/standard/address/delivery/disable-new
629
+             * @see client/html/checkout/standard/address/delivery/mandatory
630
+             * @see client/html/checkout/standard/address/delivery/optional
631
+             * @see client/html/checkout/standard/address/delivery/hidden
632
+             * @see client/html/checkout/standard/address/countries
633
+             */
634
+            $view->deliverySalutations = $view->config( 'client/html/checkout/standard/address/delivery/salutations', $salutations );
635
+
636
+            $view->deliveryMandatory = $view->config( 'client/html/checkout/standard/address/delivery/mandatory', $this->mandatory );
637
+            $view->deliveryOptional = $view->config( 'client/html/checkout/standard/address/delivery/optional', $this->optional );
638
+            $view->deliveryHidden = $hidden;
639
+
640
+
641
+            $this->cache = $view;
642
+        }
643
+
644
+        return $this->cache;
645
+    }
646
+
647
+
648
+    /**
649
+     * Validate the address key/value pairs using regular expressions
650
+     *
651
+     * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
652
+     * @param array $fields List of field names to validate
653
+     * @return array List of invalid address keys
654
+     * @since 2016.05
655
+     */
656
+    protected function validateFields( array &$params, array $fields )
657
+    {
658
+        $config = $this->getContext()->getConfig();
659
+
660
+        /** client/html/checkout/standard/address/validate/company
661
+         * Regular expression to check the "company" address value
662
+         *
663
+         * @see client/html/checkout/standard/address/validate
664
+         */
665
+
666
+        /** client/html/checkout/standard/address/validate/vatid
667
+         * Regular expression to check the "vatid" address value
668
+         *
669
+         * @see client/html/checkout/standard/address/validate
670
+         */
671
+
672
+        /** client/html/checkout/standard/address/validate/salutation
673
+         * Regular expression to check the "salutation" address value
674
+         *
675
+         * @see client/html/checkout/standard/address/validate
676
+         */
677
+
678
+        /** client/html/checkout/standard/address/validate/firstname
679
+         * Regular expression to check the "firstname" address value
680
+         *
681
+         * @see client/html/checkout/standard/address/validate
682
+         */
683
+
684
+        /** client/html/checkout/standard/address/validate/lastname
685
+         * Regular expression to check the "lastname" address value
686
+         *
687
+         * @see client/html/checkout/standard/address/validate
688
+         */
689
+
690
+        /** client/html/checkout/standard/address/validate/address1
691
+         * Regular expression to check the "address1" address value
692
+         *
693
+         * @see client/html/checkout/standard/address/validate
694
+         */
695
+
696
+        /** client/html/checkout/standard/address/validate/address2
697
+         * Regular expression to check the "address2" address value
698
+         *
699
+         * @see client/html/checkout/standard/address/validate
700
+         */
701
+
702
+        /** client/html/checkout/standard/address/validate/address3
703
+         * Regular expression to check the "address3" address value
704
+         *
705
+         * @see client/html/checkout/standard/address/validate
706
+         */
707
+
708
+        /** client/html/checkout/standard/address/validate/postal
709
+         * Regular expression to check the "postal" address value
710
+         *
711
+         * @see client/html/checkout/standard/address/validate
712
+         */
713
+
714
+        /** client/html/checkout/standard/address/validate/city
715
+         * Regular expression to check the "city" address value
716
+         *
717
+         * @see client/html/checkout/standard/address/validate
718
+         */
719
+
720
+        /** client/html/checkout/standard/address/validate/state
721
+         * Regular expression to check the "state" address value
722
+         *
723
+         * @see client/html/checkout/standard/address/validate
724
+         */
725
+
726
+        /** client/html/checkout/standard/address/validate/languageid
727
+         * Regular expression to check the "languageid" address value
728
+         *
729
+         * @see client/html/checkout/standard/address/validate
730
+         */
731
+
732
+        /** client/html/checkout/standard/address/validate/countryid
733
+         * Regular expression to check the "countryid" address value
734
+         *
735
+         * @see client/html/checkout/standard/address/validate
736
+         */
737
+
738
+        /** client/html/checkout/standard/address/validate/telephone
739
+         * Regular expression to check the "telephone" address value
740
+         *
741
+         * @see client/html/checkout/standard/address/validate
742
+         */
743
+
744
+        /** client/html/checkout/standard/address/validate/telefax
745
+         * Regular expression to check the "telefax" address value
746
+         *
747
+         * @see client/html/checkout/standard/address/validate
748
+         */
749
+
750
+        /** client/html/checkout/standard/address/validate/email
751
+         * Regular expression to check the "email" address value
752
+         *
753
+         * @see client/html/checkout/standard/address/validate
754
+         */
755
+
756
+        /** client/html/checkout/standard/address/validate/website
757
+         * Regular expression to check the "website" address value
758
+         *
759
+         * @see client/html/checkout/standard/address/validate
760
+         */
761
+
762
+        $invalid = array();
763
+
764
+        foreach( $params as $key => $value )
765
+        {
766
+            if( isset( $fields[$key] ) )
767
+            {
768
+                $name = substr( $key, 19 );
769
+                $regex = $config->get( 'client/html/checkout/standard/address/validate/' . $name );
770
+
771
+                if( $regex && preg_match( '/' . $regex . '/', $value ) !== 1 )
772
+                {
773
+                    $invalid[$key] = $name;
774
+                    unset( $params[$key] );
775
+                }
776
+            }
777
+            else
778
+            {
779
+                unset( $params[$key] );
780
+            }
781
+        }
782
+
783
+        return $invalid;
784
+    }
785 785
 }
786 786
\ No newline at end of file
Please login to merge, or discard this patch.
Spacing   +84 added lines, -84 removed lines patch added patch discarded remove patch
@@ -86,13 +86,13 @@  discard block
 block discarded – undo
86 86
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
87 87
 	 * @return string HTML code
88 88
 	 */
89
-	public function getBody( $uid = '', array &$tags = array(), &$expire = null )
89
+	public function getBody($uid = '', array &$tags = array(), &$expire = null)
90 90
 	{
91
-		$view = $this->setViewParams( $this->getView(), $tags, $expire );
91
+		$view = $this->setViewParams($this->getView(), $tags, $expire);
92 92
 
93 93
 		$html = '';
94
-		foreach( $this->getSubClients() as $subclient ) {
95
-			$html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
94
+		foreach ($this->getSubClients() as $subclient) {
95
+			$html .= $subclient->setView($view)->getBody($uid, $tags, $expire);
96 96
 		}
97 97
 		$view->deliveryBody = $html;
98 98
 
@@ -119,7 +119,7 @@  discard block
 block discarded – undo
119 119
 		$tplconf = 'client/html/checkout/standard/address/delivery/standard/template-body';
120 120
 		$default = 'checkout/standard/address-delivery-body-default.php';
121 121
 
122
-		return $view->render( $view->config( $tplconf, $default ) );
122
+		return $view->render($view->config($tplconf, $default));
123 123
 	}
124 124
 
125 125
 
@@ -131,13 +131,13 @@  discard block
 block discarded – undo
131 131
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
132 132
 	 * @return string|null String including HTML tags for the header on error
133 133
 	 */
134
-	public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
134
+	public function getHeader($uid = '', array &$tags = array(), &$expire = null)
135 135
 	{
136
-		$view = $this->setViewParams( $this->getView(), $tags, $expire );
136
+		$view = $this->setViewParams($this->getView(), $tags, $expire);
137 137
 
138 138
 		$html = '';
139
-		foreach( $this->getSubClients() as $subclient ) {
140
-			$html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
139
+		foreach ($this->getSubClients() as $subclient) {
140
+			$html .= $subclient->setView($view)->getHeader($uid, $tags, $expire);
141 141
 		}
142 142
 		$view->deliveryHeader = $html;
143 143
 
@@ -165,7 +165,7 @@  discard block
 block discarded – undo
165 165
 		$tplconf = 'client/html/checkout/standard/address/delivery/standard/template-header';
166 166
 		$default = 'checkout/standard/address-delivery-header-default.php';
167 167
 
168
-		return $view->render( $view->config( $tplconf, $default ) );
168
+		return $view->render($view->config($tplconf, $default));
169 169
 	}
170 170
 
171 171
 
@@ -176,7 +176,7 @@  discard block
 block discarded – undo
176 176
 	 * @param string|null $name Name of the sub-client (Default if null)
177 177
 	 * @return \Aimeos\Client\Html\Iface Sub-client object
178 178
 	 */
179
-	public function getSubClient( $type, $name = null )
179
+	public function getSubClient($type, $name = null)
180 180
 	{
181 181
 		/** client/html/checkout/standard/address/delivery/decorators/excludes
182 182
 		 * Excludes decorators added by the "common" option from the checkout standard address delivery html client
@@ -252,7 +252,7 @@  discard block
 block discarded – undo
252 252
 		 * @see client/html/checkout/standard/address/delivery/decorators/global
253 253
 		 */
254 254
 
255
-		return $this->createSubClient( 'checkout/standard/address/delivery/' . $type, $name );
255
+		return $this->createSubClient('checkout/standard/address/delivery/'.$type, $name);
256 256
 	}
257 257
 
258 258
 
@@ -266,28 +266,28 @@  discard block
 block discarded – undo
266 266
 
267 267
 		try
268 268
 		{
269
-			if( ( $id = $view->param( 'ca_delivery_delete', null ) ) !== null )
269
+			if (($id = $view->param('ca_delivery_delete', null)) !== null)
270 270
 			{
271
-				$customerAddressManager = \Aimeos\MShop\Factory::createManager( $context, 'customer/address' );
272
-				$address = $customerAddressManager->getItem( $id );
271
+				$customerAddressManager = \Aimeos\MShop\Factory::createManager($context, 'customer/address');
272
+				$address = $customerAddressManager->getItem($id);
273 273
 
274
-				if( $address->getParentId() != $context->getUserId() ) {
275
-					throw new \Aimeos\Client\Html\Exception( sprintf( 'Address with ID "%1$s" not found', $id ) );
274
+				if ($address->getParentId() != $context->getUserId()) {
275
+					throw new \Aimeos\Client\Html\Exception(sprintf('Address with ID "%1$s" not found', $id));
276 276
 				}
277 277
 
278
-				$customerAddressManager->deleteItem( $id );
278
+				$customerAddressManager->deleteItem($id);
279 279
 			}
280 280
 
281 281
 			// only start if there's something to do
282
-			if( $view->param( 'ca_deliveryoption', null ) === null ) {
282
+			if ($view->param('ca_deliveryoption', null) === null) {
283 283
 				return;
284 284
 			}
285 285
 
286
-			$this->setAddress( $view );
286
+			$this->setAddress($view);
287 287
 
288 288
 			parent::process();
289 289
 		}
290
-		catch( \Aimeos\Controller\Frontend\Exception $e )
290
+		catch (\Aimeos\Controller\Frontend\Exception $e)
291 291
 		{
292 292
 			$view->deliveryError = $e->getErrorList();
293 293
 			throw $e;
@@ -301,7 +301,7 @@  discard block
 block discarded – undo
301 301
 	 * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
302 302
 	 * @return array List of missing field names
303 303
 	 */
304
-	protected function checkFields( array &$params )
304
+	protected function checkFields(array &$params)
305 305
 	{
306 306
 		$view = $this->getView();
307 307
 
@@ -343,7 +343,7 @@  discard block
 block discarded – undo
343 343
 		 * @see client/html/checkout/standard/address/countries
344 344
 		 * @see client/html/checkout/standard/address/validate
345 345
 		 */
346
-		$mandatory = $view->config( 'client/html/checkout/standard/address/delivery/mandatory', $this->mandatory );
346
+		$mandatory = $view->config('client/html/checkout/standard/address/delivery/mandatory', $this->mandatory);
347 347
 
348 348
 		/** client/html/checkout/standard/address/delivery/optional
349 349
 		 * List of delivery address input fields that are optional
@@ -383,7 +383,7 @@  discard block
 block discarded – undo
383 383
 		 * @see client/html/checkout/standard/address/countries
384 384
 		 * @see client/html/checkout/standard/address/validate
385 385
 		 */
386
-		$optional = $view->config( 'client/html/checkout/standard/address/delivery/optional', $this->optional );
386
+		$optional = $view->config('client/html/checkout/standard/address/delivery/optional', $this->optional);
387 387
 
388 388
 		/** client/html/checkout/standard/address/validate
389 389
 		 *
@@ -391,23 +391,23 @@  discard block
 block discarded – undo
391 391
 		 * @see client/html/checkout/standard/address/delivery/optional
392 392
 		 */
393 393
 
394
-		$allFields = array_flip( array_merge( $mandatory, $optional ) );
395
-		$invalid = $this->validateFields( $params, $allFields );
396
-		$this->checkSalutation( $params, $mandatory );
394
+		$allFields = array_flip(array_merge($mandatory, $optional));
395
+		$invalid = $this->validateFields($params, $allFields);
396
+		$this->checkSalutation($params, $mandatory);
397 397
 
398
-		foreach( $invalid as $key => $name )
398
+		foreach ($invalid as $key => $name)
399 399
 		{
400
-			$msg = $view->translate( 'client', 'Delivery address part "%1$s" is invalid' );
401
-			$invalid[$key] = sprintf( $msg, $name );
400
+			$msg = $view->translate('client', 'Delivery address part "%1$s" is invalid');
401
+			$invalid[$key] = sprintf($msg, $name);
402 402
 		}
403 403
 
404
-		foreach( $mandatory as $key )
404
+		foreach ($mandatory as $key)
405 405
 		{
406
-			if( !isset( $params[$key] ) || $params[$key] == '' )
406
+			if (!isset($params[$key]) || $params[$key] == '')
407 407
 			{
408
-				$msg = $view->translate( 'client', 'Delivery address part "%1$s" is missing' );
409
-				$invalid[$key] = sprintf( $msg, substr( $key, 19 ) );
410
-				unset( $params[$key] );
408
+				$msg = $view->translate('client', 'Delivery address part "%1$s" is missing');
409
+				$invalid[$key] = sprintf($msg, substr($key, 19));
410
+				unset($params[$key]);
411 411
 			}
412 412
 		}
413 413
 
@@ -423,11 +423,11 @@  discard block
 block discarded – undo
423 423
 	 * @return array Additional mandatory address keys
424 424
 	 * @since 2016.05
425 425
 	 */
426
-	protected function checkSalutation( array &$params, array &$mandatory )
426
+	protected function checkSalutation(array &$params, array &$mandatory)
427 427
 	{
428
-		if( isset( $params['order.base.address.salutation'] )
428
+		if (isset($params['order.base.address.salutation'])
429 429
 				&& $params['order.base.address.salutation'] === \Aimeos\MShop\Common\Item\Address\Base::SALUTATION_COMPANY
430
-				&& in_array( 'order.base.address.company', $mandatory ) === false
430
+				&& in_array('order.base.address.company', $mandatory) === false
431 431
 		) {
432 432
 			$mandatory[] = 'order.base.address.company';
433 433
 		} else {
@@ -443,7 +443,7 @@  discard block
 block discarded – undo
443 443
 	 */
444 444
 	protected function getSubClientNames()
445 445
 	{
446
-		return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
446
+		return $this->getContext()->getConfig()->get($this->subPartPath, $this->subPartNames);
447 447
 	}
448 448
 
449 449
 
@@ -454,10 +454,10 @@  discard block
 block discarded – undo
454 454
 	 * @throws \Aimeos\Client\Html\Exception If an error occurs
455 455
 	 * @since 2016.05
456 456
 	 */
457
-	protected function setAddress( \Aimeos\MW\View\Iface $view )
457
+	protected function setAddress(\Aimeos\MW\View\Iface $view)
458 458
 	{
459 459
 		$context = $this->getContext();
460
-		$basketCtrl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
460
+		$basketCtrl = \Aimeos\Controller\Frontend\Factory::createController($context, 'basket');
461 461
 
462 462
 		/** client/html/checkout/standard/address/delivery/disable-new
463 463
 		 * Disables the option to enter a different delivery address for an order
@@ -478,58 +478,58 @@  discard block
 block discarded – undo
478 478
 		 * @see client/html/checkout/standard/address/delivery/optional
479 479
 		 * @see client/html/checkout/standard/address/delivery/hidden
480 480
 		*/
481
-		$disable = $view->config( 'client/html/checkout/standard/address/delivery/disable-new', false );
481
+		$disable = $view->config('client/html/checkout/standard/address/delivery/disable-new', false);
482 482
 		$type = \Aimeos\MShop\Order\Item\Base\Address\Base::TYPE_DELIVERY;
483 483
 
484
-		if( ( $option = $view->param( 'ca_deliveryoption', 'null' ) ) === 'null' && $disable === false ) // new address
484
+		if (($option = $view->param('ca_deliveryoption', 'null')) === 'null' && $disable === false) // new address
485 485
 		{
486
-			$params = $view->param( 'ca_delivery', array() );
487
-			$invalid = $this->checkFields( $params );
486
+			$params = $view->param('ca_delivery', array());
487
+			$invalid = $this->checkFields($params);
488 488
 
489
-			if( count( $invalid ) > 0 )
489
+			if (count($invalid) > 0)
490 490
 			{
491 491
 				$view->deliveryError = $invalid;
492
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'At least one delivery address part is missing or invalid' ) );
492
+				throw new \Aimeos\Client\Html\Exception(sprintf('At least one delivery address part is missing or invalid'));
493 493
 			}
494 494
 
495
-			$basketCtrl->setAddress( $type, $params );
495
+			$basketCtrl->setAddress($type, $params);
496 496
 		}
497
-		else if( ( $option = $view->param( 'ca_deliveryoption', 'null' ) ) !== '-1' ) // existing address
497
+		else if (($option = $view->param('ca_deliveryoption', 'null')) !== '-1') // existing address
498 498
 		{
499
-			$customerAddressManager = \Aimeos\MShop\Factory::createManager( $context, 'customer/address' );
500
-			$address = $customerAddressManager->getItem( $option );
499
+			$customerAddressManager = \Aimeos\MShop\Factory::createManager($context, 'customer/address');
500
+			$address = $customerAddressManager->getItem($option);
501 501
 
502
-			if( $address->getParentId() != $context->getUserId() ) {
503
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'Address with ID "%1$s" not found', $option ) );
502
+			if ($address->getParentId() != $context->getUserId()) {
503
+				throw new \Aimeos\Client\Html\Exception(sprintf('Address with ID "%1$s" not found', $option));
504 504
 			}
505 505
 
506 506
 			$invalid = array();
507
-			$params = $view->param( 'ca_delivery_' . $option, array() );
507
+			$params = $view->param('ca_delivery_'.$option, array());
508 508
 
509
-			if( !empty( $params ) )
509
+			if (!empty($params))
510 510
 			{
511 511
 				$list = array();
512
-				$invalid = $this->checkFields( $params );
512
+				$invalid = $this->checkFields($params);
513 513
 
514
-				foreach( $params as $key => $value ) {
515
-					$list[str_replace( 'order.base', 'customer', $key )] = $value;
514
+				foreach ($params as $key => $value) {
515
+					$list[str_replace('order.base', 'customer', $key)] = $value;
516 516
 				}
517 517
 
518
-				$address->fromArray( $list );
519
-				$customerAddressManager->saveItem( $address );
518
+				$address->fromArray($list);
519
+				$customerAddressManager->saveItem($address);
520 520
 			}
521 521
 
522
-			if( count( $invalid ) > 0 )
522
+			if (count($invalid) > 0)
523 523
 			{
524 524
 				$view->deliveryError = $invalid;
525
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'At least one delivery address part is missing or invalid' ) );
525
+				throw new \Aimeos\Client\Html\Exception(sprintf('At least one delivery address part is missing or invalid'));
526 526
 			}
527 527
 
528
-			$basketCtrl->setAddress( $type, $address );
528
+			$basketCtrl->setAddress($type, $address);
529 529
 		}
530 530
 		else
531 531
 		{
532
-			$basketCtrl->setAddress( $type, null );
532
+			$basketCtrl->setAddress($type, null);
533 533
 		}
534 534
 	}
535 535
 
@@ -542,17 +542,17 @@  discard block
 block discarded – undo
542 542
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
543 543
 	 * @return \Aimeos\MW\View\Iface Modified view object
544 544
 	 */
545
-	protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
545
+	protected function setViewParams(\Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null)
546 546
 	{
547
-		if( !isset( $this->cache ) )
547
+		if (!isset($this->cache))
548 548
 		{
549 549
 			$context = $this->getContext();
550
-			$basketCntl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
550
+			$basketCntl = \Aimeos\Controller\Frontend\Factory::createController($context, 'basket');
551 551
 
552 552
 			try {
553
-				$langid = $basketCntl->get()->getAddress( 'delivery' )->getLanguageId();
554
-			} catch( \Exception $e ) {
555
-				$langid = $view->param( 'ca_delivery/order.base.address.languageid', $context->getLocale()->getLanguageId() );
553
+				$langid = $basketCntl->get()->getAddress('delivery')->getLanguageId();
554
+			} catch (\Exception $e) {
555
+				$langid = $view->param('ca_delivery/order.base.address.languageid', $context->getLocale()->getLanguageId());
556 556
 			}
557 557
 			$view->deliveryLanguage = $langid;
558 558
 
@@ -595,13 +595,13 @@  discard block
 block discarded – undo
595 595
 			 * @see client/html/checkout/standard/address/delivery/optional
596 596
 			 * @see client/html/checkout/standard/address/countries
597 597
 			 */
598
-			$hidden = $view->config( 'client/html/checkout/standard/address/delivery/hidden', array() );
598
+			$hidden = $view->config('client/html/checkout/standard/address/delivery/hidden', array());
599 599
 
600
-			if( count( $view->get( 'addressLanguages', array() ) ) === 1 ) {
600
+			if (count($view->get('addressLanguages', array())) === 1) {
601 601
 				$hidden[] = 'order.base.address.languageid';
602 602
 			}
603 603
 
604
-			$salutations = array( 'company', 'mr', 'mrs' );
604
+			$salutations = array('company', 'mr', 'mrs');
605 605
 
606 606
 			/** client/html/checkout/standard/address/delivery/salutations
607 607
 			 * List of salutions the customer can select from for the delivery address
@@ -631,10 +631,10 @@  discard block
 block discarded – undo
631 631
 			 * @see client/html/checkout/standard/address/delivery/hidden
632 632
 			 * @see client/html/checkout/standard/address/countries
633 633
 			 */
634
-			$view->deliverySalutations = $view->config( 'client/html/checkout/standard/address/delivery/salutations', $salutations );
634
+			$view->deliverySalutations = $view->config('client/html/checkout/standard/address/delivery/salutations', $salutations);
635 635
 
636
-			$view->deliveryMandatory = $view->config( 'client/html/checkout/standard/address/delivery/mandatory', $this->mandatory );
637
-			$view->deliveryOptional = $view->config( 'client/html/checkout/standard/address/delivery/optional', $this->optional );
636
+			$view->deliveryMandatory = $view->config('client/html/checkout/standard/address/delivery/mandatory', $this->mandatory);
637
+			$view->deliveryOptional = $view->config('client/html/checkout/standard/address/delivery/optional', $this->optional);
638 638
 			$view->deliveryHidden = $hidden;
639 639
 
640 640
 
@@ -653,7 +653,7 @@  discard block
 block discarded – undo
653 653
 	 * @return array List of invalid address keys
654 654
 	 * @since 2016.05
655 655
 	 */
656
-	protected function validateFields( array &$params, array $fields )
656
+	protected function validateFields(array &$params, array $fields)
657 657
 	{
658 658
 		$config = $this->getContext()->getConfig();
659 659
 
@@ -761,22 +761,22 @@  discard block
 block discarded – undo
761 761
 
762 762
 		$invalid = array();
763 763
 
764
-		foreach( $params as $key => $value )
764
+		foreach ($params as $key => $value)
765 765
 		{
766
-			if( isset( $fields[$key] ) )
766
+			if (isset($fields[$key]))
767 767
 			{
768
-				$name = substr( $key, 19 );
769
-				$regex = $config->get( 'client/html/checkout/standard/address/validate/' . $name );
768
+				$name = substr($key, 19);
769
+				$regex = $config->get('client/html/checkout/standard/address/validate/'.$name);
770 770
 
771
-				if( $regex && preg_match( '/' . $regex . '/', $value ) !== 1 )
771
+				if ($regex && preg_match('/'.$regex.'/', $value) !== 1)
772 772
 				{
773 773
 					$invalid[$key] = $name;
774
-					unset( $params[$key] );
774
+					unset($params[$key]);
775 775
 				}
776 776
 			}
777 777
 			else
778 778
 			{
779
-				unset( $params[$key] );
779
+				unset($params[$key]);
780 780
 			}
781 781
 		}
782 782
 
Please login to merge, or discard this patch.
Braces   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -286,8 +286,7 @@  discard block
 block discarded – undo
286 286
 			$this->setAddress( $view );
287 287
 
288 288
 			parent::process();
289
-		}
290
-		catch( \Aimeos\Controller\Frontend\Exception $e )
289
+		} catch( \Aimeos\Controller\Frontend\Exception $e )
291 290
 		{
292 291
 			$view->deliveryError = $e->getErrorList();
293 292
 			throw $e;
@@ -481,9 +480,11 @@  discard block
 block discarded – undo
481 480
 		$disable = $view->config( 'client/html/checkout/standard/address/delivery/disable-new', false );
482 481
 		$type = \Aimeos\MShop\Order\Item\Base\Address\Base::TYPE_DELIVERY;
483 482
 
484
-		if( ( $option = $view->param( 'ca_deliveryoption', 'null' ) ) === 'null' && $disable === false ) // new address
483
+		if( ( $option = $view->param( 'ca_deliveryoption', 'null' ) ) === 'null' && $disable === false ) {
484
+		    // new address
485 485
 		{
486 486
 			$params = $view->param( 'ca_delivery', array() );
487
+		}
487 488
 			$invalid = $this->checkFields( $params );
488 489
 
489 490
 			if( count( $invalid ) > 0 )
@@ -493,10 +494,11 @@  discard block
 block discarded – undo
493 494
 			}
494 495
 
495 496
 			$basketCtrl->setAddress( $type, $params );
496
-		}
497
-		else if( ( $option = $view->param( 'ca_deliveryoption', 'null' ) ) !== '-1' ) // existing address
497
+		} else if( ( $option = $view->param( 'ca_deliveryoption', 'null' ) ) !== '-1' ) {
498
+		    // existing address
498 499
 		{
499 500
 			$customerAddressManager = \Aimeos\MShop\Factory::createManager( $context, 'customer/address' );
501
+		}
500 502
 			$address = $customerAddressManager->getItem( $option );
501 503
 
502 504
 			if( $address->getParentId() != $context->getUserId() ) {
@@ -526,8 +528,7 @@  discard block
 block discarded – undo
526 528
 			}
527 529
 
528 530
 			$basketCtrl->setAddress( $type, $address );
529
-		}
530
-		else
531
+		} else
531 532
 		{
532 533
 			$basketCtrl->setAddress( $type, null );
533 534
 		}
@@ -773,8 +774,7 @@  discard block
 block discarded – undo
773 774
 					$invalid[$key] = $name;
774 775
 					unset( $params[$key] );
775 776
 				}
776
-			}
777
-			else
777
+			} else
778 778
 			{
779 779
 				unset( $params[$key] );
780 780
 			}
Please login to merge, or discard this patch.
client/html/src/Client/Html/Checkout/Standard/Address/Billing/Standard.php 3 patches
Indentation   +826 added lines, -826 removed lines patch added patch discarded remove patch
@@ -19,831 +19,831 @@
 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
-					$msg =
837
-					$invalid[$key] = $name;
838
-					unset( $params[$key] );
839
-				}
840
-			}
841
-			else
842
-			{
843
-				unset( $params[$key] );
844
-			}
845
-		}
846
-
847
-		return $invalid;
848
-	}
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
+                    $msg =
837
+                    $invalid[$key] = $name;
838
+                    unset( $params[$key] );
839
+                }
840
+            }
841
+            else
842
+            {
843
+                unset( $params[$key] );
844
+            }
845
+        }
846
+
847
+        return $invalid;
848
+    }
849 849
 }
850 850
\ No newline at end of file
Please login to merge, or discard this patch.
Spacing   +83 added lines, -83 removed lines patch added patch discarded remove patch
@@ -87,13 +87,13 @@  discard block
 block discarded – undo
87 87
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
88 88
 	 * @return string HTML code
89 89
 	 */
90
-	public function getBody( $uid = '', array &$tags = array(), &$expire = null )
90
+	public function getBody($uid = '', array &$tags = array(), &$expire = null)
91 91
 	{
92
-		$view = $this->setViewParams( $this->getView(), $tags, $expire );
92
+		$view = $this->setViewParams($this->getView(), $tags, $expire);
93 93
 
94 94
 		$html = '';
95
-		foreach( $this->getSubClients() as $subclient ) {
96
-			$html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
95
+		foreach ($this->getSubClients() as $subclient) {
96
+			$html .= $subclient->setView($view)->getBody($uid, $tags, $expire);
97 97
 		}
98 98
 		$view->billingBody = $html;
99 99
 
@@ -120,7 +120,7 @@  discard block
 block discarded – undo
120 120
 		$tplconf = 'client/html/checkout/standard/address/billing/standard/template-body';
121 121
 		$default = 'checkout/standard/address-billing-body-default.php';
122 122
 
123
-		return $view->render( $view->config( $tplconf, $default ) );
123
+		return $view->render($view->config($tplconf, $default));
124 124
 	}
125 125
 
126 126
 
@@ -132,13 +132,13 @@  discard block
 block discarded – undo
132 132
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
133 133
 	 * @return string|null String including HTML tags for the header on error
134 134
 	 */
135
-	public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
135
+	public function getHeader($uid = '', array &$tags = array(), &$expire = null)
136 136
 	{
137
-		$view = $this->setViewParams( $this->getView(), $tags, $expire );
137
+		$view = $this->setViewParams($this->getView(), $tags, $expire);
138 138
 
139 139
 		$html = '';
140
-		foreach( $this->getSubClients() as $subclient ) {
141
-			$html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
140
+		foreach ($this->getSubClients() as $subclient) {
141
+			$html .= $subclient->setView($view)->getHeader($uid, $tags, $expire);
142 142
 		}
143 143
 		$view->billingHeader = $html;
144 144
 
@@ -166,7 +166,7 @@  discard block
 block discarded – undo
166 166
 		$tplconf = 'client/html/checkout/standard/address/billing/standard/template-header';
167 167
 		$default = 'checkout/standard/address-billing-header-default.php';
168 168
 
169
-		return $view->render( $view->config( $tplconf, $default ) );
169
+		return $view->render($view->config($tplconf, $default));
170 170
 	}
171 171
 
172 172
 
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
 	 * @param string|null $name Name of the sub-client (Default if null)
178 178
 	 * @return \Aimeos\Client\Html\Iface Sub-client object
179 179
 	 */
180
-	public function getSubClient( $type, $name = null )
180
+	public function getSubClient($type, $name = null)
181 181
 	{
182 182
 		/** client/html/checkout/standard/address/billing/decorators/excludes
183 183
 		 * Excludes decorators added by the "common" option from the checkout standard address billing html client
@@ -253,7 +253,7 @@  discard block
 block discarded – undo
253 253
 		 * @see client/html/checkout/standard/address/billing/decorators/global
254 254
 		 */
255 255
 
256
-		return $this->createSubClient( 'checkout/standard/address/billing/' . $type, $name );
256
+		return $this->createSubClient('checkout/standard/address/billing/'.$type, $name);
257 257
 	}
258 258
 
259 259
 
@@ -267,15 +267,15 @@  discard block
 block discarded – undo
267 267
 		try
268 268
 		{
269 269
 			// only start if there's something to do
270
-			if( $view->param( 'ca_billingoption', null ) === null ) {
270
+			if ($view->param('ca_billingoption', null) === null) {
271 271
 				return;
272 272
 			}
273 273
 
274
-			$this->setAddress( $view );
274
+			$this->setAddress($view);
275 275
 
276 276
 			parent::process();
277 277
 		}
278
-		catch( \Aimeos\Controller\Frontend\Exception $e )
278
+		catch (\Aimeos\Controller\Frontend\Exception $e)
279 279
 		{
280 280
 			$view->billingError = $e->getErrorList();
281 281
 			throw $e;
@@ -289,7 +289,7 @@  discard block
 block discarded – undo
289 289
 	 * @param array &$params Associative list of address keys (order.base.address.* or customer.address.*) and their values
290 290
 	 * @return array List of missing field names
291 291
 	 */
292
-	protected function checkFields( array &$params )
292
+	protected function checkFields(array &$params)
293 293
 	{
294 294
 		$view = $this->getView();
295 295
 
@@ -331,7 +331,7 @@  discard block
 block discarded – undo
331 331
 		 * @see client/html/checkout/standard/address/countries
332 332
 		 * @see client/html/checkout/standard/address/validate
333 333
 		 */
334
-		$mandatory = $view->config( 'client/html/checkout/standard/address/billing/mandatory', $this->mandatory );
334
+		$mandatory = $view->config('client/html/checkout/standard/address/billing/mandatory', $this->mandatory);
335 335
 
336 336
 		/** client/html/checkout/standard/address/billing/optional
337 337
 		 * List of billing address input fields that are optional
@@ -371,7 +371,7 @@  discard block
 block discarded – undo
371 371
 		 * @see client/html/checkout/standard/address/countries
372 372
 		 * @see client/html/checkout/standard/address/validate
373 373
 		 */
374
-		$optional = $view->config( 'client/html/checkout/standard/address/billing/optional', $this->optional );
374
+		$optional = $view->config('client/html/checkout/standard/address/billing/optional', $this->optional);
375 375
 
376 376
 		/** client/html/checkout/standard/address/validate
377 377
 		 * List of regular expressions to validate the data of the address fields
@@ -430,23 +430,23 @@  discard block
 block discarded – undo
430 430
 		 * @see client/html/checkout/standard/address/billing/optional
431 431
 		 */
432 432
 
433
-		$allFields = array_flip( array_merge( $mandatory, $optional ) );
434
-		$invalid = $this->validateFields( $params, $allFields );
435
-		$this->checkSalutation( $params, $mandatory );
433
+		$allFields = array_flip(array_merge($mandatory, $optional));
434
+		$invalid = $this->validateFields($params, $allFields);
435
+		$this->checkSalutation($params, $mandatory);
436 436
 
437
-		foreach( $invalid as $key => $name )
437
+		foreach ($invalid as $key => $name)
438 438
 		{
439
-			$msg = $view->translate( 'client', 'Billing address part "%1$s" is invalid' );
440
-			$invalid[$key] = sprintf( $msg, $name );
439
+			$msg = $view->translate('client', 'Billing address part "%1$s" is invalid');
440
+			$invalid[$key] = sprintf($msg, $name);
441 441
 		}
442 442
 
443
-		foreach( $mandatory as $key )
443
+		foreach ($mandatory as $key)
444 444
 		{
445
-			if( !isset( $params[$key] ) || $params[$key] == '' )
445
+			if (!isset($params[$key]) || $params[$key] == '')
446 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] );
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 450
 			}
451 451
 		}
452 452
 
@@ -461,11 +461,11 @@  discard block
 block discarded – undo
461 461
 	 * @param array &$mandatory List of mandatory field names
462 462
 	 * @since 2016.05
463 463
 	 */
464
-	protected function checkSalutation( array &$params, array &$mandatory )
464
+	protected function checkSalutation(array &$params, array &$mandatory)
465 465
 	{
466
-		if( isset( $params['order.base.address.salutation'] )
466
+		if (isset($params['order.base.address.salutation'])
467 467
 			&& $params['order.base.address.salutation'] === \Aimeos\MShop\Common\Item\Address\Base::SALUTATION_COMPANY
468
-			&& in_array( 'order.base.address.company', $mandatory ) === false
468
+			&& in_array('order.base.address.company', $mandatory) === false
469 469
 		) {
470 470
 			$mandatory[] = 'order.base.address.company';
471 471
 		} else {
@@ -482,22 +482,22 @@  discard block
 block discarded – undo
482 482
 	 * @throws \Aimeos\Client\Html\Exception If no customer item is available
483 483
 	 * @since 2016.05
484 484
 	 */
485
-	protected function getCustomerItem( $id )
485
+	protected function getCustomerItem($id)
486 486
 	{
487 487
 		$context = $this->getContext();
488
-		$customerManager = \Aimeos\MShop\Factory::createManager( $context, 'customer' );
488
+		$customerManager = \Aimeos\MShop\Factory::createManager($context, 'customer');
489 489
 
490
-		$search = $customerManager->createSearch( true );
490
+		$search = $customerManager->createSearch(true);
491 491
 		$expr = array(
492
-				$search->compare( '==', 'customer.id', $id ),
492
+				$search->compare('==', 'customer.id', $id),
493 493
 				$search->getConditions(),
494 494
 		);
495
-		$search->setConditions( $search->combine( '&&', $expr ) );
495
+		$search->setConditions($search->combine('&&', $expr));
496 496
 
497
-		$items = $customerManager->searchItems( $search );
497
+		$items = $customerManager->searchItems($search);
498 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 ) );
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 501
 		}
502 502
 
503 503
 		return $item;
@@ -511,7 +511,7 @@  discard block
 block discarded – undo
511 511
 	 */
512 512
 	protected function getSubClientNames()
513 513
 	{
514
-		return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
514
+		return $this->getContext()->getConfig()->get($this->subPartPath, $this->subPartNames);
515 515
 	}
516 516
 
517 517
 
@@ -522,10 +522,10 @@  discard block
 block discarded – undo
522 522
 	 * @throws \Aimeos\Client\Html\Exception If an error occurs
523 523
 	 * @since 2016.05
524 524
 	 */
525
-	protected function setAddress( \Aimeos\MW\View\Iface $view )
525
+	protected function setAddress(\Aimeos\MW\View\Iface $view)
526 526
 	{
527 527
 		$context = $this->getContext();
528
-		$basketCtrl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
528
+		$basketCtrl = \Aimeos\Controller\Frontend\Factory::createController($context, 'basket');
529 529
 
530 530
 
531 531
 		/** client/html/checkout/standard/address/billing/disable-new
@@ -547,53 +547,53 @@  discard block
 block discarded – undo
547 547
 		 * @see client/html/checkout/standard/address/billing/optional
548 548
 		 * @see client/html/checkout/standard/address/billing/hidden
549 549
 		*/
550
-		$disable = $view->config( 'client/html/checkout/standard/address/billing/disable-new', false );
550
+		$disable = $view->config('client/html/checkout/standard/address/billing/disable-new', false);
551 551
 		$type = \Aimeos\MShop\Order\Item\Base\Address\Base::TYPE_PAYMENT;
552 552
 
553
-		if( ( $option = $view->param( 'ca_billingoption', 'null' ) ) === 'null' && $disable === false ) // new address
553
+		if (($option = $view->param('ca_billingoption', 'null')) === 'null' && $disable === false) // new address
554 554
 		{
555
-			$params = $view->param( 'ca_billing', array() );
556
-			$invalid = $this->checkFields( $params );
555
+			$params = $view->param('ca_billing', array());
556
+			$invalid = $this->checkFields($params);
557 557
 
558
-			if( count( $invalid ) > 0 )
558
+			if (count($invalid) > 0)
559 559
 			{
560 560
 				$view->billingError = $invalid;
561
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'At least one billing address part is missing or invalid' ) );
561
+				throw new \Aimeos\Client\Html\Exception(sprintf('At least one billing address part is missing or invalid'));
562 562
 			}
563 563
 
564
-			$basketCtrl->setAddress( $type, $params );
564
+			$basketCtrl->setAddress($type, $params);
565 565
 		}
566 566
 		else // existing address
567 567
 		{
568
-			$item = $this->getCustomerItem( $option );
569
-			$customerManager = \Aimeos\MShop\Factory::createManager( $context, 'customer' );
568
+			$item = $this->getCustomerItem($option);
569
+			$customerManager = \Aimeos\MShop\Factory::createManager($context, 'customer');
570 570
 
571 571
 			$invalid = array();
572 572
 			$addr = $item->getPaymentAddress();
573
-			$params = $view->param( 'ca_billing_' . $option, array() );
573
+			$params = $view->param('ca_billing_'.$option, array());
574 574
 
575
-			if( !empty( $params ) )
575
+			if (!empty($params))
576 576
 			{
577 577
 				$list = array();
578
-				$invalid = $this->checkFields( $params );
578
+				$invalid = $this->checkFields($params);
579 579
 
580
-				foreach( $params as $key => $value ) {
581
-					$list[str_replace( 'order.base', 'customer', $key )] = $value;
580
+				foreach ($params as $key => $value) {
581
+					$list[str_replace('order.base', 'customer', $key)] = $value;
582 582
 				}
583 583
 
584
-				$addr->fromArray( $list );
585
-				$item->setPaymentAddress( $addr );
584
+				$addr->fromArray($list);
585
+				$item->setPaymentAddress($addr);
586 586
 
587
-				$customerManager->saveItem( $item );
587
+				$customerManager->saveItem($item);
588 588
 			}
589 589
 
590
-			if( count( $invalid ) > 0 )
590
+			if (count($invalid) > 0)
591 591
 			{
592 592
 				$view->billingError = $invalid;
593
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'At least one billing address part is missing or invalid' ) );
593
+				throw new \Aimeos\Client\Html\Exception(sprintf('At least one billing address part is missing or invalid'));
594 594
 			}
595 595
 
596
-			$basketCtrl->setAddress( $type, $addr );
596
+			$basketCtrl->setAddress($type, $addr);
597 597
 		}
598 598
 	}
599 599
 
@@ -606,17 +606,17 @@  discard block
 block discarded – undo
606 606
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
607 607
 	 * @return \Aimeos\MW\View\Iface Modified view object
608 608
 	 */
609
-	protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
609
+	protected function setViewParams(\Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null)
610 610
 	{
611
-		if( !isset( $this->cache ) )
611
+		if (!isset($this->cache))
612 612
 		{
613 613
 			$context = $this->getContext();
614
-			$basketCntl = \Aimeos\Controller\Frontend\Factory::createController( $context, 'basket' );
614
+			$basketCntl = \Aimeos\Controller\Frontend\Factory::createController($context, 'basket');
615 615
 
616 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() );
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 620
 			}
621 621
 			$view->billingLanguage = $langid;
622 622
 
@@ -659,13 +659,13 @@  discard block
 block discarded – undo
659 659
 			 * @see client/html/checkout/standard/address/billing/optional
660 660
 			 * @see client/html/checkout/standard/address/countries
661 661
 			 */
662
-			$hidden = $view->config( 'client/html/checkout/standard/address/billing/hidden', array() );
662
+			$hidden = $view->config('client/html/checkout/standard/address/billing/hidden', array());
663 663
 
664
-			if( count( $view->get( 'addressLanguages', array() ) ) === 1 ) {
664
+			if (count($view->get('addressLanguages', array())) === 1) {
665 665
 				$hidden[] = 'order.base.address.languageid';
666 666
 			}
667 667
 
668
-			$salutations = array( 'company', 'mr', 'mrs' );
668
+			$salutations = array('company', 'mr', 'mrs');
669 669
 
670 670
 			/** client/html/checkout/standard/address/billing/salutations
671 671
 			 * List of salutions the customer can select from for the billing address
@@ -695,10 +695,10 @@  discard block
 block discarded – undo
695 695
 			 * @see client/html/checkout/standard/address/billing/hidden
696 696
 			 * @see client/html/checkout/standard/address/countries
697 697
 			 */
698
-			$view->billingSalutations = $view->config( 'client/html/checkout/standard/address/billing/salutations', $salutations );
698
+			$view->billingSalutations = $view->config('client/html/checkout/standard/address/billing/salutations', $salutations);
699 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 );
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 702
 			$view->billingHidden = $hidden;
703 703
 
704 704
 			$this->cache = $view;
@@ -716,7 +716,7 @@  discard block
 block discarded – undo
716 716
 	 * @return array List of invalid address keys
717 717
 	 * @since 2016.05
718 718
 	 */
719
-	protected function validateFields( array &$params, array $fields )
719
+	protected function validateFields(array &$params, array $fields)
720 720
 	{
721 721
 		$config = $this->getContext()->getConfig();
722 722
 
@@ -824,23 +824,23 @@  discard block
 block discarded – undo
824 824
 
825 825
 		$invalid = array();
826 826
 
827
-		foreach( $params as $key => $value )
827
+		foreach ($params as $key => $value)
828 828
 		{
829
-			if( isset( $fields[$key] ) )
829
+			if (isset($fields[$key]))
830 830
 			{
831
-				$name = substr( $key, 19 );
832
-				$regex = $config->get( 'client/html/checkout/standard/address/validate/' . $name );
831
+				$name = substr($key, 19);
832
+				$regex = $config->get('client/html/checkout/standard/address/validate/'.$name);
833 833
 
834
-				if( $regex && preg_match( '/' . $regex . '/', $value ) !== 1 )
834
+				if ($regex && preg_match('/'.$regex.'/', $value) !== 1)
835 835
 				{
836 836
 					$msg =
837 837
 					$invalid[$key] = $name;
838
-					unset( $params[$key] );
838
+					unset($params[$key]);
839 839
 				}
840 840
 			}
841 841
 			else
842 842
 			{
843
-				unset( $params[$key] );
843
+				unset($params[$key]);
844 844
 			}
845 845
 		}
846 846
 
Please login to merge, or discard this patch.
Braces   +6 added lines, -7 removed lines patch added patch discarded remove patch
@@ -274,8 +274,7 @@  discard block
 block discarded – undo
274 274
 			$this->setAddress( $view );
275 275
 
276 276
 			parent::process();
277
-		}
278
-		catch( \Aimeos\Controller\Frontend\Exception $e )
277
+		} catch( \Aimeos\Controller\Frontend\Exception $e )
279 278
 		{
280 279
 			$view->billingError = $e->getErrorList();
281 280
 			throw $e;
@@ -550,9 +549,11 @@  discard block
 block discarded – undo
550 549
 		$disable = $view->config( 'client/html/checkout/standard/address/billing/disable-new', false );
551 550
 		$type = \Aimeos\MShop\Order\Item\Base\Address\Base::TYPE_PAYMENT;
552 551
 
553
-		if( ( $option = $view->param( 'ca_billingoption', 'null' ) ) === 'null' && $disable === false ) // new address
552
+		if( ( $option = $view->param( 'ca_billingoption', 'null' ) ) === 'null' && $disable === false ) {
553
+		    // new address
554 554
 		{
555 555
 			$params = $view->param( 'ca_billing', array() );
556
+		}
556 557
 			$invalid = $this->checkFields( $params );
557 558
 
558 559
 			if( count( $invalid ) > 0 )
@@ -562,8 +563,7 @@  discard block
 block discarded – undo
562 563
 			}
563 564
 
564 565
 			$basketCtrl->setAddress( $type, $params );
565
-		}
566
-		else // existing address
566
+		} else // existing address
567 567
 		{
568 568
 			$item = $this->getCustomerItem( $option );
569 569
 			$customerManager = \Aimeos\MShop\Factory::createManager( $context, 'customer' );
@@ -837,8 +837,7 @@  discard block
 block discarded – undo
837 837
 					$invalid[$key] = $name;
838 838
 					unset( $params[$key] );
839 839
 				}
840
-			}
841
-			else
840
+			} else
842 841
 			{
843 842
 				unset( $params[$key] );
844 843
 			}
Please login to merge, or discard this patch.
client/html/src/Client/Html/Account/Watch/Standard.php 2 patches
Indentation   +601 added lines, -601 removed lines patch added patch discarded remove patch
@@ -19,606 +19,606 @@
 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/account/watch/standard/subparts
26
-	 * List of HTML sub-clients rendered within the account watch 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/account/watch/standard/subparts';
59
-	private $subPartNames = array();
60
-	private $cache;
61
-
62
-
63
-	/**
64
-	 * Returns the HTML code for insertion into the body.
65
-	 *
66
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
67
-	 * @param array &$tags Result array for the list of tags that are associated to the output
68
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
69
-	 * @return string HTML code
70
-	 */
71
-	public function getBody( $uid = '', array &$tags = array(), &$expire = null )
72
-	{
73
-		$context = $this->getContext();
74
-		$view = $this->getView();
75
-
76
-		try
77
-		{
78
-			$view = $this->setViewParams( $view, $tags, $expire );
79
-
80
-			$html = '';
81
-			foreach( $this->getSubClients() as $subclient ) {
82
-				$html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
83
-			}
84
-			$view->watchBody = $html;
85
-		}
86
-		catch( \Aimeos\Client\Html\Exception $e )
87
-		{
88
-			$error = array( $this->getContext()->getI18n()->dt( 'client', $e->getMessage() ) );
89
-			$view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
90
-		}
91
-		catch( \Aimeos\Controller\Frontend\Exception $e )
92
-		{
93
-			$error = array( $this->getContext()->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
94
-			$view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
95
-		}
96
-		catch( \Aimeos\MShop\Exception $e )
97
-		{
98
-			$error = array( $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ) );
99
-			$view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
100
-		}
101
-		catch( \Exception $e )
102
-		{
103
-			$context->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
104
-
105
-			$error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
106
-			$view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
107
-		}
108
-
109
-		/** client/html/account/watch/standard/template-body
110
-		 * Relative path to the HTML body template of the account watch client.
111
-		 *
112
-		 * The template file contains the HTML code and processing instructions
113
-		 * to generate the result shown in the body of the frontend. The
114
-		 * configuration string is the path to the template file relative
115
-		 * to the templates directory (usually in client/html/templates).
116
-		 *
117
-		 * You can overwrite the template file configuration in extensions and
118
-		 * provide alternative templates. These alternative templates should be
119
-		 * named like the default one but with the string "standard" replaced by
120
-		 * an unique name. You may use the name of your project for this. If
121
-		 * you've implemented an alternative client class as well, "standard"
122
-		 * should be replaced by the name of the new class.
123
-		 *
124
-		 * @param string Relative path to the template creating code for the HTML page body
125
-		 * @since 2014.03
126
-		 * @category Developer
127
-		 * @see client/html/account/watch/standard/template-header
128
-		 */
129
-		$tplconf = 'client/html/account/watch/standard/template-body';
130
-		$default = 'account/watch/body-default.php';
131
-
132
-		return $view->render( $view->config( $tplconf, $default ) );
133
-	}
134
-
135
-
136
-	/**
137
-	 * Returns the HTML string for insertion into the header.
138
-	 *
139
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
140
-	 * @param array &$tags Result array for the list of tags that are associated to the output
141
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
142
-	 * @return string|null String including HTML tags for the header on error
143
-	 */
144
-	public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
145
-	{
146
-		try
147
-		{
148
-			$view = $this->setViewParams( $this->getView(), $tags, $expire );
149
-
150
-			$html = '';
151
-			foreach( $this->getSubClients() as $subclient ) {
152
-				$html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
153
-			}
154
-			$view->watchHeader = $html;
155
-
156
-			/** client/html/account/watch/standard/template-header
157
-			 * Relative path to the HTML header template of the account watch client.
158
-			 *
159
-			 * The template file contains the HTML code and processing instructions
160
-			 * to generate the HTML code that is inserted into the HTML page header
161
-			 * of the rendered page in the frontend. The configuration string is the
162
-			 * path to the template file relative to the templates directory (usually
163
-			 * in client/html/templates).
164
-			 *
165
-			 * You can overwrite the template file configuration in extensions and
166
-			 * provide alternative templates. These alternative templates should be
167
-			 * named like the default one but with the string "standard" replaced by
168
-			 * an unique name. You may use the name of your project for this. If
169
-			 * you've implemented an alternative client class as well, "standard"
170
-			 * should be replaced by the name of the new class.
171
-			 *
172
-			 * @param string Relative path to the template creating code for the HTML page head
173
-			 * @since 2014.03
174
-			 * @category Developer
175
-			 * @see client/html/account/watch/standard/template-body
176
-			 */
177
-			$tplconf = 'client/html/account/watch/standard/template-header';
178
-			$default = 'account/watch/header-default.php';
179
-
180
-			return $view->render( $view->config( $tplconf, $default ) );
181
-		}
182
-		catch( \Exception $e )
183
-		{
184
-			$this->getContext()->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
185
-		}
186
-	}
187
-
188
-
189
-	/**
190
-	 * Returns the sub-client given by its name.
191
-	 *
192
-	 * @param string $type Name of the client type
193
-	 * @param string|null $name Name of the sub-client (Default if null)
194
-	 * @return \Aimeos\Client\Html\Iface Sub-client object
195
-	 */
196
-	public function getSubClient( $type, $name = null )
197
-	{
198
-		/** client/html/account/watch/decorators/excludes
199
-		 * Excludes decorators added by the "common" option from the account watch html client
200
-		 *
201
-		 * Decorators extend the functionality of a class by adding new aspects
202
-		 * (e.g. log what is currently done), executing the methods of the underlying
203
-		 * class only in certain conditions (e.g. only for logged in users) or
204
-		 * modify what is returned to the caller.
205
-		 *
206
-		 * This option allows you to remove a decorator added via
207
-		 * "client/html/common/decorators/default" before they are wrapped
208
-		 * around the html client.
209
-		 *
210
-		 *  client/html/account/watch/decorators/excludes = array( 'decorator1' )
211
-		 *
212
-		 * This would remove the decorator named "decorator1" from the list of
213
-		 * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
214
-		 * "client/html/common/decorators/default" to the html client.
215
-		 *
216
-		 * @param array List of decorator names
217
-		 * @since 2014.05
218
-		 * @category Developer
219
-		 * @see client/html/common/decorators/default
220
-		 * @see client/html/account/watch/decorators/global
221
-		 * @see client/html/account/watch/decorators/local
222
-		 */
223
-
224
-		/** client/html/account/watch/decorators/global
225
-		 * Adds a list of globally available decorators only to the account watch html client
226
-		 *
227
-		 * Decorators extend the functionality of a class by adding new aspects
228
-		 * (e.g. log what is currently done), executing the methods of the underlying
229
-		 * class only in certain conditions (e.g. only for logged in users) or
230
-		 * modify what is returned to the caller.
231
-		 *
232
-		 * This option allows you to wrap global decorators
233
-		 * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
234
-		 *
235
-		 *  client/html/account/watch/decorators/global = array( 'decorator1' )
236
-		 *
237
-		 * This would add the decorator named "decorator1" defined by
238
-		 * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
239
-		 *
240
-		 * @param array List of decorator names
241
-		 * @since 2014.05
242
-		 * @category Developer
243
-		 * @see client/html/common/decorators/default
244
-		 * @see client/html/account/watch/decorators/excludes
245
-		 * @see client/html/account/watch/decorators/local
246
-		 */
247
-
248
-		/** client/html/account/watch/decorators/local
249
-		 * Adds a list of local decorators only to the account watch html client
250
-		 *
251
-		 * Decorators extend the functionality of a class by adding new aspects
252
-		 * (e.g. log what is currently done), executing the methods of the underlying
253
-		 * class only in certain conditions (e.g. only for logged in users) or
254
-		 * modify what is returned to the caller.
255
-		 *
256
-		 * This option allows you to wrap local decorators
257
-		 * ("\Aimeos\Client\Html\Account\Decorator\*") around the html client.
258
-		 *
259
-		 *  client/html/account/watch/decorators/local = array( 'decorator2' )
260
-		 *
261
-		 * This would add the decorator named "decorator2" defined by
262
-		 * "\Aimeos\Client\Html\Account\Decorator\Decorator2" only to the html client.
263
-		 *
264
-		 * @param array List of decorator names
265
-		 * @since 2014.05
266
-		 * @category Developer
267
-		 * @see client/html/common/decorators/default
268
-		 * @see client/html/account/watch/decorators/excludes
269
-		 * @see client/html/account/watch/decorators/global
270
-		 */
271
-
272
-		return $this->createSubClient( 'account/watch/' . $type, $name );
273
-	}
274
-
275
-
276
-	/**
277
-	 * Processes the input, e.g. store given values.
278
-	 * A view must be available and this method doesn't generate any output
279
-	 * besides setting view variables.
280
-	 */
281
-	public function process()
282
-	{
283
-		$view = $this->getView();
284
-		$context = $this->getContext();
285
-		$userId = $context->getUserId();
286
-		$ids = (array) $view->param( 'wat_id', array() );
287
-
288
-		if( $userId != null && !empty( $ids ) )
289
-		{
290
-			$typeId = $this->getTypeItem( 'customer/lists/type', 'product', 'watch' )->getId();
291
-			$manager = \Aimeos\MShop\Factory::createManager( $context, 'customer/lists' );
292
-			$items = $this->getListItems( $manager, $ids, $typeId, $userId );
293
-
294
-			switch( $view->param( 'wat_action' ) )
295
-			{
296
-				case 'add':
297
-
298
-					/** client/html/account/watch/standard/maxitems
299
-					 * Maximum number of products that can be watched in parallel
300
-					 *
301
-					 * This option limits the number of products that can be watched
302
-					 * after the users added the products to their watch list.
303
-					 * It must be a positive integer value greater than 0.
304
-					 *
305
-					 * Note: It's recommended to set this value not too high as this
306
-					 * leads to a high memory consumption when the e-mails are generated
307
-					 * to notify the customers. The memory used will up to 100*maxitems
308
-					 * of the footprint of one product item including the associated
309
-					 * texts, prices and media.
310
-					 *
311
-					 * @param integer Number of products
312
-					 * @since 2014.09
313
-					 * @category User
314
-					 * @category Developer
315
-					 */
316
-					$max = $context->getConfig()->get( 'client/html/account/watch/standard/maxitems', 100 );
317
-					$cnt = count( $ids );
318
-
319
-					if( $this->checkLimit( $manager, $typeId, $userId, $max, $cnt ) === false )
320
-					{
321
-						$error = sprintf( $context->getI18n()->dt( 'client', 'You can only watch up to %1$s products' ), $max );
322
-						$view->watchErrorList = $view->get( 'watchErrorList', array() ) + array( $error );
323
-						break;
324
-					}
325
-
326
-					$this->addItems( $manager, $items, $ids, $typeId, $userId );
327
-					break;
328
-
329
-				case 'edit':
330
-
331
-					$config = array(
332
-						'timeframe' => $view->param( 'wat_timeframe', 7 ),
333
-						'pricevalue' => $view->param( 'wat_pricevalue', '0.00' ),
334
-						'price' => $view->param( 'wat_price', 0 ),
335
-						'stock' => $view->param( 'wat_stock', 0 ),
336
-						'currency' => $context->getLocale()->getCurrencyId(),
337
-					);
338
-					$this->editItems( $manager, $items, $ids, $config );
339
-					break;
340
-
341
-				case 'delete':
342
-
343
-					$this->deleteItems( $manager, $items, $ids );
344
-					break;
345
-			}
346
-		}
347
-
348
-		parent::process();
349
-	}
350
-
351
-
352
-	/**
353
-	 * Tests if the maximum number of entries per user is already reached
354
-	 *
355
-	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
356
-	 * @param string $typeId List type ID of the referenced items
357
-	 * @param string $userId Unique user ID
358
-	 * @param integer $max Maximum number of items that are allowed
359
-	 * @param integer $cnt Number of items that should be added
360
-	 * @return boolean True if items can be added, false if not
361
-	 */
362
-	protected function checkLimit( \Aimeos\MShop\Common\Manager\Iface $manager, $typeId, $userId, $max, $cnt )
363
-	{
364
-		$search = $manager->createSearch();
365
-		$expr = array(
366
-			$search->compare( '==', 'customer.lists.parentid', $userId ),
367
-			$search->compare( '==', 'customer.lists.typeid', $typeId ),
368
-			$search->compare( '==', 'customer.lists.domain', 'product' ),
369
-		);
370
-		$search->setConditions( $search->combine( '&&', $expr ) );
371
-		$search->setSlice( 0, 0 );
372
-
373
-		$total = 0;
374
-		$manager->searchItems( $search, array(), $total );
375
-
376
-		if( $total + $cnt > $max ) {
377
-			return false;
378
-		}
379
-
380
-		return true;
381
-	}
382
-
383
-
384
-	/**
385
-	 * Adds one or more list items to the given user
386
-	 *
387
-	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
388
-	 * @param array $listItems Associative list of the reference IDs as keys and the list items as values
389
-	 * @param array $ids List of referenced IDs
390
-	 * @param string $typeId List type ID of the referenced items
391
-	 * @param string $userId Unique user ID
392
-	 */
393
-	protected function addItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids, $typeId, $userId )
394
-	{
395
-		$item = $manager->createItem();
396
-		$item->setParentId( $userId );
397
-		$item->setTypeId( $typeId );
398
-		$item->setDomain( 'product' );
399
-		$item->setStatus( 1 );
400
-
401
-		foreach( $ids as $id )
402
-		{
403
-			if( !isset( $listItems[$id] ) )
404
-			{
405
-				$item->setId( null );
406
-				$item->setRefId( $id );
407
-
408
-				$manager->saveItem( $item );
409
-				$manager->moveItem( $item->getId() );
410
-			}
411
-		}
412
-	}
413
-
414
-
415
-	/**
416
-	 * Removes the list items for the given reference IDs
417
-	 *
418
-	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
419
-	 * @param array $listItems Associative list of the reference IDs as keys and the list items as values
420
-	 * @param array $ids List of referenced IDs
421
-	 */
422
-	protected function deleteItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids )
423
-	{
424
-		$listIds = array();
425
-
426
-		foreach( $ids as $id )
427
-		{
428
-			if( isset( $listItems[$id] ) ) {
429
-				$listIds[] = $listItems[$id]->getId();
430
-			}
431
-		}
432
-
433
-		$manager->deleteItems( $listIds );
434
-	}
435
-
436
-
437
-	/**
438
-	 * Updates the list items for the given reference IDs
439
-	 *
440
-	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
441
-	 * @param array $listItems Associative list of the reference IDs as keys and the list items as values
442
-	 * @param array $ids List of referenced IDs
443
-	 * @param array $config Configuration settins with "timeframe", "pricevalue", "price", "stock" and "currency"
444
-	 */
445
-	protected function editItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids, array $config )
446
-	{
447
-		foreach( $ids as $id )
448
-		{
449
-			if( isset( $listItems[$id] ) )
450
-			{
451
-				$item = $listItems[$id];
452
-				$time = time() + ( $config['timeframe'] + 1 ) * 86400;
453
-
454
-				$item->setDateEnd( date( 'Y-m-d 00:00:00', $time ) );
455
-				$item->setConfig( $config );
456
-
457
-				$manager->saveItem( $item );
458
-			}
459
-		}
460
-	}
461
-
462
-
463
-	/**
464
-	 * Returns the list items associated to the given user ID
465
-	 *
466
-	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
467
-	 * @param array $refIds IDs of the referenced items
468
-	 * @param string $typeId List type ID of the referenced items
469
-	 * @param string $userId Unique user ID
470
-	 * @return array Associative list of the reference IDs as keys and the list items as values
471
-	 */
472
-	protected function getListItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $refIds, $typeId, $userId )
473
-	{
474
-		$search = $manager->createSearch();
475
-		$expr = array(
476
-			$search->compare( '==', 'customer.lists.parentid', $userId ),
477
-			$search->compare( '==', 'customer.lists.refid', $refIds ),
478
-			$search->compare( '==', 'customer.lists.domain', 'product' ),
479
-			$search->compare( '==', 'customer.lists.typeid', $typeId ),
480
-		);
481
-		$search->setConditions( $search->combine( '&&', $expr ) );
482
-
483
-		$items = array();
484
-		foreach( $manager->searchItems( $search ) as $item ) {
485
-			$items[$item->getRefId()] = $item;
486
-		}
487
-
488
-		return $items;
489
-	}
490
-
491
-
492
-	/**
493
-	 * Returns the list of sub-client names configured for the client.
494
-	 *
495
-	 * @return array List of HTML client names
496
-	 */
497
-	protected function getSubClientNames()
498
-	{
499
-		return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
500
-	}
501
-
502
-
503
-	/**
504
-	 * Returns the sanitized page from the parameters for the product list.
505
-	 *
506
-	 * @param \Aimeos\MW\View\Iface $view View instance with helper for retrieving the required parameters
507
-	 * @return integer Page number starting from 1
508
-	 */
509
-	protected function getProductListPage( \Aimeos\MW\View\Iface $view )
510
-	{
511
-		$page = (int) $view->param( 'wat_page', 1 );
512
-		return ( $page < 1 ? 1 : $page );
513
-	}
514
-
515
-
516
-	/**
517
-	 * Returns the sanitized page size from the parameters for the product list.
518
-	 *
519
-	 * @param \Aimeos\MW\View\Iface $view View instance with helper for retrieving the required parameters
520
-	 * @return integer Page size
521
-	 */
522
-	protected function getProductListSize( \Aimeos\MW\View\Iface $view )
523
-	{
524
-		/** client/html/account/watch/size
525
-		 * The number of products shown in a list page for watch products
526
-		 *
527
-		 * Limits the number of products that is shown in the list pages to the
528
-		 * given value. If more products are available, the products are split
529
-		 * into bunches which will be shown on their own list page. The user is
530
-		 * able to move to the next page (or previous one if it's not the first)
531
-		 * to display the next (or previous) products.
532
-		 *
533
-		 * The value must be an integer number from 1 to 100. Negative values as
534
-		 * well as values above 100 are not allowed. The value can be overwritten
535
-		 * per request if the "l_size" parameter is part of the URL.
536
-		 *
537
-		 * @param integer Number of products
538
-		 * @since 2014.09
539
-		 * @category User
540
-		 * @category Developer
541
-		 * @see client/html/catalog/lists/size
542
-		 */
543
-		$defaultSize = $this->getContext()->getConfig()->get( 'client/html/account/watch/size', 48 );
544
-
545
-		$size = (int) $view->param( 'watch-size', $defaultSize );
546
-		return ( $size < 1 || $size > 100 ? $defaultSize : $size );
547
-	}
548
-
549
-
550
-	/**
551
-	 * Sets the necessary parameter values in the view.
552
-	 *
553
-	 * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
554
-	 * @param array &$tags Result array for the list of tags that are associated to the output
555
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
556
-	 * @return \Aimeos\MW\View\Iface Modified view object
557
-	 */
558
-	protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
559
-	{
560
-		if( !isset( $this->cache ) )
561
-		{
562
-			$total = 0;
563
-			$productIds = array();
564
-			$context = $this->getContext();
565
-			$typeItem = $this->getTypeItem( 'customer/lists/type', 'product', 'watch' );
566
-
567
-			$size = $this->getProductListSize( $view );
568
-			$current = $this->getProductListPage( $view );
569
-			$last = ( $total != 0 ? ceil( $total / $size ) : 1 );
570
-
571
-
572
-			$manager = \Aimeos\MShop\Factory::createManager( $context, 'customer/lists' );
573
-
574
-			$search = $manager->createSearch();
575
-			$expr = array(
576
-				$search->compare( '==', 'customer.lists.parentid', $context->getUserId() ),
577
-				$search->compare( '==', 'customer.lists.typeid', $typeItem->getId() ),
578
-				$search->compare( '==', 'customer.lists.domain', 'product' ),
579
-			);
580
-			$search->setConditions( $search->combine( '&&', $expr ) );
581
-			$search->setSortations( array( $search->sort( '-', 'customer.lists.position' ) ) );
582
-			$search->setSlice( ( $current - 1 ) * $size, $size );
583
-
584
-			$view->watchListItems = $manager->searchItems( $search, array(), $total );
585
-
586
-
587
-			/** client/html/account/watch/domains
588
-			 * A list of domain names whose items should be available in the account watch view template
589
-			 *
590
-			 * The templates rendering product details usually add the images,
591
-			 * prices and texts associated to the product item. If you want to
592
-			 * display additional or less content, you can configure your own
593
-			 * list of domains (attribute, media, price, product, text, etc. are
594
-			 * domains) whose items are fetched from the storage. Please keep
595
-			 * in mind that the more domains you add to the configuration, the
596
-			 * more time is required for fetching the content!
597
-			 *
598
-			 * @param array List of domain names
599
-			 * @since 2014.09
600
-			 * @category Developer
601
-			 * @see client/html/catalog/domains
602
-			 */
603
-			$default = array( 'text', 'price', 'media' );
604
-			$domains = $context->getConfig()->get( 'client/html/account/watch/domains', $default );
605
-
606
-			foreach( $view->watchListItems as $listItem ) {
607
-				$productIds[] = $listItem->getRefId();
608
-			}
609
-
610
-			$controller = \Aimeos\Controller\Frontend\Factory::createController( $context, 'catalog' );
611
-
612
-			$view->watchProductItems = $controller->getProductItems( $productIds, $domains );
613
-			$view->watchPageFirst = 1;
614
-			$view->watchPagePrev = ( $current > 1 ? $current - 1 : 1 );
615
-			$view->watchPageNext = ( $current < $last ? $current + 1 : $last );
616
-			$view->watchPageLast = $last;
617
-			$view->watchPageCurr = $current;
618
-
619
-			$this->cache = $view;
620
-		}
621
-
622
-		return $this->cache;
623
-	}
25
+    /** client/html/account/watch/standard/subparts
26
+     * List of HTML sub-clients rendered within the account watch 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/account/watch/standard/subparts';
59
+    private $subPartNames = array();
60
+    private $cache;
61
+
62
+
63
+    /**
64
+     * Returns the HTML code for insertion into the body.
65
+     *
66
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
67
+     * @param array &$tags Result array for the list of tags that are associated to the output
68
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
69
+     * @return string HTML code
70
+     */
71
+    public function getBody( $uid = '', array &$tags = array(), &$expire = null )
72
+    {
73
+        $context = $this->getContext();
74
+        $view = $this->getView();
75
+
76
+        try
77
+        {
78
+            $view = $this->setViewParams( $view, $tags, $expire );
79
+
80
+            $html = '';
81
+            foreach( $this->getSubClients() as $subclient ) {
82
+                $html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
83
+            }
84
+            $view->watchBody = $html;
85
+        }
86
+        catch( \Aimeos\Client\Html\Exception $e )
87
+        {
88
+            $error = array( $this->getContext()->getI18n()->dt( 'client', $e->getMessage() ) );
89
+            $view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
90
+        }
91
+        catch( \Aimeos\Controller\Frontend\Exception $e )
92
+        {
93
+            $error = array( $this->getContext()->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
94
+            $view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
95
+        }
96
+        catch( \Aimeos\MShop\Exception $e )
97
+        {
98
+            $error = array( $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ) );
99
+            $view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
100
+        }
101
+        catch( \Exception $e )
102
+        {
103
+            $context->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
104
+
105
+            $error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
106
+            $view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
107
+        }
108
+
109
+        /** client/html/account/watch/standard/template-body
110
+         * Relative path to the HTML body template of the account watch client.
111
+         *
112
+         * The template file contains the HTML code and processing instructions
113
+         * to generate the result shown in the body of the frontend. The
114
+         * configuration string is the path to the template file relative
115
+         * to the templates directory (usually in client/html/templates).
116
+         *
117
+         * You can overwrite the template file configuration in extensions and
118
+         * provide alternative templates. These alternative templates should be
119
+         * named like the default one but with the string "standard" replaced by
120
+         * an unique name. You may use the name of your project for this. If
121
+         * you've implemented an alternative client class as well, "standard"
122
+         * should be replaced by the name of the new class.
123
+         *
124
+         * @param string Relative path to the template creating code for the HTML page body
125
+         * @since 2014.03
126
+         * @category Developer
127
+         * @see client/html/account/watch/standard/template-header
128
+         */
129
+        $tplconf = 'client/html/account/watch/standard/template-body';
130
+        $default = 'account/watch/body-default.php';
131
+
132
+        return $view->render( $view->config( $tplconf, $default ) );
133
+    }
134
+
135
+
136
+    /**
137
+     * Returns the HTML string for insertion into the header.
138
+     *
139
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
140
+     * @param array &$tags Result array for the list of tags that are associated to the output
141
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
142
+     * @return string|null String including HTML tags for the header on error
143
+     */
144
+    public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
145
+    {
146
+        try
147
+        {
148
+            $view = $this->setViewParams( $this->getView(), $tags, $expire );
149
+
150
+            $html = '';
151
+            foreach( $this->getSubClients() as $subclient ) {
152
+                $html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
153
+            }
154
+            $view->watchHeader = $html;
155
+
156
+            /** client/html/account/watch/standard/template-header
157
+             * Relative path to the HTML header template of the account watch client.
158
+             *
159
+             * The template file contains the HTML code and processing instructions
160
+             * to generate the HTML code that is inserted into the HTML page header
161
+             * of the rendered page in the frontend. The configuration string is the
162
+             * path to the template file relative to the templates directory (usually
163
+             * in client/html/templates).
164
+             *
165
+             * You can overwrite the template file configuration in extensions and
166
+             * provide alternative templates. These alternative templates should be
167
+             * named like the default one but with the string "standard" replaced by
168
+             * an unique name. You may use the name of your project for this. If
169
+             * you've implemented an alternative client class as well, "standard"
170
+             * should be replaced by the name of the new class.
171
+             *
172
+             * @param string Relative path to the template creating code for the HTML page head
173
+             * @since 2014.03
174
+             * @category Developer
175
+             * @see client/html/account/watch/standard/template-body
176
+             */
177
+            $tplconf = 'client/html/account/watch/standard/template-header';
178
+            $default = 'account/watch/header-default.php';
179
+
180
+            return $view->render( $view->config( $tplconf, $default ) );
181
+        }
182
+        catch( \Exception $e )
183
+        {
184
+            $this->getContext()->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
185
+        }
186
+    }
187
+
188
+
189
+    /**
190
+     * Returns the sub-client given by its name.
191
+     *
192
+     * @param string $type Name of the client type
193
+     * @param string|null $name Name of the sub-client (Default if null)
194
+     * @return \Aimeos\Client\Html\Iface Sub-client object
195
+     */
196
+    public function getSubClient( $type, $name = null )
197
+    {
198
+        /** client/html/account/watch/decorators/excludes
199
+         * Excludes decorators added by the "common" option from the account watch html client
200
+         *
201
+         * Decorators extend the functionality of a class by adding new aspects
202
+         * (e.g. log what is currently done), executing the methods of the underlying
203
+         * class only in certain conditions (e.g. only for logged in users) or
204
+         * modify what is returned to the caller.
205
+         *
206
+         * This option allows you to remove a decorator added via
207
+         * "client/html/common/decorators/default" before they are wrapped
208
+         * around the html client.
209
+         *
210
+         *  client/html/account/watch/decorators/excludes = array( 'decorator1' )
211
+         *
212
+         * This would remove the decorator named "decorator1" from the list of
213
+         * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
214
+         * "client/html/common/decorators/default" to the html client.
215
+         *
216
+         * @param array List of decorator names
217
+         * @since 2014.05
218
+         * @category Developer
219
+         * @see client/html/common/decorators/default
220
+         * @see client/html/account/watch/decorators/global
221
+         * @see client/html/account/watch/decorators/local
222
+         */
223
+
224
+        /** client/html/account/watch/decorators/global
225
+         * Adds a list of globally available decorators only to the account watch html client
226
+         *
227
+         * Decorators extend the functionality of a class by adding new aspects
228
+         * (e.g. log what is currently done), executing the methods of the underlying
229
+         * class only in certain conditions (e.g. only for logged in users) or
230
+         * modify what is returned to the caller.
231
+         *
232
+         * This option allows you to wrap global decorators
233
+         * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
234
+         *
235
+         *  client/html/account/watch/decorators/global = array( 'decorator1' )
236
+         *
237
+         * This would add the decorator named "decorator1" defined by
238
+         * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
239
+         *
240
+         * @param array List of decorator names
241
+         * @since 2014.05
242
+         * @category Developer
243
+         * @see client/html/common/decorators/default
244
+         * @see client/html/account/watch/decorators/excludes
245
+         * @see client/html/account/watch/decorators/local
246
+         */
247
+
248
+        /** client/html/account/watch/decorators/local
249
+         * Adds a list of local decorators only to the account watch html client
250
+         *
251
+         * Decorators extend the functionality of a class by adding new aspects
252
+         * (e.g. log what is currently done), executing the methods of the underlying
253
+         * class only in certain conditions (e.g. only for logged in users) or
254
+         * modify what is returned to the caller.
255
+         *
256
+         * This option allows you to wrap local decorators
257
+         * ("\Aimeos\Client\Html\Account\Decorator\*") around the html client.
258
+         *
259
+         *  client/html/account/watch/decorators/local = array( 'decorator2' )
260
+         *
261
+         * This would add the decorator named "decorator2" defined by
262
+         * "\Aimeos\Client\Html\Account\Decorator\Decorator2" only to the html client.
263
+         *
264
+         * @param array List of decorator names
265
+         * @since 2014.05
266
+         * @category Developer
267
+         * @see client/html/common/decorators/default
268
+         * @see client/html/account/watch/decorators/excludes
269
+         * @see client/html/account/watch/decorators/global
270
+         */
271
+
272
+        return $this->createSubClient( 'account/watch/' . $type, $name );
273
+    }
274
+
275
+
276
+    /**
277
+     * Processes the input, e.g. store given values.
278
+     * A view must be available and this method doesn't generate any output
279
+     * besides setting view variables.
280
+     */
281
+    public function process()
282
+    {
283
+        $view = $this->getView();
284
+        $context = $this->getContext();
285
+        $userId = $context->getUserId();
286
+        $ids = (array) $view->param( 'wat_id', array() );
287
+
288
+        if( $userId != null && !empty( $ids ) )
289
+        {
290
+            $typeId = $this->getTypeItem( 'customer/lists/type', 'product', 'watch' )->getId();
291
+            $manager = \Aimeos\MShop\Factory::createManager( $context, 'customer/lists' );
292
+            $items = $this->getListItems( $manager, $ids, $typeId, $userId );
293
+
294
+            switch( $view->param( 'wat_action' ) )
295
+            {
296
+                case 'add':
297
+
298
+                    /** client/html/account/watch/standard/maxitems
299
+                     * Maximum number of products that can be watched in parallel
300
+                     *
301
+                     * This option limits the number of products that can be watched
302
+                     * after the users added the products to their watch list.
303
+                     * It must be a positive integer value greater than 0.
304
+                     *
305
+                     * Note: It's recommended to set this value not too high as this
306
+                     * leads to a high memory consumption when the e-mails are generated
307
+                     * to notify the customers. The memory used will up to 100*maxitems
308
+                     * of the footprint of one product item including the associated
309
+                     * texts, prices and media.
310
+                     *
311
+                     * @param integer Number of products
312
+                     * @since 2014.09
313
+                     * @category User
314
+                     * @category Developer
315
+                     */
316
+                    $max = $context->getConfig()->get( 'client/html/account/watch/standard/maxitems', 100 );
317
+                    $cnt = count( $ids );
318
+
319
+                    if( $this->checkLimit( $manager, $typeId, $userId, $max, $cnt ) === false )
320
+                    {
321
+                        $error = sprintf( $context->getI18n()->dt( 'client', 'You can only watch up to %1$s products' ), $max );
322
+                        $view->watchErrorList = $view->get( 'watchErrorList', array() ) + array( $error );
323
+                        break;
324
+                    }
325
+
326
+                    $this->addItems( $manager, $items, $ids, $typeId, $userId );
327
+                    break;
328
+
329
+                case 'edit':
330
+
331
+                    $config = array(
332
+                        'timeframe' => $view->param( 'wat_timeframe', 7 ),
333
+                        'pricevalue' => $view->param( 'wat_pricevalue', '0.00' ),
334
+                        'price' => $view->param( 'wat_price', 0 ),
335
+                        'stock' => $view->param( 'wat_stock', 0 ),
336
+                        'currency' => $context->getLocale()->getCurrencyId(),
337
+                    );
338
+                    $this->editItems( $manager, $items, $ids, $config );
339
+                    break;
340
+
341
+                case 'delete':
342
+
343
+                    $this->deleteItems( $manager, $items, $ids );
344
+                    break;
345
+            }
346
+        }
347
+
348
+        parent::process();
349
+    }
350
+
351
+
352
+    /**
353
+     * Tests if the maximum number of entries per user is already reached
354
+     *
355
+     * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
356
+     * @param string $typeId List type ID of the referenced items
357
+     * @param string $userId Unique user ID
358
+     * @param integer $max Maximum number of items that are allowed
359
+     * @param integer $cnt Number of items that should be added
360
+     * @return boolean True if items can be added, false if not
361
+     */
362
+    protected function checkLimit( \Aimeos\MShop\Common\Manager\Iface $manager, $typeId, $userId, $max, $cnt )
363
+    {
364
+        $search = $manager->createSearch();
365
+        $expr = array(
366
+            $search->compare( '==', 'customer.lists.parentid', $userId ),
367
+            $search->compare( '==', 'customer.lists.typeid', $typeId ),
368
+            $search->compare( '==', 'customer.lists.domain', 'product' ),
369
+        );
370
+        $search->setConditions( $search->combine( '&&', $expr ) );
371
+        $search->setSlice( 0, 0 );
372
+
373
+        $total = 0;
374
+        $manager->searchItems( $search, array(), $total );
375
+
376
+        if( $total + $cnt > $max ) {
377
+            return false;
378
+        }
379
+
380
+        return true;
381
+    }
382
+
383
+
384
+    /**
385
+     * Adds one or more list items to the given user
386
+     *
387
+     * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
388
+     * @param array $listItems Associative list of the reference IDs as keys and the list items as values
389
+     * @param array $ids List of referenced IDs
390
+     * @param string $typeId List type ID of the referenced items
391
+     * @param string $userId Unique user ID
392
+     */
393
+    protected function addItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids, $typeId, $userId )
394
+    {
395
+        $item = $manager->createItem();
396
+        $item->setParentId( $userId );
397
+        $item->setTypeId( $typeId );
398
+        $item->setDomain( 'product' );
399
+        $item->setStatus( 1 );
400
+
401
+        foreach( $ids as $id )
402
+        {
403
+            if( !isset( $listItems[$id] ) )
404
+            {
405
+                $item->setId( null );
406
+                $item->setRefId( $id );
407
+
408
+                $manager->saveItem( $item );
409
+                $manager->moveItem( $item->getId() );
410
+            }
411
+        }
412
+    }
413
+
414
+
415
+    /**
416
+     * Removes the list items for the given reference IDs
417
+     *
418
+     * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
419
+     * @param array $listItems Associative list of the reference IDs as keys and the list items as values
420
+     * @param array $ids List of referenced IDs
421
+     */
422
+    protected function deleteItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids )
423
+    {
424
+        $listIds = array();
425
+
426
+        foreach( $ids as $id )
427
+        {
428
+            if( isset( $listItems[$id] ) ) {
429
+                $listIds[] = $listItems[$id]->getId();
430
+            }
431
+        }
432
+
433
+        $manager->deleteItems( $listIds );
434
+    }
435
+
436
+
437
+    /**
438
+     * Updates the list items for the given reference IDs
439
+     *
440
+     * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
441
+     * @param array $listItems Associative list of the reference IDs as keys and the list items as values
442
+     * @param array $ids List of referenced IDs
443
+     * @param array $config Configuration settins with "timeframe", "pricevalue", "price", "stock" and "currency"
444
+     */
445
+    protected function editItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids, array $config )
446
+    {
447
+        foreach( $ids as $id )
448
+        {
449
+            if( isset( $listItems[$id] ) )
450
+            {
451
+                $item = $listItems[$id];
452
+                $time = time() + ( $config['timeframe'] + 1 ) * 86400;
453
+
454
+                $item->setDateEnd( date( 'Y-m-d 00:00:00', $time ) );
455
+                $item->setConfig( $config );
456
+
457
+                $manager->saveItem( $item );
458
+            }
459
+        }
460
+    }
461
+
462
+
463
+    /**
464
+     * Returns the list items associated to the given user ID
465
+     *
466
+     * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
467
+     * @param array $refIds IDs of the referenced items
468
+     * @param string $typeId List type ID of the referenced items
469
+     * @param string $userId Unique user ID
470
+     * @return array Associative list of the reference IDs as keys and the list items as values
471
+     */
472
+    protected function getListItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $refIds, $typeId, $userId )
473
+    {
474
+        $search = $manager->createSearch();
475
+        $expr = array(
476
+            $search->compare( '==', 'customer.lists.parentid', $userId ),
477
+            $search->compare( '==', 'customer.lists.refid', $refIds ),
478
+            $search->compare( '==', 'customer.lists.domain', 'product' ),
479
+            $search->compare( '==', 'customer.lists.typeid', $typeId ),
480
+        );
481
+        $search->setConditions( $search->combine( '&&', $expr ) );
482
+
483
+        $items = array();
484
+        foreach( $manager->searchItems( $search ) as $item ) {
485
+            $items[$item->getRefId()] = $item;
486
+        }
487
+
488
+        return $items;
489
+    }
490
+
491
+
492
+    /**
493
+     * Returns the list of sub-client names configured for the client.
494
+     *
495
+     * @return array List of HTML client names
496
+     */
497
+    protected function getSubClientNames()
498
+    {
499
+        return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
500
+    }
501
+
502
+
503
+    /**
504
+     * Returns the sanitized page from the parameters for the product list.
505
+     *
506
+     * @param \Aimeos\MW\View\Iface $view View instance with helper for retrieving the required parameters
507
+     * @return integer Page number starting from 1
508
+     */
509
+    protected function getProductListPage( \Aimeos\MW\View\Iface $view )
510
+    {
511
+        $page = (int) $view->param( 'wat_page', 1 );
512
+        return ( $page < 1 ? 1 : $page );
513
+    }
514
+
515
+
516
+    /**
517
+     * Returns the sanitized page size from the parameters for the product list.
518
+     *
519
+     * @param \Aimeos\MW\View\Iface $view View instance with helper for retrieving the required parameters
520
+     * @return integer Page size
521
+     */
522
+    protected function getProductListSize( \Aimeos\MW\View\Iface $view )
523
+    {
524
+        /** client/html/account/watch/size
525
+         * The number of products shown in a list page for watch products
526
+         *
527
+         * Limits the number of products that is shown in the list pages to the
528
+         * given value. If more products are available, the products are split
529
+         * into bunches which will be shown on their own list page. The user is
530
+         * able to move to the next page (or previous one if it's not the first)
531
+         * to display the next (or previous) products.
532
+         *
533
+         * The value must be an integer number from 1 to 100. Negative values as
534
+         * well as values above 100 are not allowed. The value can be overwritten
535
+         * per request if the "l_size" parameter is part of the URL.
536
+         *
537
+         * @param integer Number of products
538
+         * @since 2014.09
539
+         * @category User
540
+         * @category Developer
541
+         * @see client/html/catalog/lists/size
542
+         */
543
+        $defaultSize = $this->getContext()->getConfig()->get( 'client/html/account/watch/size', 48 );
544
+
545
+        $size = (int) $view->param( 'watch-size', $defaultSize );
546
+        return ( $size < 1 || $size > 100 ? $defaultSize : $size );
547
+    }
548
+
549
+
550
+    /**
551
+     * Sets the necessary parameter values in the view.
552
+     *
553
+     * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
554
+     * @param array &$tags Result array for the list of tags that are associated to the output
555
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
556
+     * @return \Aimeos\MW\View\Iface Modified view object
557
+     */
558
+    protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
559
+    {
560
+        if( !isset( $this->cache ) )
561
+        {
562
+            $total = 0;
563
+            $productIds = array();
564
+            $context = $this->getContext();
565
+            $typeItem = $this->getTypeItem( 'customer/lists/type', 'product', 'watch' );
566
+
567
+            $size = $this->getProductListSize( $view );
568
+            $current = $this->getProductListPage( $view );
569
+            $last = ( $total != 0 ? ceil( $total / $size ) : 1 );
570
+
571
+
572
+            $manager = \Aimeos\MShop\Factory::createManager( $context, 'customer/lists' );
573
+
574
+            $search = $manager->createSearch();
575
+            $expr = array(
576
+                $search->compare( '==', 'customer.lists.parentid', $context->getUserId() ),
577
+                $search->compare( '==', 'customer.lists.typeid', $typeItem->getId() ),
578
+                $search->compare( '==', 'customer.lists.domain', 'product' ),
579
+            );
580
+            $search->setConditions( $search->combine( '&&', $expr ) );
581
+            $search->setSortations( array( $search->sort( '-', 'customer.lists.position' ) ) );
582
+            $search->setSlice( ( $current - 1 ) * $size, $size );
583
+
584
+            $view->watchListItems = $manager->searchItems( $search, array(), $total );
585
+
586
+
587
+            /** client/html/account/watch/domains
588
+             * A list of domain names whose items should be available in the account watch view template
589
+             *
590
+             * The templates rendering product details usually add the images,
591
+             * prices and texts associated to the product item. If you want to
592
+             * display additional or less content, you can configure your own
593
+             * list of domains (attribute, media, price, product, text, etc. are
594
+             * domains) whose items are fetched from the storage. Please keep
595
+             * in mind that the more domains you add to the configuration, the
596
+             * more time is required for fetching the content!
597
+             *
598
+             * @param array List of domain names
599
+             * @since 2014.09
600
+             * @category Developer
601
+             * @see client/html/catalog/domains
602
+             */
603
+            $default = array( 'text', 'price', 'media' );
604
+            $domains = $context->getConfig()->get( 'client/html/account/watch/domains', $default );
605
+
606
+            foreach( $view->watchListItems as $listItem ) {
607
+                $productIds[] = $listItem->getRefId();
608
+            }
609
+
610
+            $controller = \Aimeos\Controller\Frontend\Factory::createController( $context, 'catalog' );
611
+
612
+            $view->watchProductItems = $controller->getProductItems( $productIds, $domains );
613
+            $view->watchPageFirst = 1;
614
+            $view->watchPagePrev = ( $current > 1 ? $current - 1 : 1 );
615
+            $view->watchPageNext = ( $current < $last ? $current + 1 : $last );
616
+            $view->watchPageLast = $last;
617
+            $view->watchPageCurr = $current;
618
+
619
+            $this->cache = $view;
620
+        }
621
+
622
+        return $this->cache;
623
+    }
624 624
 }
625 625
\ No newline at end of file
Please login to merge, or discard this patch.
Spacing   +111 added lines, -111 removed lines patch added patch discarded remove patch
@@ -68,42 +68,42 @@  discard block
 block discarded – undo
68 68
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
69 69
 	 * @return string HTML code
70 70
 	 */
71
-	public function getBody( $uid = '', array &$tags = array(), &$expire = null )
71
+	public function getBody($uid = '', array &$tags = array(), &$expire = null)
72 72
 	{
73 73
 		$context = $this->getContext();
74 74
 		$view = $this->getView();
75 75
 
76 76
 		try
77 77
 		{
78
-			$view = $this->setViewParams( $view, $tags, $expire );
78
+			$view = $this->setViewParams($view, $tags, $expire);
79 79
 
80 80
 			$html = '';
81
-			foreach( $this->getSubClients() as $subclient ) {
82
-				$html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
81
+			foreach ($this->getSubClients() as $subclient) {
82
+				$html .= $subclient->setView($view)->getBody($uid, $tags, $expire);
83 83
 			}
84 84
 			$view->watchBody = $html;
85 85
 		}
86
-		catch( \Aimeos\Client\Html\Exception $e )
86
+		catch (\Aimeos\Client\Html\Exception $e)
87 87
 		{
88
-			$error = array( $this->getContext()->getI18n()->dt( 'client', $e->getMessage() ) );
89
-			$view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
88
+			$error = array($this->getContext()->getI18n()->dt('client', $e->getMessage()));
89
+			$view->watchErrorList = $view->get('watchErrorList', array()) + $error;
90 90
 		}
91
-		catch( \Aimeos\Controller\Frontend\Exception $e )
91
+		catch (\Aimeos\Controller\Frontend\Exception $e)
92 92
 		{
93
-			$error = array( $this->getContext()->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
94
-			$view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
93
+			$error = array($this->getContext()->getI18n()->dt('controller/frontend', $e->getMessage()));
94
+			$view->watchErrorList = $view->get('watchErrorList', array()) + $error;
95 95
 		}
96
-		catch( \Aimeos\MShop\Exception $e )
96
+		catch (\Aimeos\MShop\Exception $e)
97 97
 		{
98
-			$error = array( $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ) );
99
-			$view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
98
+			$error = array($this->getContext()->getI18n()->dt('mshop', $e->getMessage()));
99
+			$view->watchErrorList = $view->get('watchErrorList', array()) + $error;
100 100
 		}
101
-		catch( \Exception $e )
101
+		catch (\Exception $e)
102 102
 		{
103
-			$context->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
103
+			$context->getLogger()->log($e->getMessage().PHP_EOL.$e->getTraceAsString());
104 104
 
105
-			$error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
106
-			$view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
105
+			$error = array($context->getI18n()->dt('client', 'A non-recoverable error occured'));
106
+			$view->watchErrorList = $view->get('watchErrorList', array()) + $error;
107 107
 		}
108 108
 
109 109
 		/** client/html/account/watch/standard/template-body
@@ -129,7 +129,7 @@  discard block
 block discarded – undo
129 129
 		$tplconf = 'client/html/account/watch/standard/template-body';
130 130
 		$default = 'account/watch/body-default.php';
131 131
 
132
-		return $view->render( $view->config( $tplconf, $default ) );
132
+		return $view->render($view->config($tplconf, $default));
133 133
 	}
134 134
 
135 135
 
@@ -141,15 +141,15 @@  discard block
 block discarded – undo
141 141
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
142 142
 	 * @return string|null String including HTML tags for the header on error
143 143
 	 */
144
-	public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
144
+	public function getHeader($uid = '', array &$tags = array(), &$expire = null)
145 145
 	{
146 146
 		try
147 147
 		{
148
-			$view = $this->setViewParams( $this->getView(), $tags, $expire );
148
+			$view = $this->setViewParams($this->getView(), $tags, $expire);
149 149
 
150 150
 			$html = '';
151
-			foreach( $this->getSubClients() as $subclient ) {
152
-				$html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
151
+			foreach ($this->getSubClients() as $subclient) {
152
+				$html .= $subclient->setView($view)->getHeader($uid, $tags, $expire);
153 153
 			}
154 154
 			$view->watchHeader = $html;
155 155
 
@@ -177,11 +177,11 @@  discard block
 block discarded – undo
177 177
 			$tplconf = 'client/html/account/watch/standard/template-header';
178 178
 			$default = 'account/watch/header-default.php';
179 179
 
180
-			return $view->render( $view->config( $tplconf, $default ) );
180
+			return $view->render($view->config($tplconf, $default));
181 181
 		}
182
-		catch( \Exception $e )
182
+		catch (\Exception $e)
183 183
 		{
184
-			$this->getContext()->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
184
+			$this->getContext()->getLogger()->log($e->getMessage().PHP_EOL.$e->getTraceAsString());
185 185
 		}
186 186
 	}
187 187
 
@@ -193,7 +193,7 @@  discard block
 block discarded – undo
193 193
 	 * @param string|null $name Name of the sub-client (Default if null)
194 194
 	 * @return \Aimeos\Client\Html\Iface Sub-client object
195 195
 	 */
196
-	public function getSubClient( $type, $name = null )
196
+	public function getSubClient($type, $name = null)
197 197
 	{
198 198
 		/** client/html/account/watch/decorators/excludes
199 199
 		 * Excludes decorators added by the "common" option from the account watch html client
@@ -269,7 +269,7 @@  discard block
 block discarded – undo
269 269
 		 * @see client/html/account/watch/decorators/global
270 270
 		 */
271 271
 
272
-		return $this->createSubClient( 'account/watch/' . $type, $name );
272
+		return $this->createSubClient('account/watch/'.$type, $name);
273 273
 	}
274 274
 
275 275
 
@@ -283,15 +283,15 @@  discard block
 block discarded – undo
283 283
 		$view = $this->getView();
284 284
 		$context = $this->getContext();
285 285
 		$userId = $context->getUserId();
286
-		$ids = (array) $view->param( 'wat_id', array() );
286
+		$ids = (array) $view->param('wat_id', array());
287 287
 
288
-		if( $userId != null && !empty( $ids ) )
288
+		if ($userId != null && !empty($ids))
289 289
 		{
290
-			$typeId = $this->getTypeItem( 'customer/lists/type', 'product', 'watch' )->getId();
291
-			$manager = \Aimeos\MShop\Factory::createManager( $context, 'customer/lists' );
292
-			$items = $this->getListItems( $manager, $ids, $typeId, $userId );
290
+			$typeId = $this->getTypeItem('customer/lists/type', 'product', 'watch')->getId();
291
+			$manager = \Aimeos\MShop\Factory::createManager($context, 'customer/lists');
292
+			$items = $this->getListItems($manager, $ids, $typeId, $userId);
293 293
 
294
-			switch( $view->param( 'wat_action' ) )
294
+			switch ($view->param('wat_action'))
295 295
 			{
296 296
 				case 'add':
297 297
 
@@ -313,34 +313,34 @@  discard block
 block discarded – undo
313 313
 					 * @category User
314 314
 					 * @category Developer
315 315
 					 */
316
-					$max = $context->getConfig()->get( 'client/html/account/watch/standard/maxitems', 100 );
317
-					$cnt = count( $ids );
316
+					$max = $context->getConfig()->get('client/html/account/watch/standard/maxitems', 100);
317
+					$cnt = count($ids);
318 318
 
319
-					if( $this->checkLimit( $manager, $typeId, $userId, $max, $cnt ) === false )
319
+					if ($this->checkLimit($manager, $typeId, $userId, $max, $cnt) === false)
320 320
 					{
321
-						$error = sprintf( $context->getI18n()->dt( 'client', 'You can only watch up to %1$s products' ), $max );
322
-						$view->watchErrorList = $view->get( 'watchErrorList', array() ) + array( $error );
321
+						$error = sprintf($context->getI18n()->dt('client', 'You can only watch up to %1$s products'), $max);
322
+						$view->watchErrorList = $view->get('watchErrorList', array()) + array($error);
323 323
 						break;
324 324
 					}
325 325
 
326
-					$this->addItems( $manager, $items, $ids, $typeId, $userId );
326
+					$this->addItems($manager, $items, $ids, $typeId, $userId);
327 327
 					break;
328 328
 
329 329
 				case 'edit':
330 330
 
331 331
 					$config = array(
332
-						'timeframe' => $view->param( 'wat_timeframe', 7 ),
333
-						'pricevalue' => $view->param( 'wat_pricevalue', '0.00' ),
334
-						'price' => $view->param( 'wat_price', 0 ),
335
-						'stock' => $view->param( 'wat_stock', 0 ),
332
+						'timeframe' => $view->param('wat_timeframe', 7),
333
+						'pricevalue' => $view->param('wat_pricevalue', '0.00'),
334
+						'price' => $view->param('wat_price', 0),
335
+						'stock' => $view->param('wat_stock', 0),
336 336
 						'currency' => $context->getLocale()->getCurrencyId(),
337 337
 					);
338
-					$this->editItems( $manager, $items, $ids, $config );
338
+					$this->editItems($manager, $items, $ids, $config);
339 339
 					break;
340 340
 
341 341
 				case 'delete':
342 342
 
343
-					$this->deleteItems( $manager, $items, $ids );
343
+					$this->deleteItems($manager, $items, $ids);
344 344
 					break;
345 345
 			}
346 346
 		}
@@ -359,21 +359,21 @@  discard block
 block discarded – undo
359 359
 	 * @param integer $cnt Number of items that should be added
360 360
 	 * @return boolean True if items can be added, false if not
361 361
 	 */
362
-	protected function checkLimit( \Aimeos\MShop\Common\Manager\Iface $manager, $typeId, $userId, $max, $cnt )
362
+	protected function checkLimit(\Aimeos\MShop\Common\Manager\Iface $manager, $typeId, $userId, $max, $cnt)
363 363
 	{
364 364
 		$search = $manager->createSearch();
365 365
 		$expr = array(
366
-			$search->compare( '==', 'customer.lists.parentid', $userId ),
367
-			$search->compare( '==', 'customer.lists.typeid', $typeId ),
368
-			$search->compare( '==', 'customer.lists.domain', 'product' ),
366
+			$search->compare('==', 'customer.lists.parentid', $userId),
367
+			$search->compare('==', 'customer.lists.typeid', $typeId),
368
+			$search->compare('==', 'customer.lists.domain', 'product'),
369 369
 		);
370
-		$search->setConditions( $search->combine( '&&', $expr ) );
371
-		$search->setSlice( 0, 0 );
370
+		$search->setConditions($search->combine('&&', $expr));
371
+		$search->setSlice(0, 0);
372 372
 
373 373
 		$total = 0;
374
-		$manager->searchItems( $search, array(), $total );
374
+		$manager->searchItems($search, array(), $total);
375 375
 
376
-		if( $total + $cnt > $max ) {
376
+		if ($total + $cnt > $max) {
377 377
 			return false;
378 378
 		}
379 379
 
@@ -390,23 +390,23 @@  discard block
 block discarded – undo
390 390
 	 * @param string $typeId List type ID of the referenced items
391 391
 	 * @param string $userId Unique user ID
392 392
 	 */
393
-	protected function addItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids, $typeId, $userId )
393
+	protected function addItems(\Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids, $typeId, $userId)
394 394
 	{
395 395
 		$item = $manager->createItem();
396
-		$item->setParentId( $userId );
397
-		$item->setTypeId( $typeId );
398
-		$item->setDomain( 'product' );
399
-		$item->setStatus( 1 );
396
+		$item->setParentId($userId);
397
+		$item->setTypeId($typeId);
398
+		$item->setDomain('product');
399
+		$item->setStatus(1);
400 400
 
401
-		foreach( $ids as $id )
401
+		foreach ($ids as $id)
402 402
 		{
403
-			if( !isset( $listItems[$id] ) )
403
+			if (!isset($listItems[$id]))
404 404
 			{
405
-				$item->setId( null );
406
-				$item->setRefId( $id );
405
+				$item->setId(null);
406
+				$item->setRefId($id);
407 407
 
408
-				$manager->saveItem( $item );
409
-				$manager->moveItem( $item->getId() );
408
+				$manager->saveItem($item);
409
+				$manager->moveItem($item->getId());
410 410
 			}
411 411
 		}
412 412
 	}
@@ -419,18 +419,18 @@  discard block
 block discarded – undo
419 419
 	 * @param array $listItems Associative list of the reference IDs as keys and the list items as values
420 420
 	 * @param array $ids List of referenced IDs
421 421
 	 */
422
-	protected function deleteItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids )
422
+	protected function deleteItems(\Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids)
423 423
 	{
424 424
 		$listIds = array();
425 425
 
426
-		foreach( $ids as $id )
426
+		foreach ($ids as $id)
427 427
 		{
428
-			if( isset( $listItems[$id] ) ) {
428
+			if (isset($listItems[$id])) {
429 429
 				$listIds[] = $listItems[$id]->getId();
430 430
 			}
431 431
 		}
432 432
 
433
-		$manager->deleteItems( $listIds );
433
+		$manager->deleteItems($listIds);
434 434
 	}
435 435
 
436 436
 
@@ -442,19 +442,19 @@  discard block
 block discarded – undo
442 442
 	 * @param array $ids List of referenced IDs
443 443
 	 * @param array $config Configuration settins with "timeframe", "pricevalue", "price", "stock" and "currency"
444 444
 	 */
445
-	protected function editItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids, array $config )
445
+	protected function editItems(\Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids, array $config)
446 446
 	{
447
-		foreach( $ids as $id )
447
+		foreach ($ids as $id)
448 448
 		{
449
-			if( isset( $listItems[$id] ) )
449
+			if (isset($listItems[$id]))
450 450
 			{
451 451
 				$item = $listItems[$id];
452
-				$time = time() + ( $config['timeframe'] + 1 ) * 86400;
452
+				$time = time() + ($config['timeframe'] + 1) * 86400;
453 453
 
454
-				$item->setDateEnd( date( 'Y-m-d 00:00:00', $time ) );
455
-				$item->setConfig( $config );
454
+				$item->setDateEnd(date('Y-m-d 00:00:00', $time));
455
+				$item->setConfig($config);
456 456
 
457
-				$manager->saveItem( $item );
457
+				$manager->saveItem($item);
458 458
 			}
459 459
 		}
460 460
 	}
@@ -469,19 +469,19 @@  discard block
 block discarded – undo
469 469
 	 * @param string $userId Unique user ID
470 470
 	 * @return array Associative list of the reference IDs as keys and the list items as values
471 471
 	 */
472
-	protected function getListItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $refIds, $typeId, $userId )
472
+	protected function getListItems(\Aimeos\MShop\Common\Manager\Iface $manager, array $refIds, $typeId, $userId)
473 473
 	{
474 474
 		$search = $manager->createSearch();
475 475
 		$expr = array(
476
-			$search->compare( '==', 'customer.lists.parentid', $userId ),
477
-			$search->compare( '==', 'customer.lists.refid', $refIds ),
478
-			$search->compare( '==', 'customer.lists.domain', 'product' ),
479
-			$search->compare( '==', 'customer.lists.typeid', $typeId ),
476
+			$search->compare('==', 'customer.lists.parentid', $userId),
477
+			$search->compare('==', 'customer.lists.refid', $refIds),
478
+			$search->compare('==', 'customer.lists.domain', 'product'),
479
+			$search->compare('==', 'customer.lists.typeid', $typeId),
480 480
 		);
481
-		$search->setConditions( $search->combine( '&&', $expr ) );
481
+		$search->setConditions($search->combine('&&', $expr));
482 482
 
483 483
 		$items = array();
484
-		foreach( $manager->searchItems( $search ) as $item ) {
484
+		foreach ($manager->searchItems($search) as $item) {
485 485
 			$items[$item->getRefId()] = $item;
486 486
 		}
487 487
 
@@ -496,7 +496,7 @@  discard block
 block discarded – undo
496 496
 	 */
497 497
 	protected function getSubClientNames()
498 498
 	{
499
-		return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
499
+		return $this->getContext()->getConfig()->get($this->subPartPath, $this->subPartNames);
500 500
 	}
501 501
 
502 502
 
@@ -506,10 +506,10 @@  discard block
 block discarded – undo
506 506
 	 * @param \Aimeos\MW\View\Iface $view View instance with helper for retrieving the required parameters
507 507
 	 * @return integer Page number starting from 1
508 508
 	 */
509
-	protected function getProductListPage( \Aimeos\MW\View\Iface $view )
509
+	protected function getProductListPage(\Aimeos\MW\View\Iface $view)
510 510
 	{
511
-		$page = (int) $view->param( 'wat_page', 1 );
512
-		return ( $page < 1 ? 1 : $page );
511
+		$page = (int) $view->param('wat_page', 1);
512
+		return ($page < 1 ? 1 : $page);
513 513
 	}
514 514
 
515 515
 
@@ -519,7 +519,7 @@  discard block
 block discarded – undo
519 519
 	 * @param \Aimeos\MW\View\Iface $view View instance with helper for retrieving the required parameters
520 520
 	 * @return integer Page size
521 521
 	 */
522
-	protected function getProductListSize( \Aimeos\MW\View\Iface $view )
522
+	protected function getProductListSize(\Aimeos\MW\View\Iface $view)
523 523
 	{
524 524
 		/** client/html/account/watch/size
525 525
 		 * The number of products shown in a list page for watch products
@@ -540,10 +540,10 @@  discard block
 block discarded – undo
540 540
 		 * @category Developer
541 541
 		 * @see client/html/catalog/lists/size
542 542
 		 */
543
-		$defaultSize = $this->getContext()->getConfig()->get( 'client/html/account/watch/size', 48 );
543
+		$defaultSize = $this->getContext()->getConfig()->get('client/html/account/watch/size', 48);
544 544
 
545
-		$size = (int) $view->param( 'watch-size', $defaultSize );
546
-		return ( $size < 1 || $size > 100 ? $defaultSize : $size );
545
+		$size = (int) $view->param('watch-size', $defaultSize);
546
+		return ($size < 1 || $size > 100 ? $defaultSize : $size);
547 547
 	}
548 548
 
549 549
 
@@ -555,33 +555,33 @@  discard block
 block discarded – undo
555 555
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
556 556
 	 * @return \Aimeos\MW\View\Iface Modified view object
557 557
 	 */
558
-	protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
558
+	protected function setViewParams(\Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null)
559 559
 	{
560
-		if( !isset( $this->cache ) )
560
+		if (!isset($this->cache))
561 561
 		{
562 562
 			$total = 0;
563 563
 			$productIds = array();
564 564
 			$context = $this->getContext();
565
-			$typeItem = $this->getTypeItem( 'customer/lists/type', 'product', 'watch' );
565
+			$typeItem = $this->getTypeItem('customer/lists/type', 'product', 'watch');
566 566
 
567
-			$size = $this->getProductListSize( $view );
568
-			$current = $this->getProductListPage( $view );
569
-			$last = ( $total != 0 ? ceil( $total / $size ) : 1 );
567
+			$size = $this->getProductListSize($view);
568
+			$current = $this->getProductListPage($view);
569
+			$last = ($total != 0 ? ceil($total / $size) : 1);
570 570
 
571 571
 
572
-			$manager = \Aimeos\MShop\Factory::createManager( $context, 'customer/lists' );
572
+			$manager = \Aimeos\MShop\Factory::createManager($context, 'customer/lists');
573 573
 
574 574
 			$search = $manager->createSearch();
575 575
 			$expr = array(
576
-				$search->compare( '==', 'customer.lists.parentid', $context->getUserId() ),
577
-				$search->compare( '==', 'customer.lists.typeid', $typeItem->getId() ),
578
-				$search->compare( '==', 'customer.lists.domain', 'product' ),
576
+				$search->compare('==', 'customer.lists.parentid', $context->getUserId()),
577
+				$search->compare('==', 'customer.lists.typeid', $typeItem->getId()),
578
+				$search->compare('==', 'customer.lists.domain', 'product'),
579 579
 			);
580
-			$search->setConditions( $search->combine( '&&', $expr ) );
581
-			$search->setSortations( array( $search->sort( '-', 'customer.lists.position' ) ) );
582
-			$search->setSlice( ( $current - 1 ) * $size, $size );
580
+			$search->setConditions($search->combine('&&', $expr));
581
+			$search->setSortations(array($search->sort('-', 'customer.lists.position')));
582
+			$search->setSlice(($current - 1) * $size, $size);
583 583
 
584
-			$view->watchListItems = $manager->searchItems( $search, array(), $total );
584
+			$view->watchListItems = $manager->searchItems($search, array(), $total);
585 585
 
586 586
 
587 587
 			/** client/html/account/watch/domains
@@ -600,19 +600,19 @@  discard block
 block discarded – undo
600 600
 			 * @category Developer
601 601
 			 * @see client/html/catalog/domains
602 602
 			 */
603
-			$default = array( 'text', 'price', 'media' );
604
-			$domains = $context->getConfig()->get( 'client/html/account/watch/domains', $default );
603
+			$default = array('text', 'price', 'media');
604
+			$domains = $context->getConfig()->get('client/html/account/watch/domains', $default);
605 605
 
606
-			foreach( $view->watchListItems as $listItem ) {
606
+			foreach ($view->watchListItems as $listItem) {
607 607
 				$productIds[] = $listItem->getRefId();
608 608
 			}
609 609
 
610
-			$controller = \Aimeos\Controller\Frontend\Factory::createController( $context, 'catalog' );
610
+			$controller = \Aimeos\Controller\Frontend\Factory::createController($context, 'catalog');
611 611
 
612
-			$view->watchProductItems = $controller->getProductItems( $productIds, $domains );
612
+			$view->watchProductItems = $controller->getProductItems($productIds, $domains);
613 613
 			$view->watchPageFirst = 1;
614
-			$view->watchPagePrev = ( $current > 1 ? $current - 1 : 1 );
615
-			$view->watchPageNext = ( $current < $last ? $current + 1 : $last );
614
+			$view->watchPagePrev = ($current > 1 ? $current - 1 : 1);
615
+			$view->watchPageNext = ($current < $last ? $current + 1 : $last);
616 616
 			$view->watchPageLast = $last;
617 617
 			$view->watchPageCurr = $current;
618 618
 
Please login to merge, or discard this patch.
client/html/src/Client/Html/Base.php 2 patches
Indentation   +674 added lines, -674 removed lines patch added patch discarded remove patch
@@ -19,679 +19,679 @@
 block discarded – undo
19 19
  * @subpackage Html
20 20
  */
21 21
 abstract class Base
22
-	implements \Aimeos\Client\Html\Iface
22
+    implements \Aimeos\Client\Html\Iface
23 23
 {
24
-	private $view;
25
-	private $cache;
26
-	private $context;
27
-	private $subclients;
28
-	private $templatePaths;
29
-
30
-
31
-	/**
32
-	 * Initializes the class instance.
33
-	 *
34
-	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object
35
-	 * @param array $templatePaths Associative list of the file system paths to the core or the extensions as key
36
-	 * 	and a list of relative paths inside the core or the extension as values
37
-	 */
38
-	public function __construct( \Aimeos\MShop\Context\Item\Iface $context, array $templatePaths )
39
-	{
40
-		$this->context = $context;
41
-		$this->templatePaths = $templatePaths;
42
-	}
43
-
44
-
45
-	/**
46
-	 * Returns the view object that will generate the HTML output.
47
-	 *
48
-	 * @return \Aimeos\MW\View\Iface $view The view object which generates the HTML output
49
-	 */
50
-	public function getView()
51
-	{
52
-		if( !isset( $this->view ) ) {
53
-			throw new \Aimeos\Client\Html\Exception( sprintf( 'No view available' ) );
54
-		}
55
-
56
-		return $this->view;
57
-	}
58
-
59
-
60
-	/**
61
-	 * Modifies the cached body content to replace content based on sessions or cookies.
62
-	 *
63
-	 * @param string $content Cached content
64
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
65
-	 * @return string Modified body content
66
-	 */
67
-	public function modifyBody( $content, $uid )
68
-	{
69
-		$view = $this->getView();
70
-
71
-		foreach( $this->getSubClients() as $subclient )
72
-		{
73
-			$subclient->setView( $view );
74
-			$content = $subclient->modifyBody( $content, $uid );
75
-		}
76
-
77
-		return $content;
78
-	}
79
-
80
-
81
-	/**
82
-	 * Modifies the cached header content to replace content based on sessions or cookies.
83
-	 *
84
-	 * @param string $content Cached content
85
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
86
-	 * @return string Modified header content
87
-	 */
88
-	public function modifyHeader( $content, $uid )
89
-	{
90
-		$view = $this->getView();
91
-
92
-		foreach( $this->getSubClients() as $subclient )
93
-		{
94
-			$subclient->setView( $view );
95
-			$content = $subclient->modifyHeader( $content, $uid );
96
-		}
97
-
98
-		return $content;
99
-	}
100
-
101
-
102
-	/**
103
-	 * Processes the input, e.g. store given values.
104
-	 * A view must be available and this method doesn't generate any output
105
-	 * besides setting view variables.
106
-	 *
107
-	 * @return boolean False if processing is stopped, otherwise all processing was completed successfully
108
-	 */
109
-	public function process()
110
-	{
111
-		$view = $this->getView();
112
-
113
-		foreach( $this->getSubClients() as $subclient )
114
-		{
115
-			$subclient->setView( $view );
116
-
117
-			if( $subclient->process() === false ) {
118
-				return false;
119
-			}
120
-		}
121
-
122
-		return true;
123
-	}
124
-
125
-
126
-	/**
127
-	 * Sets the view object that will generate the HTML output.
128
-	 *
129
-	 * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
130
-	 * @return \Aimeos\Client\Html\Iface Reference to this object for fluent calls
131
-	 */
132
-	public function setView( \Aimeos\MW\View\Iface $view )
133
-	{
134
-		$this->view = $view;
135
-		return $this;
136
-	}
137
-
138
-
139
-	/**
140
-	 * Adds the decorators to the client object
141
-	 *
142
-	 * @param \Aimeos\Client\Html\Iface $client Client object
143
-	 * @param array $templatePaths List of file system paths where the templates are stored
144
-	 * @param array $decorators List of decorator name that should be wrapped around the client
145
-	 * @param string $classprefix Decorator class prefix, e.g. "\Aimeos\Client\Html\Catalog\Decorator\"
146
-	 * @return \Aimeos\Client\Html\Iface Client object
147
-	 */
148
-	protected function addDecorators( \Aimeos\Client\Html\Iface $client, array $templatePaths,
149
-		array $decorators, $classprefix )
150
-	{
151
-		$iface = '\\Aimeos\\Client\\Html\\Common\\Decorator\\Iface';
152
-
153
-		foreach( $decorators as $name )
154
-		{
155
-			if( ctype_alnum( $name ) === false )
156
-			{
157
-				$classname = is_string( $name ) ? $classprefix . $name : '<not a string>';
158
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'Invalid class name "%1$s"', $classname ) );
159
-			}
160
-
161
-			$classname = $classprefix . $name;
162
-
163
-			if( class_exists( $classname ) === false ) {
164
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'Class "%1$s" not found', $classname ) );
165
-			}
166
-
167
-			$client = new $classname( $client, $this->context, $this->templatePaths );
168
-
169
-			if( !( $client instanceof $iface ) ) {
170
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'Class "%1$s" does not implement "%2$s"', $classname, $iface ) );
171
-			}
172
-		}
173
-
174
-		return $client;
175
-	}
176
-
177
-
178
-	/**
179
-	 * Adds the decorators to the client object
180
-	 *
181
-	 * @param \Aimeos\Client\Html\Iface $client Client object
182
-	 * @param array $templatePaths List of file system paths where the templates are stored
183
-	 * @param string $path Client string in lower case, e.g. "catalog/detail/basic"
184
-	 * @return \Aimeos\Client\Html\Iface Client object
185
-	 */
186
-	protected function addClientDecorators( \Aimeos\Client\Html\Iface $client, array $templatePaths, $path )
187
-	{
188
-		if( !is_string( $path ) || $path === '' ) {
189
-			throw new \Aimeos\Client\Html\Exception( sprintf( 'Invalid domain "%1$s"', $path ) );
190
-		}
191
-
192
-		$localClass = str_replace( ' ', '\\', ucwords( str_replace( '/', ' ', $path ) ) );
193
-		$config = $this->context->getConfig();
194
-
195
-		$decorators = $config->get( 'client/html/common/decorators/default', array() );
196
-		$excludes = $config->get( 'client/html/' . $path . '/decorators/excludes', array() );
197
-
198
-		foreach( $decorators as $key => $name )
199
-		{
200
-			if( in_array( $name, $excludes ) ) {
201
-				unset( $decorators[$key] );
202
-			}
203
-		}
204
-
205
-		$classprefix = '\\Aimeos\\Client\\Html\\Common\\Decorator\\';
206
-		$client = $this->addDecorators( $client, $templatePaths, $decorators, $classprefix );
207
-
208
-		$classprefix = '\\Aimeos\\Client\\Html\\Common\\Decorator\\';
209
-		$decorators = $config->get( 'client/html/' . $path . '/decorators/global', array() );
210
-		$client = $this->addDecorators( $client, $templatePaths, $decorators, $classprefix );
211
-
212
-		$classprefix = '\\Aimeos\\Client\\Html\\' . $localClass . '\\Decorator\\';
213
-		$decorators = $config->get( 'client/html/' . $path . '/decorators/local', array() );
214
-		$client = $this->addDecorators( $client, $templatePaths, $decorators, $classprefix );
215
-
216
-		return $client;
217
-	}
218
-
219
-
220
-	/**
221
-	 * Adds the cache tags to the given list and sets a new expiration date if necessary based on the given item.
222
-	 *
223
-	 * @param array|\Aimeos\MShop\Common\Item\Iface $items Item or list of items, maybe with associated list items
224
-	 * @param string $domain Name of the domain the item is from
225
-	 * @param string|null &$expire Expiration date that will be overwritten if an earlier date is found
226
-	 * @param array &$tags List of tags the new tags will be added to
227
-	 */
228
-	protected function addMetaItem( $items, $domain, &$expire, array &$tags )
229
-	{
230
-		/** client/html/common/cache/tag-all
231
-		 * Adds tags for all items used in a cache entry
232
-		 *
233
-		 * Each cache entry storing rendered parts for the HTML header or body
234
-		 * can be tagged with information which items like texts, media, etc.
235
-		 * are used in the HTML. This allows removing only those cache entries
236
-		 * whose content has really changed and only that entries have to be
237
-		 * rebuild the next time.
238
-		 *
239
-		 * The standard behavior stores only tags for each used domain, e.g. if
240
-		 * a text is used, only the tag "text" is added. If you change a text
241
-		 * in the administration interface, all cache entries with the tag
242
-		 * "text" will be removed from the cache. This effectively wipes out
243
-		 * almost all cached entries, which have to be rebuild with the next
244
-		 * request.
245
-		 *
246
-		 * Important: As a list or detail view can use several hundred items,
247
-		 * this configuration option will also add this number of tags to the
248
-		 * cache entry. When using a cache adapter that can't insert all tags
249
-		 * at once, this slows down the initial cache insert (and therefore the
250
-		 * page speed) drastically! It's only recommended to enable this option
251
-		 * if you use the DB, Mysql or Redis adapter that can insert all tags
252
-		 * at once.
253
-		 *
254
-		 * @param boolean True to add tags for all items, false to use only a domain tag
255
-		 * @since 2014.07
256
-		 * @category Developer
257
-		 * @category User
258
-		 * @see client/html/common/cache/force
259
-		 * @see madmin/cache/manager/name
260
-		 * @see madmin/cache/name
261
-		 */
262
-		$tagAll = $this->context->getConfig()->get( 'client/html/common/cache/tag-all', false );
263
-
264
-		if( !is_array( $items ) ) {
265
-			$items = array( $items );
266
-		}
267
-
268
-		if( $tagAll !== true && !empty( $items ) ) {
269
-			$tags[] = $domain;
270
-		}
271
-
272
-		foreach( $items as $item ) {
273
-			$this->addMetaItemSingle( $item, $domain, $expire, $tags, $tagAll );
274
-		}
275
-	}
276
-
277
-
278
-	/**
279
-	 * Adds expire date and tags for a single item.
280
-	 *
281
-	 * @param \Aimeos\MShop\Common\Item\Iface $item Item, maybe with associated list items
282
-	 * @param string $domain Name of the domain the item is from
283
-	 * @param string|null &$expire Expiration date that will be overwritten if an earlier date is found
284
-	 * @param array &$tags List of tags the new tags will be added to
285
-	 * @param boolean $tagAll True of tags for all items should be added, false if only for the main item
286
-	 */
287
-	private function addMetaItemSingle( \Aimeos\MShop\Common\Item\Iface $item, $domain, &$expire, array &$tags, $tagAll )
288
-	{
289
-		$expires = array();
290
-		$domain = str_replace( '/', '_', $domain ); // maximum compatiblity
291
-
292
-		if( $tagAll === true ) {
293
-			$tags[] = $domain . '-' . $item->getId();
294
-		}
295
-
296
-		if( $item instanceof \Aimeos\MShop\Common\Item\Time\Iface && ( $date = $item->getDateEnd() ) !== null ) {
297
-			$expires[] = $date;
298
-		}
299
-
300
-		if( $item instanceof \Aimeos\MShop\Common\Item\ListRef\Iface ) {
301
-			$this->addMetaItemRef( $item, $expires, $tags, $tagAll );
302
-		}
303
-
304
-		if( !empty( $expires ) ) {
305
-			$expire = min( $expires );
306
-		}
307
-	}
308
-
309
-
310
-	/**
311
-	 * Adds expire date and tags for referenced items
312
-	 *
313
-	 * @param \Aimeos\MShop\Common\Item\ListRef\Iface $item Item with associated list items
314
-	 * @param array &$expire Expiration date that will be overwritten if an earlier date is found
315
-	 * @param array &$tags List of tags the new tags will be added to
316
-	 * @param boolean $tagAll True of tags for all items should be added, false if only for the main item
317
-	 */
318
-	private function addMetaItemRef( \Aimeos\MShop\Common\Item\ListRef\Iface $item, array &$expires, array &$tags, $tagAll )
319
-	{
320
-		foreach( $item->getListItems() as $listitem )
321
-		{
322
-			if( $tagAll === true ) {
323
-				$tags[] = str_replace( '/', '_', $listitem->getDomain() ) . '-' . $listitem->getRefId();
324
-			}
325
-
326
-			if( ( $date = $listitem->getDateEnd() ) !== null ) {
327
-				$expires[] = $date;
328
-			}
329
-		}
330
-	}
331
-
332
-
333
-	/**
334
-	 * Adds a new expiration date if a list item is activated in the future.
335
-	 *
336
-	 * @param array|string $ids Item ID or list of item IDs from the given domain
337
-	 * @param string $domain Name of the domain the item IDs are from
338
-	 * @param string|null &$expire Expiration date that will be overwritten if an start date in the future is available
339
-	 */
340
-	protected function addMetaList( $ids, $domain, &$expire )
341
-	{
342
-		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), $domain . '/lists' );
343
-
344
-		$search = $manager->createSearch();
345
-		$expr = array(
346
-			$search->compare( '==', $domain . '.lists.parentid', $ids ),
347
-			$search->compare( '>', $domain . '.lists.datestart', date( 'Y-m-d H:i:00' ) ),
348
-		);
349
-		$search->setConditions( $search->combine( '&&', $expr ) );
350
-		$search->setSortations( array( $search->sort( '+', $domain . '.lists.datestart' ) ) );
351
-		$search->setSlice( 0, 1 );
352
-
353
-		foreach( $manager->searchItems( $search ) as $listItem ) {
354
-			$expire = $this->expires( $expire, $listItem->getDateStart() );
355
-		}
356
-	}
357
-
358
-
359
-	/**
360
-	 * Returns the sub-client given by its name.
361
-	 *
362
-	 * @param string $path Name of the sub-part in lower case (can contain a path like catalog/filter/tree)
363
-	 * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
364
-	 * @return \Aimeos\Client\Html\Iface Sub-part object
365
-	 */
366
-	protected function createSubClient( $path, $name )
367
-	{
368
-		$path = strtolower( $path );
369
-
370
-		if( $name === null ) {
371
-			$name = $this->context->getConfig()->get( 'client/html/' . $path . '/name', 'Standard' );
372
-		}
373
-
374
-		if( empty( $name ) || ctype_alnum( $name ) === false ) {
375
-			throw new \Aimeos\Client\Html\Exception( sprintf( 'Invalid characters in client name "%1$s"', $name ) );
376
-		}
377
-
378
-		$subnames = str_replace( ' ', '\\', ucwords( str_replace( '/', ' ', $path ) ) );
379
-
380
-		$classname = '\\Aimeos\\Client\\Html\\' . $subnames . '\\' . $name;
381
-		$interface = '\\Aimeos\\Client\\Html\\Iface';
382
-
383
-		if( class_exists( $classname ) === false ) {
384
-			throw new \Aimeos\Client\Html\Exception( sprintf( 'Class "%1$s" not available', $classname ) );
385
-		}
386
-
387
-		$object = new $classname( $this->context, $this->templatePaths );
388
-
389
-		if( ( $object instanceof $interface ) === false ) {
390
-			throw new \Aimeos\Client\Html\Exception( sprintf( 'Class "%1$s" does not implement interface "%2$s"', $classname, $interface ) );
391
-		}
392
-
393
-		return $this->addClientDecorators( $object, $this->templatePaths, $path );
394
-	}
395
-
396
-
397
-	/**
398
-	 * Returns the minimal expiration date.
399
-	 *
400
-	 * @param string|null $first First expiration date or null
401
-	 * @param string|null $second Second expiration date or null
402
-	 * @return string|null Expiration date
403
-	 */
404
-	protected function expires( $first, $second )
405
-	{
406
-		return ( $first !== null ? ( $second !== null ? min( $first, $second ) : $first ) : $second );
407
-	}
408
-
409
-
410
-	/**
411
-	 * Returns the parameters used by the html client.
412
-	 *
413
-	 * @param array $params Associative list of all parameters
414
-	 * @param array $prefixes List of prefixes the parameters must start with
415
-	 * @return array Associative list of parameters used by the html client
416
-	 */
417
-	protected function getClientParams( array $params, array $prefixes = array( 'f', 'l', 'd', 'a' ) )
418
-	{
419
-		$list = array();
420
-
421
-		foreach( $params as $key => $value )
422
-		{
423
-			if( in_array( $key[0], $prefixes ) && $key[1] === '_' ) {
424
-				$list[$key] = $value;
425
-			}
426
-		}
427
-
428
-		return $list;
429
-	}
430
-
431
-
432
-	/**
433
-	 * Returns the context object.
434
-	 *
435
-	 * @return \Aimeos\MShop\Context\Item\Iface Context object
436
-	 */
437
-	protected function getContext()
438
-	{
439
-		return $this->context;
440
-	}
441
-
442
-
443
-	/**
444
-	 * Generates an unique hash from based on the input suitable to be used as part of the cache key
445
-	 *
446
-	 * @param array $prefixes List of prefixes the parameters must start with
447
-	 * @param string $key Unique identifier if the content is placed more than once on the same page
448
-	 * @param array $config Multi-dimensional array of configuration options used by the client and sub-clients
449
-	 * @return string Unique hash
450
-	 */
451
-	protected function getParamHash( array $prefixes = array( 'f', 'l', 'd' ), $key = '', array $config = array() )
452
-	{
453
-		$locale = $this->getContext()->getLocale();
454
-		$params = $this->getClientParams( $this->getView()->param(), $prefixes );
455
-		ksort( $params );
456
-
457
-		if( ( $pstr = json_encode( $params ) ) === false || ( $cstr = json_encode( $config ) ) === false ) {
458
-			throw new \Aimeos\Client\Html\Exception( 'Unable to encode parameters or configuration options' );
459
-		}
460
-
461
-		return md5( $key . $pstr . $cstr . $locale->getLanguageId() . $locale->getCurrencyId() );
462
-	}
463
-
464
-
465
-	/**
466
-	 * Returns the list of sub-client names configured for the client.
467
-	 *
468
-	 * @return array List of HTML client names
469
-	 */
470
-	abstract protected function getSubClientNames();
471
-
472
-
473
-	/**
474
-	 * Returns the configured sub-clients or the ones named in the default parameter if none are configured.
475
-	 *
476
-	 * @return array List of sub-clients implementing \Aimeos\Client\Html\Iface	ordered in the same way as the names
477
-	 */
478
-	protected function getSubClients()
479
-	{
480
-		if( !isset( $this->subclients ) )
481
-		{
482
-			$this->subclients = array();
483
-
484
-			foreach( $this->getSubClientNames() as $name ) {
485
-				$this->subclients[] = $this->getSubClient( $name );
486
-			}
487
-		}
488
-
489
-		return $this->subclients;
490
-	}
491
-
492
-
493
-	/**
494
-	 * Returns the paths where the layout templates can be found
495
-	 *
496
-	 * @return array List of template paths
497
-	 * @since 2015.09
498
-	 */
499
-	protected function getTemplatePaths()
500
-	{
501
-		return $this->templatePaths;
502
-	}
503
-
504
-
505
-	/**
506
-	 * Returns the attribute type item specified by the code.
507
-	 *
508
-	 * @param string $prefix Domain prefix for the manager, e.g. "media/type"
509
-	 * @param string $domain Domain of the type item
510
-	 * @param string $code Code of the type item
511
-	 * @return \Aimeos\MShop\Common\Item\Type\Iface Type item
512
-	 * @throws \Aimeos\Controller\Jobs\Exception If no item is found
513
-	 */
514
-	protected function getTypeItem( $prefix, $domain, $code )
515
-	{
516
-		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), $prefix );
517
-		$prefix = str_replace( '/', '.', $prefix );
518
-
519
-		$search = $manager->createSearch();
520
-		$expr = array(
521
-				$search->compare( '==', $prefix . '.domain', $domain ),
522
-				$search->compare( '==', $prefix . '.code', $code ),
523
-		);
524
-		$search->setConditions( $search->combine( '&&', $expr ) );
525
-		$result = $manager->searchItems( $search );
526
-
527
-		if( ( $item = reset( $result ) ) === false )
528
-		{
529
-			$msg = sprintf( 'No type item for "%1$s/%2$s" in "%3$s" found', $domain, $code, $prefix );
530
-			throw new \Aimeos\Controller\Jobs\Exception( $msg );
531
-		}
532
-
533
-		return $item;
534
-	}
535
-
536
-
537
-	/**
538
-	 * Returns the cache entry for the given unique ID and type.
539
-	 *
540
-	 * @param string $type Type of the cache entry, i.e. "body" or "header"
541
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
542
-	 * @param string[] $prefixes List of prefixes of all parameters that are relevant for generating the output
543
-	 * @param string $confkey Configuration key prefix that matches all relevant settings for the component
544
-	 * @return string Cached entry or empty string if not available
545
-	 */
546
-	protected function getCached( $type, $uid, array $prefixes, $confkey )
547
-	{
548
-		if( !isset( $this->cache ) )
549
-		{
550
-			$context = $this->getContext();
551
-			$config = $context->getConfig();
552
-
553
-			/** client/html/common/cache/force
554
-			 * Enforces content caching regardless of user logins
555
-			 *
556
-			 * Caching the component output is normally disabled as soon as the
557
-			 * user has logged in. This enables displaying user or user group
558
-			 * specific content without mixing standard and user specific output.
559
-			 *
560
-			 * If you don't have any user or user group specific content
561
-			 * (products, categories, attributes, media, prices, texts, etc.),
562
-			 * you can enforce content caching nevertheless to keep response
563
-			 * times as low as possible.
564
-			 *
565
-			 * @param boolean True to cache output regardless of login, false for no caching
566
-			 * @since 2015.08
567
-			 * @category Developer
568
-			 * @category User
569
-			 * @see client/html/common/cache/tag-all
570
-			 */
571
-			$force = $config->get( 'client/html/common/cache/force', false );
572
-
573
-			if( $force == false && $context->getUserId() !== null ) {
574
-				return null;
575
-			}
576
-
577
-			$cfg = $config->get( $confkey, array() );
578
-
579
-			$keys = array(
580
-				'body' => $this->getParamHash( $prefixes, $uid . ':' . $confkey . ':body', $cfg ),
581
-				'header' => $this->getParamHash( $prefixes, $uid . ':' . $confkey . ':header', $cfg ),
582
-			);
583
-
584
-			$entries = $context->getCache()->getList( $keys );
585
-			$this->cache = array();
586
-
587
-			foreach( $keys as $key => $hash ) {
588
-				$this->cache[$key] = ( array_key_exists( $hash, $entries ) ? $entries[$hash] : null );
589
-			}
590
-		}
591
-
592
-		return ( array_key_exists( $type, $this->cache ) ? $this->cache[$type] : null );
593
-	}
594
-
595
-
596
-	/**
597
-	 * Returns the cache entry for the given type and unique ID.
598
-	 *
599
-	 * @param string $type Type of the cache entry, i.e. "body" or "header"
600
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
601
-	 * @param string[] $prefixes List of prefixes of all parameters that are relevant for generating the output
602
-	 * @param string $confkey Configuration key prefix that matches all relevant settings for the component
603
-	 * @param string $value Value string that should be stored for the given key
604
-	 * @param array $tags List of tag strings that should be assoicated to the
605
-	 * 	given value in the cache
606
-	 * @param string|null $expire Date/time string in "YYYY-MM-DD HH:mm:ss"
607
-	 * 	format when the cache entry expires
608
-	 */
609
-	protected function setCached( $type, $uid, array $prefixes, $confkey, $value, array $tags, $expire )
610
-	{
611
-		$context = $this->getContext();
612
-		$config = $context->getConfig();
613
-
614
-		$force = $config->get( 'client/html/common/cache/force', false );
615
-
616
-		if( $force == false && $context->getUserId() !== null ) {
617
-			return;
618
-		}
619
-
620
-		try
621
-		{
622
-			$cfg = $config->get( $confkey, array() );
623
-			$key = $this->getParamHash( $prefixes, $uid . ':' . $confkey . ':' . $type, $cfg );
624
-
625
-			$context->getCache()->set( $key, $value, array_unique( $tags ), $expire );
626
-		}
627
-		catch( \Exception $e )
628
-		{
629
-			$msg = sprintf( 'Unable to set cache entry: %1$s', $e->getMessage() );
630
-			$context->getLogger()->log( $msg, \Aimeos\MW\Logger\Base::NOTICE );
631
-		}
632
-	}
633
-
634
-
635
-	/**
636
-	 * Replaces the section in the content that is enclosed by the marker.
637
-	 *
638
-	 * @param string $content Cached content
639
-	 * @param string $section New section content
640
-	 * @param string $marker Name of the section marker without "<!-- " and " -->" parts
641
-	 */
642
-	protected function replaceSection( $content, $section, $marker )
643
-	{
644
-		$start = 0;
645
-		$len = strlen( $section );
646
-		$marker = '<!-- ' . $marker . ' -->';
647
-
648
-		while( ( $start = @strpos( $content, $marker, $start ) ) !== false )
649
-		{
650
-			if( ( $end = strpos( $content, $marker, $start + 1 ) ) !== false ) {
651
-				$content = substr_replace( $content, $section, $start, $end - $start + strlen( $marker ) );
652
-			}
653
-
654
-			$start += 2 * strlen( $marker ) + $len;
655
-		}
656
-
657
-		return $content;
658
-	}
659
-
660
-
661
-	/**
662
-	 * Sets the necessary parameter values in the view.
663
-	 *
664
-	 * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
665
-	 * @param array &$tags Result array for the list of tags that are associated to the output
666
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
667
-	 * @return \Aimeos\MW\View\Iface Modified view object
668
-	 */
669
-	protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
670
-	{
671
-		return $view;
672
-	}
673
-
674
-
675
-	/**
676
-	 * Translates the plugin error codes to human readable error strings.
677
-	 *
678
-	 * @param array $codes Associative list of scope and object as key and error code as value
679
-	 * @return array List of translated error messages
680
-	 */
681
-	protected function translatePluginErrorCodes( array $codes )
682
-	{
683
-		$errors = array();
684
-		$i18n = $this->context->getI18n();
685
-
686
-		foreach( $codes as $scope => $list )
687
-		{
688
-			foreach( $list as $object => $errcode )
689
-			{
690
-				$key = $scope . ( $scope !== 'product' ? '.' . $object : '' ) . '.' . $errcode;
691
-				$errors[] = $i18n->dt( 'mshop/code', $key );
692
-			}
693
-		}
694
-
695
-		return $errors;
696
-	}
24
+    private $view;
25
+    private $cache;
26
+    private $context;
27
+    private $subclients;
28
+    private $templatePaths;
29
+
30
+
31
+    /**
32
+     * Initializes the class instance.
33
+     *
34
+     * @param \Aimeos\MShop\Context\Item\Iface $context Context object
35
+     * @param array $templatePaths Associative list of the file system paths to the core or the extensions as key
36
+     * 	and a list of relative paths inside the core or the extension as values
37
+     */
38
+    public function __construct( \Aimeos\MShop\Context\Item\Iface $context, array $templatePaths )
39
+    {
40
+        $this->context = $context;
41
+        $this->templatePaths = $templatePaths;
42
+    }
43
+
44
+
45
+    /**
46
+     * Returns the view object that will generate the HTML output.
47
+     *
48
+     * @return \Aimeos\MW\View\Iface $view The view object which generates the HTML output
49
+     */
50
+    public function getView()
51
+    {
52
+        if( !isset( $this->view ) ) {
53
+            throw new \Aimeos\Client\Html\Exception( sprintf( 'No view available' ) );
54
+        }
55
+
56
+        return $this->view;
57
+    }
58
+
59
+
60
+    /**
61
+     * Modifies the cached body content to replace content based on sessions or cookies.
62
+     *
63
+     * @param string $content Cached content
64
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
65
+     * @return string Modified body content
66
+     */
67
+    public function modifyBody( $content, $uid )
68
+    {
69
+        $view = $this->getView();
70
+
71
+        foreach( $this->getSubClients() as $subclient )
72
+        {
73
+            $subclient->setView( $view );
74
+            $content = $subclient->modifyBody( $content, $uid );
75
+        }
76
+
77
+        return $content;
78
+    }
79
+
80
+
81
+    /**
82
+     * Modifies the cached header content to replace content based on sessions or cookies.
83
+     *
84
+     * @param string $content Cached content
85
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
86
+     * @return string Modified header content
87
+     */
88
+    public function modifyHeader( $content, $uid )
89
+    {
90
+        $view = $this->getView();
91
+
92
+        foreach( $this->getSubClients() as $subclient )
93
+        {
94
+            $subclient->setView( $view );
95
+            $content = $subclient->modifyHeader( $content, $uid );
96
+        }
97
+
98
+        return $content;
99
+    }
100
+
101
+
102
+    /**
103
+     * Processes the input, e.g. store given values.
104
+     * A view must be available and this method doesn't generate any output
105
+     * besides setting view variables.
106
+     *
107
+     * @return boolean False if processing is stopped, otherwise all processing was completed successfully
108
+     */
109
+    public function process()
110
+    {
111
+        $view = $this->getView();
112
+
113
+        foreach( $this->getSubClients() as $subclient )
114
+        {
115
+            $subclient->setView( $view );
116
+
117
+            if( $subclient->process() === false ) {
118
+                return false;
119
+            }
120
+        }
121
+
122
+        return true;
123
+    }
124
+
125
+
126
+    /**
127
+     * Sets the view object that will generate the HTML output.
128
+     *
129
+     * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
130
+     * @return \Aimeos\Client\Html\Iface Reference to this object for fluent calls
131
+     */
132
+    public function setView( \Aimeos\MW\View\Iface $view )
133
+    {
134
+        $this->view = $view;
135
+        return $this;
136
+    }
137
+
138
+
139
+    /**
140
+     * Adds the decorators to the client object
141
+     *
142
+     * @param \Aimeos\Client\Html\Iface $client Client object
143
+     * @param array $templatePaths List of file system paths where the templates are stored
144
+     * @param array $decorators List of decorator name that should be wrapped around the client
145
+     * @param string $classprefix Decorator class prefix, e.g. "\Aimeos\Client\Html\Catalog\Decorator\"
146
+     * @return \Aimeos\Client\Html\Iface Client object
147
+     */
148
+    protected function addDecorators( \Aimeos\Client\Html\Iface $client, array $templatePaths,
149
+        array $decorators, $classprefix )
150
+    {
151
+        $iface = '\\Aimeos\\Client\\Html\\Common\\Decorator\\Iface';
152
+
153
+        foreach( $decorators as $name )
154
+        {
155
+            if( ctype_alnum( $name ) === false )
156
+            {
157
+                $classname = is_string( $name ) ? $classprefix . $name : '<not a string>';
158
+                throw new \Aimeos\Client\Html\Exception( sprintf( 'Invalid class name "%1$s"', $classname ) );
159
+            }
160
+
161
+            $classname = $classprefix . $name;
162
+
163
+            if( class_exists( $classname ) === false ) {
164
+                throw new \Aimeos\Client\Html\Exception( sprintf( 'Class "%1$s" not found', $classname ) );
165
+            }
166
+
167
+            $client = new $classname( $client, $this->context, $this->templatePaths );
168
+
169
+            if( !( $client instanceof $iface ) ) {
170
+                throw new \Aimeos\Client\Html\Exception( sprintf( 'Class "%1$s" does not implement "%2$s"', $classname, $iface ) );
171
+            }
172
+        }
173
+
174
+        return $client;
175
+    }
176
+
177
+
178
+    /**
179
+     * Adds the decorators to the client object
180
+     *
181
+     * @param \Aimeos\Client\Html\Iface $client Client object
182
+     * @param array $templatePaths List of file system paths where the templates are stored
183
+     * @param string $path Client string in lower case, e.g. "catalog/detail/basic"
184
+     * @return \Aimeos\Client\Html\Iface Client object
185
+     */
186
+    protected function addClientDecorators( \Aimeos\Client\Html\Iface $client, array $templatePaths, $path )
187
+    {
188
+        if( !is_string( $path ) || $path === '' ) {
189
+            throw new \Aimeos\Client\Html\Exception( sprintf( 'Invalid domain "%1$s"', $path ) );
190
+        }
191
+
192
+        $localClass = str_replace( ' ', '\\', ucwords( str_replace( '/', ' ', $path ) ) );
193
+        $config = $this->context->getConfig();
194
+
195
+        $decorators = $config->get( 'client/html/common/decorators/default', array() );
196
+        $excludes = $config->get( 'client/html/' . $path . '/decorators/excludes', array() );
197
+
198
+        foreach( $decorators as $key => $name )
199
+        {
200
+            if( in_array( $name, $excludes ) ) {
201
+                unset( $decorators[$key] );
202
+            }
203
+        }
204
+
205
+        $classprefix = '\\Aimeos\\Client\\Html\\Common\\Decorator\\';
206
+        $client = $this->addDecorators( $client, $templatePaths, $decorators, $classprefix );
207
+
208
+        $classprefix = '\\Aimeos\\Client\\Html\\Common\\Decorator\\';
209
+        $decorators = $config->get( 'client/html/' . $path . '/decorators/global', array() );
210
+        $client = $this->addDecorators( $client, $templatePaths, $decorators, $classprefix );
211
+
212
+        $classprefix = '\\Aimeos\\Client\\Html\\' . $localClass . '\\Decorator\\';
213
+        $decorators = $config->get( 'client/html/' . $path . '/decorators/local', array() );
214
+        $client = $this->addDecorators( $client, $templatePaths, $decorators, $classprefix );
215
+
216
+        return $client;
217
+    }
218
+
219
+
220
+    /**
221
+     * Adds the cache tags to the given list and sets a new expiration date if necessary based on the given item.
222
+     *
223
+     * @param array|\Aimeos\MShop\Common\Item\Iface $items Item or list of items, maybe with associated list items
224
+     * @param string $domain Name of the domain the item is from
225
+     * @param string|null &$expire Expiration date that will be overwritten if an earlier date is found
226
+     * @param array &$tags List of tags the new tags will be added to
227
+     */
228
+    protected function addMetaItem( $items, $domain, &$expire, array &$tags )
229
+    {
230
+        /** client/html/common/cache/tag-all
231
+         * Adds tags for all items used in a cache entry
232
+         *
233
+         * Each cache entry storing rendered parts for the HTML header or body
234
+         * can be tagged with information which items like texts, media, etc.
235
+         * are used in the HTML. This allows removing only those cache entries
236
+         * whose content has really changed and only that entries have to be
237
+         * rebuild the next time.
238
+         *
239
+         * The standard behavior stores only tags for each used domain, e.g. if
240
+         * a text is used, only the tag "text" is added. If you change a text
241
+         * in the administration interface, all cache entries with the tag
242
+         * "text" will be removed from the cache. This effectively wipes out
243
+         * almost all cached entries, which have to be rebuild with the next
244
+         * request.
245
+         *
246
+         * Important: As a list or detail view can use several hundred items,
247
+         * this configuration option will also add this number of tags to the
248
+         * cache entry. When using a cache adapter that can't insert all tags
249
+         * at once, this slows down the initial cache insert (and therefore the
250
+         * page speed) drastically! It's only recommended to enable this option
251
+         * if you use the DB, Mysql or Redis adapter that can insert all tags
252
+         * at once.
253
+         *
254
+         * @param boolean True to add tags for all items, false to use only a domain tag
255
+         * @since 2014.07
256
+         * @category Developer
257
+         * @category User
258
+         * @see client/html/common/cache/force
259
+         * @see madmin/cache/manager/name
260
+         * @see madmin/cache/name
261
+         */
262
+        $tagAll = $this->context->getConfig()->get( 'client/html/common/cache/tag-all', false );
263
+
264
+        if( !is_array( $items ) ) {
265
+            $items = array( $items );
266
+        }
267
+
268
+        if( $tagAll !== true && !empty( $items ) ) {
269
+            $tags[] = $domain;
270
+        }
271
+
272
+        foreach( $items as $item ) {
273
+            $this->addMetaItemSingle( $item, $domain, $expire, $tags, $tagAll );
274
+        }
275
+    }
276
+
277
+
278
+    /**
279
+     * Adds expire date and tags for a single item.
280
+     *
281
+     * @param \Aimeos\MShop\Common\Item\Iface $item Item, maybe with associated list items
282
+     * @param string $domain Name of the domain the item is from
283
+     * @param string|null &$expire Expiration date that will be overwritten if an earlier date is found
284
+     * @param array &$tags List of tags the new tags will be added to
285
+     * @param boolean $tagAll True of tags for all items should be added, false if only for the main item
286
+     */
287
+    private function addMetaItemSingle( \Aimeos\MShop\Common\Item\Iface $item, $domain, &$expire, array &$tags, $tagAll )
288
+    {
289
+        $expires = array();
290
+        $domain = str_replace( '/', '_', $domain ); // maximum compatiblity
291
+
292
+        if( $tagAll === true ) {
293
+            $tags[] = $domain . '-' . $item->getId();
294
+        }
295
+
296
+        if( $item instanceof \Aimeos\MShop\Common\Item\Time\Iface && ( $date = $item->getDateEnd() ) !== null ) {
297
+            $expires[] = $date;
298
+        }
299
+
300
+        if( $item instanceof \Aimeos\MShop\Common\Item\ListRef\Iface ) {
301
+            $this->addMetaItemRef( $item, $expires, $tags, $tagAll );
302
+        }
303
+
304
+        if( !empty( $expires ) ) {
305
+            $expire = min( $expires );
306
+        }
307
+    }
308
+
309
+
310
+    /**
311
+     * Adds expire date and tags for referenced items
312
+     *
313
+     * @param \Aimeos\MShop\Common\Item\ListRef\Iface $item Item with associated list items
314
+     * @param array &$expire Expiration date that will be overwritten if an earlier date is found
315
+     * @param array &$tags List of tags the new tags will be added to
316
+     * @param boolean $tagAll True of tags for all items should be added, false if only for the main item
317
+     */
318
+    private function addMetaItemRef( \Aimeos\MShop\Common\Item\ListRef\Iface $item, array &$expires, array &$tags, $tagAll )
319
+    {
320
+        foreach( $item->getListItems() as $listitem )
321
+        {
322
+            if( $tagAll === true ) {
323
+                $tags[] = str_replace( '/', '_', $listitem->getDomain() ) . '-' . $listitem->getRefId();
324
+            }
325
+
326
+            if( ( $date = $listitem->getDateEnd() ) !== null ) {
327
+                $expires[] = $date;
328
+            }
329
+        }
330
+    }
331
+
332
+
333
+    /**
334
+     * Adds a new expiration date if a list item is activated in the future.
335
+     *
336
+     * @param array|string $ids Item ID or list of item IDs from the given domain
337
+     * @param string $domain Name of the domain the item IDs are from
338
+     * @param string|null &$expire Expiration date that will be overwritten if an start date in the future is available
339
+     */
340
+    protected function addMetaList( $ids, $domain, &$expire )
341
+    {
342
+        $manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), $domain . '/lists' );
343
+
344
+        $search = $manager->createSearch();
345
+        $expr = array(
346
+            $search->compare( '==', $domain . '.lists.parentid', $ids ),
347
+            $search->compare( '>', $domain . '.lists.datestart', date( 'Y-m-d H:i:00' ) ),
348
+        );
349
+        $search->setConditions( $search->combine( '&&', $expr ) );
350
+        $search->setSortations( array( $search->sort( '+', $domain . '.lists.datestart' ) ) );
351
+        $search->setSlice( 0, 1 );
352
+
353
+        foreach( $manager->searchItems( $search ) as $listItem ) {
354
+            $expire = $this->expires( $expire, $listItem->getDateStart() );
355
+        }
356
+    }
357
+
358
+
359
+    /**
360
+     * Returns the sub-client given by its name.
361
+     *
362
+     * @param string $path Name of the sub-part in lower case (can contain a path like catalog/filter/tree)
363
+     * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
364
+     * @return \Aimeos\Client\Html\Iface Sub-part object
365
+     */
366
+    protected function createSubClient( $path, $name )
367
+    {
368
+        $path = strtolower( $path );
369
+
370
+        if( $name === null ) {
371
+            $name = $this->context->getConfig()->get( 'client/html/' . $path . '/name', 'Standard' );
372
+        }
373
+
374
+        if( empty( $name ) || ctype_alnum( $name ) === false ) {
375
+            throw new \Aimeos\Client\Html\Exception( sprintf( 'Invalid characters in client name "%1$s"', $name ) );
376
+        }
377
+
378
+        $subnames = str_replace( ' ', '\\', ucwords( str_replace( '/', ' ', $path ) ) );
379
+
380
+        $classname = '\\Aimeos\\Client\\Html\\' . $subnames . '\\' . $name;
381
+        $interface = '\\Aimeos\\Client\\Html\\Iface';
382
+
383
+        if( class_exists( $classname ) === false ) {
384
+            throw new \Aimeos\Client\Html\Exception( sprintf( 'Class "%1$s" not available', $classname ) );
385
+        }
386
+
387
+        $object = new $classname( $this->context, $this->templatePaths );
388
+
389
+        if( ( $object instanceof $interface ) === false ) {
390
+            throw new \Aimeos\Client\Html\Exception( sprintf( 'Class "%1$s" does not implement interface "%2$s"', $classname, $interface ) );
391
+        }
392
+
393
+        return $this->addClientDecorators( $object, $this->templatePaths, $path );
394
+    }
395
+
396
+
397
+    /**
398
+     * Returns the minimal expiration date.
399
+     *
400
+     * @param string|null $first First expiration date or null
401
+     * @param string|null $second Second expiration date or null
402
+     * @return string|null Expiration date
403
+     */
404
+    protected function expires( $first, $second )
405
+    {
406
+        return ( $first !== null ? ( $second !== null ? min( $first, $second ) : $first ) : $second );
407
+    }
408
+
409
+
410
+    /**
411
+     * Returns the parameters used by the html client.
412
+     *
413
+     * @param array $params Associative list of all parameters
414
+     * @param array $prefixes List of prefixes the parameters must start with
415
+     * @return array Associative list of parameters used by the html client
416
+     */
417
+    protected function getClientParams( array $params, array $prefixes = array( 'f', 'l', 'd', 'a' ) )
418
+    {
419
+        $list = array();
420
+
421
+        foreach( $params as $key => $value )
422
+        {
423
+            if( in_array( $key[0], $prefixes ) && $key[1] === '_' ) {
424
+                $list[$key] = $value;
425
+            }
426
+        }
427
+
428
+        return $list;
429
+    }
430
+
431
+
432
+    /**
433
+     * Returns the context object.
434
+     *
435
+     * @return \Aimeos\MShop\Context\Item\Iface Context object
436
+     */
437
+    protected function getContext()
438
+    {
439
+        return $this->context;
440
+    }
441
+
442
+
443
+    /**
444
+     * Generates an unique hash from based on the input suitable to be used as part of the cache key
445
+     *
446
+     * @param array $prefixes List of prefixes the parameters must start with
447
+     * @param string $key Unique identifier if the content is placed more than once on the same page
448
+     * @param array $config Multi-dimensional array of configuration options used by the client and sub-clients
449
+     * @return string Unique hash
450
+     */
451
+    protected function getParamHash( array $prefixes = array( 'f', 'l', 'd' ), $key = '', array $config = array() )
452
+    {
453
+        $locale = $this->getContext()->getLocale();
454
+        $params = $this->getClientParams( $this->getView()->param(), $prefixes );
455
+        ksort( $params );
456
+
457
+        if( ( $pstr = json_encode( $params ) ) === false || ( $cstr = json_encode( $config ) ) === false ) {
458
+            throw new \Aimeos\Client\Html\Exception( 'Unable to encode parameters or configuration options' );
459
+        }
460
+
461
+        return md5( $key . $pstr . $cstr . $locale->getLanguageId() . $locale->getCurrencyId() );
462
+    }
463
+
464
+
465
+    /**
466
+     * Returns the list of sub-client names configured for the client.
467
+     *
468
+     * @return array List of HTML client names
469
+     */
470
+    abstract protected function getSubClientNames();
471
+
472
+
473
+    /**
474
+     * Returns the configured sub-clients or the ones named in the default parameter if none are configured.
475
+     *
476
+     * @return array List of sub-clients implementing \Aimeos\Client\Html\Iface	ordered in the same way as the names
477
+     */
478
+    protected function getSubClients()
479
+    {
480
+        if( !isset( $this->subclients ) )
481
+        {
482
+            $this->subclients = array();
483
+
484
+            foreach( $this->getSubClientNames() as $name ) {
485
+                $this->subclients[] = $this->getSubClient( $name );
486
+            }
487
+        }
488
+
489
+        return $this->subclients;
490
+    }
491
+
492
+
493
+    /**
494
+     * Returns the paths where the layout templates can be found
495
+     *
496
+     * @return array List of template paths
497
+     * @since 2015.09
498
+     */
499
+    protected function getTemplatePaths()
500
+    {
501
+        return $this->templatePaths;
502
+    }
503
+
504
+
505
+    /**
506
+     * Returns the attribute type item specified by the code.
507
+     *
508
+     * @param string $prefix Domain prefix for the manager, e.g. "media/type"
509
+     * @param string $domain Domain of the type item
510
+     * @param string $code Code of the type item
511
+     * @return \Aimeos\MShop\Common\Item\Type\Iface Type item
512
+     * @throws \Aimeos\Controller\Jobs\Exception If no item is found
513
+     */
514
+    protected function getTypeItem( $prefix, $domain, $code )
515
+    {
516
+        $manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), $prefix );
517
+        $prefix = str_replace( '/', '.', $prefix );
518
+
519
+        $search = $manager->createSearch();
520
+        $expr = array(
521
+                $search->compare( '==', $prefix . '.domain', $domain ),
522
+                $search->compare( '==', $prefix . '.code', $code ),
523
+        );
524
+        $search->setConditions( $search->combine( '&&', $expr ) );
525
+        $result = $manager->searchItems( $search );
526
+
527
+        if( ( $item = reset( $result ) ) === false )
528
+        {
529
+            $msg = sprintf( 'No type item for "%1$s/%2$s" in "%3$s" found', $domain, $code, $prefix );
530
+            throw new \Aimeos\Controller\Jobs\Exception( $msg );
531
+        }
532
+
533
+        return $item;
534
+    }
535
+
536
+
537
+    /**
538
+     * Returns the cache entry for the given unique ID and type.
539
+     *
540
+     * @param string $type Type of the cache entry, i.e. "body" or "header"
541
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
542
+     * @param string[] $prefixes List of prefixes of all parameters that are relevant for generating the output
543
+     * @param string $confkey Configuration key prefix that matches all relevant settings for the component
544
+     * @return string Cached entry or empty string if not available
545
+     */
546
+    protected function getCached( $type, $uid, array $prefixes, $confkey )
547
+    {
548
+        if( !isset( $this->cache ) )
549
+        {
550
+            $context = $this->getContext();
551
+            $config = $context->getConfig();
552
+
553
+            /** client/html/common/cache/force
554
+             * Enforces content caching regardless of user logins
555
+             *
556
+             * Caching the component output is normally disabled as soon as the
557
+             * user has logged in. This enables displaying user or user group
558
+             * specific content without mixing standard and user specific output.
559
+             *
560
+             * If you don't have any user or user group specific content
561
+             * (products, categories, attributes, media, prices, texts, etc.),
562
+             * you can enforce content caching nevertheless to keep response
563
+             * times as low as possible.
564
+             *
565
+             * @param boolean True to cache output regardless of login, false for no caching
566
+             * @since 2015.08
567
+             * @category Developer
568
+             * @category User
569
+             * @see client/html/common/cache/tag-all
570
+             */
571
+            $force = $config->get( 'client/html/common/cache/force', false );
572
+
573
+            if( $force == false && $context->getUserId() !== null ) {
574
+                return null;
575
+            }
576
+
577
+            $cfg = $config->get( $confkey, array() );
578
+
579
+            $keys = array(
580
+                'body' => $this->getParamHash( $prefixes, $uid . ':' . $confkey . ':body', $cfg ),
581
+                'header' => $this->getParamHash( $prefixes, $uid . ':' . $confkey . ':header', $cfg ),
582
+            );
583
+
584
+            $entries = $context->getCache()->getList( $keys );
585
+            $this->cache = array();
586
+
587
+            foreach( $keys as $key => $hash ) {
588
+                $this->cache[$key] = ( array_key_exists( $hash, $entries ) ? $entries[$hash] : null );
589
+            }
590
+        }
591
+
592
+        return ( array_key_exists( $type, $this->cache ) ? $this->cache[$type] : null );
593
+    }
594
+
595
+
596
+    /**
597
+     * Returns the cache entry for the given type and unique ID.
598
+     *
599
+     * @param string $type Type of the cache entry, i.e. "body" or "header"
600
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
601
+     * @param string[] $prefixes List of prefixes of all parameters that are relevant for generating the output
602
+     * @param string $confkey Configuration key prefix that matches all relevant settings for the component
603
+     * @param string $value Value string that should be stored for the given key
604
+     * @param array $tags List of tag strings that should be assoicated to the
605
+     * 	given value in the cache
606
+     * @param string|null $expire Date/time string in "YYYY-MM-DD HH:mm:ss"
607
+     * 	format when the cache entry expires
608
+     */
609
+    protected function setCached( $type, $uid, array $prefixes, $confkey, $value, array $tags, $expire )
610
+    {
611
+        $context = $this->getContext();
612
+        $config = $context->getConfig();
613
+
614
+        $force = $config->get( 'client/html/common/cache/force', false );
615
+
616
+        if( $force == false && $context->getUserId() !== null ) {
617
+            return;
618
+        }
619
+
620
+        try
621
+        {
622
+            $cfg = $config->get( $confkey, array() );
623
+            $key = $this->getParamHash( $prefixes, $uid . ':' . $confkey . ':' . $type, $cfg );
624
+
625
+            $context->getCache()->set( $key, $value, array_unique( $tags ), $expire );
626
+        }
627
+        catch( \Exception $e )
628
+        {
629
+            $msg = sprintf( 'Unable to set cache entry: %1$s', $e->getMessage() );
630
+            $context->getLogger()->log( $msg, \Aimeos\MW\Logger\Base::NOTICE );
631
+        }
632
+    }
633
+
634
+
635
+    /**
636
+     * Replaces the section in the content that is enclosed by the marker.
637
+     *
638
+     * @param string $content Cached content
639
+     * @param string $section New section content
640
+     * @param string $marker Name of the section marker without "<!-- " and " -->" parts
641
+     */
642
+    protected function replaceSection( $content, $section, $marker )
643
+    {
644
+        $start = 0;
645
+        $len = strlen( $section );
646
+        $marker = '<!-- ' . $marker . ' -->';
647
+
648
+        while( ( $start = @strpos( $content, $marker, $start ) ) !== false )
649
+        {
650
+            if( ( $end = strpos( $content, $marker, $start + 1 ) ) !== false ) {
651
+                $content = substr_replace( $content, $section, $start, $end - $start + strlen( $marker ) );
652
+            }
653
+
654
+            $start += 2 * strlen( $marker ) + $len;
655
+        }
656
+
657
+        return $content;
658
+    }
659
+
660
+
661
+    /**
662
+     * Sets the necessary parameter values in the view.
663
+     *
664
+     * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
665
+     * @param array &$tags Result array for the list of tags that are associated to the output
666
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
667
+     * @return \Aimeos\MW\View\Iface Modified view object
668
+     */
669
+    protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
670
+    {
671
+        return $view;
672
+    }
673
+
674
+
675
+    /**
676
+     * Translates the plugin error codes to human readable error strings.
677
+     *
678
+     * @param array $codes Associative list of scope and object as key and error code as value
679
+     * @return array List of translated error messages
680
+     */
681
+    protected function translatePluginErrorCodes( array $codes )
682
+    {
683
+        $errors = array();
684
+        $i18n = $this->context->getI18n();
685
+
686
+        foreach( $codes as $scope => $list )
687
+        {
688
+            foreach( $list as $object => $errcode )
689
+            {
690
+                $key = $scope . ( $scope !== 'product' ? '.' . $object : '' ) . '.' . $errcode;
691
+                $errors[] = $i18n->dt( 'mshop/code', $key );
692
+            }
693
+        }
694
+
695
+        return $errors;
696
+    }
697 697
 }
Please login to merge, or discard this patch.
Spacing   +143 added lines, -143 removed lines patch added patch discarded remove patch
@@ -35,7 +35,7 @@  discard block
 block discarded – undo
35 35
 	 * @param array $templatePaths Associative list of the file system paths to the core or the extensions as key
36 36
 	 * 	and a list of relative paths inside the core or the extension as values
37 37
 	 */
38
-	public function __construct( \Aimeos\MShop\Context\Item\Iface $context, array $templatePaths )
38
+	public function __construct(\Aimeos\MShop\Context\Item\Iface $context, array $templatePaths)
39 39
 	{
40 40
 		$this->context = $context;
41 41
 		$this->templatePaths = $templatePaths;
@@ -49,8 +49,8 @@  discard block
 block discarded – undo
49 49
 	 */
50 50
 	public function getView()
51 51
 	{
52
-		if( !isset( $this->view ) ) {
53
-			throw new \Aimeos\Client\Html\Exception( sprintf( 'No view available' ) );
52
+		if (!isset($this->view)) {
53
+			throw new \Aimeos\Client\Html\Exception(sprintf('No view available'));
54 54
 		}
55 55
 
56 56
 		return $this->view;
@@ -64,14 +64,14 @@  discard block
 block discarded – undo
64 64
 	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
65 65
 	 * @return string Modified body content
66 66
 	 */
67
-	public function modifyBody( $content, $uid )
67
+	public function modifyBody($content, $uid)
68 68
 	{
69 69
 		$view = $this->getView();
70 70
 
71
-		foreach( $this->getSubClients() as $subclient )
71
+		foreach ($this->getSubClients() as $subclient)
72 72
 		{
73
-			$subclient->setView( $view );
74
-			$content = $subclient->modifyBody( $content, $uid );
73
+			$subclient->setView($view);
74
+			$content = $subclient->modifyBody($content, $uid);
75 75
 		}
76 76
 
77 77
 		return $content;
@@ -85,14 +85,14 @@  discard block
 block discarded – undo
85 85
 	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
86 86
 	 * @return string Modified header content
87 87
 	 */
88
-	public function modifyHeader( $content, $uid )
88
+	public function modifyHeader($content, $uid)
89 89
 	{
90 90
 		$view = $this->getView();
91 91
 
92
-		foreach( $this->getSubClients() as $subclient )
92
+		foreach ($this->getSubClients() as $subclient)
93 93
 		{
94
-			$subclient->setView( $view );
95
-			$content = $subclient->modifyHeader( $content, $uid );
94
+			$subclient->setView($view);
95
+			$content = $subclient->modifyHeader($content, $uid);
96 96
 		}
97 97
 
98 98
 		return $content;
@@ -110,11 +110,11 @@  discard block
 block discarded – undo
110 110
 	{
111 111
 		$view = $this->getView();
112 112
 
113
-		foreach( $this->getSubClients() as $subclient )
113
+		foreach ($this->getSubClients() as $subclient)
114 114
 		{
115
-			$subclient->setView( $view );
115
+			$subclient->setView($view);
116 116
 
117
-			if( $subclient->process() === false ) {
117
+			if ($subclient->process() === false) {
118 118
 				return false;
119 119
 			}
120 120
 		}
@@ -129,7 +129,7 @@  discard block
 block discarded – undo
129 129
 	 * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
130 130
 	 * @return \Aimeos\Client\Html\Iface Reference to this object for fluent calls
131 131
 	 */
132
-	public function setView( \Aimeos\MW\View\Iface $view )
132
+	public function setView(\Aimeos\MW\View\Iface $view)
133 133
 	{
134 134
 		$this->view = $view;
135 135
 		return $this;
@@ -145,29 +145,29 @@  discard block
 block discarded – undo
145 145
 	 * @param string $classprefix Decorator class prefix, e.g. "\Aimeos\Client\Html\Catalog\Decorator\"
146 146
 	 * @return \Aimeos\Client\Html\Iface Client object
147 147
 	 */
148
-	protected function addDecorators( \Aimeos\Client\Html\Iface $client, array $templatePaths,
149
-		array $decorators, $classprefix )
148
+	protected function addDecorators(\Aimeos\Client\Html\Iface $client, array $templatePaths,
149
+		array $decorators, $classprefix)
150 150
 	{
151 151
 		$iface = '\\Aimeos\\Client\\Html\\Common\\Decorator\\Iface';
152 152
 
153
-		foreach( $decorators as $name )
153
+		foreach ($decorators as $name)
154 154
 		{
155
-			if( ctype_alnum( $name ) === false )
155
+			if (ctype_alnum($name) === false)
156 156
 			{
157
-				$classname = is_string( $name ) ? $classprefix . $name : '<not a string>';
158
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'Invalid class name "%1$s"', $classname ) );
157
+				$classname = is_string($name) ? $classprefix.$name : '<not a string>';
158
+				throw new \Aimeos\Client\Html\Exception(sprintf('Invalid class name "%1$s"', $classname));
159 159
 			}
160 160
 
161
-			$classname = $classprefix . $name;
161
+			$classname = $classprefix.$name;
162 162
 
163
-			if( class_exists( $classname ) === false ) {
164
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'Class "%1$s" not found', $classname ) );
163
+			if (class_exists($classname) === false) {
164
+				throw new \Aimeos\Client\Html\Exception(sprintf('Class "%1$s" not found', $classname));
165 165
 			}
166 166
 
167
-			$client = new $classname( $client, $this->context, $this->templatePaths );
167
+			$client = new $classname($client, $this->context, $this->templatePaths);
168 168
 
169
-			if( !( $client instanceof $iface ) ) {
170
-				throw new \Aimeos\Client\Html\Exception( sprintf( 'Class "%1$s" does not implement "%2$s"', $classname, $iface ) );
169
+			if (!($client instanceof $iface)) {
170
+				throw new \Aimeos\Client\Html\Exception(sprintf('Class "%1$s" does not implement "%2$s"', $classname, $iface));
171 171
 			}
172 172
 		}
173 173
 
@@ -183,35 +183,35 @@  discard block
 block discarded – undo
183 183
 	 * @param string $path Client string in lower case, e.g. "catalog/detail/basic"
184 184
 	 * @return \Aimeos\Client\Html\Iface Client object
185 185
 	 */
186
-	protected function addClientDecorators( \Aimeos\Client\Html\Iface $client, array $templatePaths, $path )
186
+	protected function addClientDecorators(\Aimeos\Client\Html\Iface $client, array $templatePaths, $path)
187 187
 	{
188
-		if( !is_string( $path ) || $path === '' ) {
189
-			throw new \Aimeos\Client\Html\Exception( sprintf( 'Invalid domain "%1$s"', $path ) );
188
+		if (!is_string($path) || $path === '') {
189
+			throw new \Aimeos\Client\Html\Exception(sprintf('Invalid domain "%1$s"', $path));
190 190
 		}
191 191
 
192
-		$localClass = str_replace( ' ', '\\', ucwords( str_replace( '/', ' ', $path ) ) );
192
+		$localClass = str_replace(' ', '\\', ucwords(str_replace('/', ' ', $path)));
193 193
 		$config = $this->context->getConfig();
194 194
 
195
-		$decorators = $config->get( 'client/html/common/decorators/default', array() );
196
-		$excludes = $config->get( 'client/html/' . $path . '/decorators/excludes', array() );
195
+		$decorators = $config->get('client/html/common/decorators/default', array());
196
+		$excludes = $config->get('client/html/'.$path.'/decorators/excludes', array());
197 197
 
198
-		foreach( $decorators as $key => $name )
198
+		foreach ($decorators as $key => $name)
199 199
 		{
200
-			if( in_array( $name, $excludes ) ) {
201
-				unset( $decorators[$key] );
200
+			if (in_array($name, $excludes)) {
201
+				unset($decorators[$key]);
202 202
 			}
203 203
 		}
204 204
 
205 205
 		$classprefix = '\\Aimeos\\Client\\Html\\Common\\Decorator\\';
206
-		$client = $this->addDecorators( $client, $templatePaths, $decorators, $classprefix );
206
+		$client = $this->addDecorators($client, $templatePaths, $decorators, $classprefix);
207 207
 
208 208
 		$classprefix = '\\Aimeos\\Client\\Html\\Common\\Decorator\\';
209
-		$decorators = $config->get( 'client/html/' . $path . '/decorators/global', array() );
210
-		$client = $this->addDecorators( $client, $templatePaths, $decorators, $classprefix );
209
+		$decorators = $config->get('client/html/'.$path.'/decorators/global', array());
210
+		$client = $this->addDecorators($client, $templatePaths, $decorators, $classprefix);
211 211
 
212
-		$classprefix = '\\Aimeos\\Client\\Html\\' . $localClass . '\\Decorator\\';
213
-		$decorators = $config->get( 'client/html/' . $path . '/decorators/local', array() );
214
-		$client = $this->addDecorators( $client, $templatePaths, $decorators, $classprefix );
212
+		$classprefix = '\\Aimeos\\Client\\Html\\'.$localClass.'\\Decorator\\';
213
+		$decorators = $config->get('client/html/'.$path.'/decorators/local', array());
214
+		$client = $this->addDecorators($client, $templatePaths, $decorators, $classprefix);
215 215
 
216 216
 		return $client;
217 217
 	}
@@ -225,7 +225,7 @@  discard block
 block discarded – undo
225 225
 	 * @param string|null &$expire Expiration date that will be overwritten if an earlier date is found
226 226
 	 * @param array &$tags List of tags the new tags will be added to
227 227
 	 */
228
-	protected function addMetaItem( $items, $domain, &$expire, array &$tags )
228
+	protected function addMetaItem($items, $domain, &$expire, array &$tags)
229 229
 	{
230 230
 		/** client/html/common/cache/tag-all
231 231
 		 * Adds tags for all items used in a cache entry
@@ -259,18 +259,18 @@  discard block
 block discarded – undo
259 259
 		 * @see madmin/cache/manager/name
260 260
 		 * @see madmin/cache/name
261 261
 		 */
262
-		$tagAll = $this->context->getConfig()->get( 'client/html/common/cache/tag-all', false );
262
+		$tagAll = $this->context->getConfig()->get('client/html/common/cache/tag-all', false);
263 263
 
264
-		if( !is_array( $items ) ) {
265
-			$items = array( $items );
264
+		if (!is_array($items)) {
265
+			$items = array($items);
266 266
 		}
267 267
 
268
-		if( $tagAll !== true && !empty( $items ) ) {
268
+		if ($tagAll !== true && !empty($items)) {
269 269
 			$tags[] = $domain;
270 270
 		}
271 271
 
272
-		foreach( $items as $item ) {
273
-			$this->addMetaItemSingle( $item, $domain, $expire, $tags, $tagAll );
272
+		foreach ($items as $item) {
273
+			$this->addMetaItemSingle($item, $domain, $expire, $tags, $tagAll);
274 274
 		}
275 275
 	}
276 276
 
@@ -284,25 +284,25 @@  discard block
 block discarded – undo
284 284
 	 * @param array &$tags List of tags the new tags will be added to
285 285
 	 * @param boolean $tagAll True of tags for all items should be added, false if only for the main item
286 286
 	 */
287
-	private function addMetaItemSingle( \Aimeos\MShop\Common\Item\Iface $item, $domain, &$expire, array &$tags, $tagAll )
287
+	private function addMetaItemSingle(\Aimeos\MShop\Common\Item\Iface $item, $domain, &$expire, array &$tags, $tagAll)
288 288
 	{
289 289
 		$expires = array();
290
-		$domain = str_replace( '/', '_', $domain ); // maximum compatiblity
290
+		$domain = str_replace('/', '_', $domain); // maximum compatiblity
291 291
 
292
-		if( $tagAll === true ) {
293
-			$tags[] = $domain . '-' . $item->getId();
292
+		if ($tagAll === true) {
293
+			$tags[] = $domain.'-'.$item->getId();
294 294
 		}
295 295
 
296
-		if( $item instanceof \Aimeos\MShop\Common\Item\Time\Iface && ( $date = $item->getDateEnd() ) !== null ) {
296
+		if ($item instanceof \Aimeos\MShop\Common\Item\Time\Iface && ($date = $item->getDateEnd()) !== null) {
297 297
 			$expires[] = $date;
298 298
 		}
299 299
 
300
-		if( $item instanceof \Aimeos\MShop\Common\Item\ListRef\Iface ) {
301
-			$this->addMetaItemRef( $item, $expires, $tags, $tagAll );
300
+		if ($item instanceof \Aimeos\MShop\Common\Item\ListRef\Iface) {
301
+			$this->addMetaItemRef($item, $expires, $tags, $tagAll);
302 302
 		}
303 303
 
304
-		if( !empty( $expires ) ) {
305
-			$expire = min( $expires );
304
+		if (!empty($expires)) {
305
+			$expire = min($expires);
306 306
 		}
307 307
 	}
308 308
 
@@ -315,15 +315,15 @@  discard block
 block discarded – undo
315 315
 	 * @param array &$tags List of tags the new tags will be added to
316 316
 	 * @param boolean $tagAll True of tags for all items should be added, false if only for the main item
317 317
 	 */
318
-	private function addMetaItemRef( \Aimeos\MShop\Common\Item\ListRef\Iface $item, array &$expires, array &$tags, $tagAll )
318
+	private function addMetaItemRef(\Aimeos\MShop\Common\Item\ListRef\Iface $item, array &$expires, array &$tags, $tagAll)
319 319
 	{
320
-		foreach( $item->getListItems() as $listitem )
320
+		foreach ($item->getListItems() as $listitem)
321 321
 		{
322
-			if( $tagAll === true ) {
323
-				$tags[] = str_replace( '/', '_', $listitem->getDomain() ) . '-' . $listitem->getRefId();
322
+			if ($tagAll === true) {
323
+				$tags[] = str_replace('/', '_', $listitem->getDomain()).'-'.$listitem->getRefId();
324 324
 			}
325 325
 
326
-			if( ( $date = $listitem->getDateEnd() ) !== null ) {
326
+			if (($date = $listitem->getDateEnd()) !== null) {
327 327
 				$expires[] = $date;
328 328
 			}
329 329
 		}
@@ -337,21 +337,21 @@  discard block
 block discarded – undo
337 337
 	 * @param string $domain Name of the domain the item IDs are from
338 338
 	 * @param string|null &$expire Expiration date that will be overwritten if an start date in the future is available
339 339
 	 */
340
-	protected function addMetaList( $ids, $domain, &$expire )
340
+	protected function addMetaList($ids, $domain, &$expire)
341 341
 	{
342
-		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), $domain . '/lists' );
342
+		$manager = \Aimeos\MShop\Factory::createManager($this->getContext(), $domain.'/lists');
343 343
 
344 344
 		$search = $manager->createSearch();
345 345
 		$expr = array(
346
-			$search->compare( '==', $domain . '.lists.parentid', $ids ),
347
-			$search->compare( '>', $domain . '.lists.datestart', date( 'Y-m-d H:i:00' ) ),
346
+			$search->compare('==', $domain.'.lists.parentid', $ids),
347
+			$search->compare('>', $domain.'.lists.datestart', date('Y-m-d H:i:00')),
348 348
 		);
349
-		$search->setConditions( $search->combine( '&&', $expr ) );
350
-		$search->setSortations( array( $search->sort( '+', $domain . '.lists.datestart' ) ) );
351
-		$search->setSlice( 0, 1 );
349
+		$search->setConditions($search->combine('&&', $expr));
350
+		$search->setSortations(array($search->sort('+', $domain.'.lists.datestart')));
351
+		$search->setSlice(0, 1);
352 352
 
353
-		foreach( $manager->searchItems( $search ) as $listItem ) {
354
-			$expire = $this->expires( $expire, $listItem->getDateStart() );
353
+		foreach ($manager->searchItems($search) as $listItem) {
354
+			$expire = $this->expires($expire, $listItem->getDateStart());
355 355
 		}
356 356
 	}
357 357
 
@@ -363,34 +363,34 @@  discard block
 block discarded – undo
363 363
 	 * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
364 364
 	 * @return \Aimeos\Client\Html\Iface Sub-part object
365 365
 	 */
366
-	protected function createSubClient( $path, $name )
366
+	protected function createSubClient($path, $name)
367 367
 	{
368
-		$path = strtolower( $path );
368
+		$path = strtolower($path);
369 369
 
370
-		if( $name === null ) {
371
-			$name = $this->context->getConfig()->get( 'client/html/' . $path . '/name', 'Standard' );
370
+		if ($name === null) {
371
+			$name = $this->context->getConfig()->get('client/html/'.$path.'/name', 'Standard');
372 372
 		}
373 373
 
374
-		if( empty( $name ) || ctype_alnum( $name ) === false ) {
375
-			throw new \Aimeos\Client\Html\Exception( sprintf( 'Invalid characters in client name "%1$s"', $name ) );
374
+		if (empty($name) || ctype_alnum($name) === false) {
375
+			throw new \Aimeos\Client\Html\Exception(sprintf('Invalid characters in client name "%1$s"', $name));
376 376
 		}
377 377
 
378
-		$subnames = str_replace( ' ', '\\', ucwords( str_replace( '/', ' ', $path ) ) );
378
+		$subnames = str_replace(' ', '\\', ucwords(str_replace('/', ' ', $path)));
379 379
 
380
-		$classname = '\\Aimeos\\Client\\Html\\' . $subnames . '\\' . $name;
380
+		$classname = '\\Aimeos\\Client\\Html\\'.$subnames.'\\'.$name;
381 381
 		$interface = '\\Aimeos\\Client\\Html\\Iface';
382 382
 
383
-		if( class_exists( $classname ) === false ) {
384
-			throw new \Aimeos\Client\Html\Exception( sprintf( 'Class "%1$s" not available', $classname ) );
383
+		if (class_exists($classname) === false) {
384
+			throw new \Aimeos\Client\Html\Exception(sprintf('Class "%1$s" not available', $classname));
385 385
 		}
386 386
 
387
-		$object = new $classname( $this->context, $this->templatePaths );
387
+		$object = new $classname($this->context, $this->templatePaths);
388 388
 
389
-		if( ( $object instanceof $interface ) === false ) {
390
-			throw new \Aimeos\Client\Html\Exception( sprintf( 'Class "%1$s" does not implement interface "%2$s"', $classname, $interface ) );
389
+		if (($object instanceof $interface) === false) {
390
+			throw new \Aimeos\Client\Html\Exception(sprintf('Class "%1$s" does not implement interface "%2$s"', $classname, $interface));
391 391
 		}
392 392
 
393
-		return $this->addClientDecorators( $object, $this->templatePaths, $path );
393
+		return $this->addClientDecorators($object, $this->templatePaths, $path);
394 394
 	}
395 395
 
396 396
 
@@ -401,9 +401,9 @@  discard block
 block discarded – undo
401 401
 	 * @param string|null $second Second expiration date or null
402 402
 	 * @return string|null Expiration date
403 403
 	 */
404
-	protected function expires( $first, $second )
404
+	protected function expires($first, $second)
405 405
 	{
406
-		return ( $first !== null ? ( $second !== null ? min( $first, $second ) : $first ) : $second );
406
+		return ($first !== null ? ($second !== null ? min($first, $second) : $first) : $second);
407 407
 	}
408 408
 
409 409
 
@@ -414,13 +414,13 @@  discard block
 block discarded – undo
414 414
 	 * @param array $prefixes List of prefixes the parameters must start with
415 415
 	 * @return array Associative list of parameters used by the html client
416 416
 	 */
417
-	protected function getClientParams( array $params, array $prefixes = array( 'f', 'l', 'd', 'a' ) )
417
+	protected function getClientParams(array $params, array $prefixes = array('f', 'l', 'd', 'a'))
418 418
 	{
419 419
 		$list = array();
420 420
 
421
-		foreach( $params as $key => $value )
421
+		foreach ($params as $key => $value)
422 422
 		{
423
-			if( in_array( $key[0], $prefixes ) && $key[1] === '_' ) {
423
+			if (in_array($key[0], $prefixes) && $key[1] === '_') {
424 424
 				$list[$key] = $value;
425 425
 			}
426 426
 		}
@@ -448,17 +448,17 @@  discard block
 block discarded – undo
448 448
 	 * @param array $config Multi-dimensional array of configuration options used by the client and sub-clients
449 449
 	 * @return string Unique hash
450 450
 	 */
451
-	protected function getParamHash( array $prefixes = array( 'f', 'l', 'd' ), $key = '', array $config = array() )
451
+	protected function getParamHash(array $prefixes = array('f', 'l', 'd'), $key = '', array $config = array())
452 452
 	{
453 453
 		$locale = $this->getContext()->getLocale();
454
-		$params = $this->getClientParams( $this->getView()->param(), $prefixes );
455
-		ksort( $params );
454
+		$params = $this->getClientParams($this->getView()->param(), $prefixes);
455
+		ksort($params);
456 456
 
457
-		if( ( $pstr = json_encode( $params ) ) === false || ( $cstr = json_encode( $config ) ) === false ) {
458
-			throw new \Aimeos\Client\Html\Exception( 'Unable to encode parameters or configuration options' );
457
+		if (($pstr = json_encode($params)) === false || ($cstr = json_encode($config)) === false) {
458
+			throw new \Aimeos\Client\Html\Exception('Unable to encode parameters or configuration options');
459 459
 		}
460 460
 
461
-		return md5( $key . $pstr . $cstr . $locale->getLanguageId() . $locale->getCurrencyId() );
461
+		return md5($key.$pstr.$cstr.$locale->getLanguageId().$locale->getCurrencyId());
462 462
 	}
463 463
 
464 464
 
@@ -477,12 +477,12 @@  discard block
 block discarded – undo
477 477
 	 */
478 478
 	protected function getSubClients()
479 479
 	{
480
-		if( !isset( $this->subclients ) )
480
+		if (!isset($this->subclients))
481 481
 		{
482 482
 			$this->subclients = array();
483 483
 
484
-			foreach( $this->getSubClientNames() as $name ) {
485
-				$this->subclients[] = $this->getSubClient( $name );
484
+			foreach ($this->getSubClientNames() as $name) {
485
+				$this->subclients[] = $this->getSubClient($name);
486 486
 			}
487 487
 		}
488 488
 
@@ -511,23 +511,23 @@  discard block
 block discarded – undo
511 511
 	 * @return \Aimeos\MShop\Common\Item\Type\Iface Type item
512 512
 	 * @throws \Aimeos\Controller\Jobs\Exception If no item is found
513 513
 	 */
514
-	protected function getTypeItem( $prefix, $domain, $code )
514
+	protected function getTypeItem($prefix, $domain, $code)
515 515
 	{
516
-		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), $prefix );
517
-		$prefix = str_replace( '/', '.', $prefix );
516
+		$manager = \Aimeos\MShop\Factory::createManager($this->getContext(), $prefix);
517
+		$prefix = str_replace('/', '.', $prefix);
518 518
 
519 519
 		$search = $manager->createSearch();
520 520
 		$expr = array(
521
-				$search->compare( '==', $prefix . '.domain', $domain ),
522
-				$search->compare( '==', $prefix . '.code', $code ),
521
+				$search->compare('==', $prefix.'.domain', $domain),
522
+				$search->compare('==', $prefix.'.code', $code),
523 523
 		);
524
-		$search->setConditions( $search->combine( '&&', $expr ) );
525
-		$result = $manager->searchItems( $search );
524
+		$search->setConditions($search->combine('&&', $expr));
525
+		$result = $manager->searchItems($search);
526 526
 
527
-		if( ( $item = reset( $result ) ) === false )
527
+		if (($item = reset($result)) === false)
528 528
 		{
529
-			$msg = sprintf( 'No type item for "%1$s/%2$s" in "%3$s" found', $domain, $code, $prefix );
530
-			throw new \Aimeos\Controller\Jobs\Exception( $msg );
529
+			$msg = sprintf('No type item for "%1$s/%2$s" in "%3$s" found', $domain, $code, $prefix);
530
+			throw new \Aimeos\Controller\Jobs\Exception($msg);
531 531
 		}
532 532
 
533 533
 		return $item;
@@ -543,9 +543,9 @@  discard block
 block discarded – undo
543 543
 	 * @param string $confkey Configuration key prefix that matches all relevant settings for the component
544 544
 	 * @return string Cached entry or empty string if not available
545 545
 	 */
546
-	protected function getCached( $type, $uid, array $prefixes, $confkey )
546
+	protected function getCached($type, $uid, array $prefixes, $confkey)
547 547
 	{
548
-		if( !isset( $this->cache ) )
548
+		if (!isset($this->cache))
549 549
 		{
550 550
 			$context = $this->getContext();
551 551
 			$config = $context->getConfig();
@@ -568,28 +568,28 @@  discard block
 block discarded – undo
568 568
 			 * @category User
569 569
 			 * @see client/html/common/cache/tag-all
570 570
 			 */
571
-			$force = $config->get( 'client/html/common/cache/force', false );
571
+			$force = $config->get('client/html/common/cache/force', false);
572 572
 
573
-			if( $force == false && $context->getUserId() !== null ) {
573
+			if ($force == false && $context->getUserId() !== null) {
574 574
 				return null;
575 575
 			}
576 576
 
577
-			$cfg = $config->get( $confkey, array() );
577
+			$cfg = $config->get($confkey, array());
578 578
 
579 579
 			$keys = array(
580
-				'body' => $this->getParamHash( $prefixes, $uid . ':' . $confkey . ':body', $cfg ),
581
-				'header' => $this->getParamHash( $prefixes, $uid . ':' . $confkey . ':header', $cfg ),
580
+				'body' => $this->getParamHash($prefixes, $uid.':'.$confkey.':body', $cfg),
581
+				'header' => $this->getParamHash($prefixes, $uid.':'.$confkey.':header', $cfg),
582 582
 			);
583 583
 
584
-			$entries = $context->getCache()->getList( $keys );
584
+			$entries = $context->getCache()->getList($keys);
585 585
 			$this->cache = array();
586 586
 
587
-			foreach( $keys as $key => $hash ) {
588
-				$this->cache[$key] = ( array_key_exists( $hash, $entries ) ? $entries[$hash] : null );
587
+			foreach ($keys as $key => $hash) {
588
+				$this->cache[$key] = (array_key_exists($hash, $entries) ? $entries[$hash] : null);
589 589
 			}
590 590
 		}
591 591
 
592
-		return ( array_key_exists( $type, $this->cache ) ? $this->cache[$type] : null );
592
+		return (array_key_exists($type, $this->cache) ? $this->cache[$type] : null);
593 593
 	}
594 594
 
595 595
 
@@ -606,28 +606,28 @@  discard block
 block discarded – undo
606 606
 	 * @param string|null $expire Date/time string in "YYYY-MM-DD HH:mm:ss"
607 607
 	 * 	format when the cache entry expires
608 608
 	 */
609
-	protected function setCached( $type, $uid, array $prefixes, $confkey, $value, array $tags, $expire )
609
+	protected function setCached($type, $uid, array $prefixes, $confkey, $value, array $tags, $expire)
610 610
 	{
611 611
 		$context = $this->getContext();
612 612
 		$config = $context->getConfig();
613 613
 
614
-		$force = $config->get( 'client/html/common/cache/force', false );
614
+		$force = $config->get('client/html/common/cache/force', false);
615 615
 
616
-		if( $force == false && $context->getUserId() !== null ) {
616
+		if ($force == false && $context->getUserId() !== null) {
617 617
 			return;
618 618
 		}
619 619
 
620 620
 		try
621 621
 		{
622
-			$cfg = $config->get( $confkey, array() );
623
-			$key = $this->getParamHash( $prefixes, $uid . ':' . $confkey . ':' . $type, $cfg );
622
+			$cfg = $config->get($confkey, array());
623
+			$key = $this->getParamHash($prefixes, $uid.':'.$confkey.':'.$type, $cfg);
624 624
 
625
-			$context->getCache()->set( $key, $value, array_unique( $tags ), $expire );
625
+			$context->getCache()->set($key, $value, array_unique($tags), $expire);
626 626
 		}
627
-		catch( \Exception $e )
627
+		catch (\Exception $e)
628 628
 		{
629
-			$msg = sprintf( 'Unable to set cache entry: %1$s', $e->getMessage() );
630
-			$context->getLogger()->log( $msg, \Aimeos\MW\Logger\Base::NOTICE );
629
+			$msg = sprintf('Unable to set cache entry: %1$s', $e->getMessage());
630
+			$context->getLogger()->log($msg, \Aimeos\MW\Logger\Base::NOTICE);
631 631
 		}
632 632
 	}
633 633
 
@@ -639,19 +639,19 @@  discard block
 block discarded – undo
639 639
 	 * @param string $section New section content
640 640
 	 * @param string $marker Name of the section marker without "<!-- " and " -->" parts
641 641
 	 */
642
-	protected function replaceSection( $content, $section, $marker )
642
+	protected function replaceSection($content, $section, $marker)
643 643
 	{
644 644
 		$start = 0;
645
-		$len = strlen( $section );
646
-		$marker = '<!-- ' . $marker . ' -->';
645
+		$len = strlen($section);
646
+		$marker = '<!-- '.$marker.' -->';
647 647
 
648
-		while( ( $start = @strpos( $content, $marker, $start ) ) !== false )
648
+		while (($start = @strpos($content, $marker, $start)) !== false)
649 649
 		{
650
-			if( ( $end = strpos( $content, $marker, $start + 1 ) ) !== false ) {
651
-				$content = substr_replace( $content, $section, $start, $end - $start + strlen( $marker ) );
650
+			if (($end = strpos($content, $marker, $start + 1)) !== false) {
651
+				$content = substr_replace($content, $section, $start, $end - $start + strlen($marker));
652 652
 			}
653 653
 
654
-			$start += 2 * strlen( $marker ) + $len;
654
+			$start += 2 * strlen($marker) + $len;
655 655
 		}
656 656
 
657 657
 		return $content;
@@ -666,7 +666,7 @@  discard block
 block discarded – undo
666 666
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
667 667
 	 * @return \Aimeos\MW\View\Iface Modified view object
668 668
 	 */
669
-	protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
669
+	protected function setViewParams(\Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null)
670 670
 	{
671 671
 		return $view;
672 672
 	}
@@ -678,17 +678,17 @@  discard block
 block discarded – undo
678 678
 	 * @param array $codes Associative list of scope and object as key and error code as value
679 679
 	 * @return array List of translated error messages
680 680
 	 */
681
-	protected function translatePluginErrorCodes( array $codes )
681
+	protected function translatePluginErrorCodes(array $codes)
682 682
 	{
683 683
 		$errors = array();
684 684
 		$i18n = $this->context->getI18n();
685 685
 
686
-		foreach( $codes as $scope => $list )
686
+		foreach ($codes as $scope => $list)
687 687
 		{
688
-			foreach( $list as $object => $errcode )
688
+			foreach ($list as $object => $errcode)
689 689
 			{
690
-				$key = $scope . ( $scope !== 'product' ? '.' . $object : '' ) . '.' . $errcode;
691
-				$errors[] = $i18n->dt( 'mshop/code', $key );
690
+				$key = $scope.($scope !== 'product' ? '.'.$object : '').'.'.$errcode;
691
+				$errors[] = $i18n->dt('mshop/code', $key);
692 692
 			}
693 693
 		}
694 694
 
Please login to merge, or discard this patch.
client/html/src/Client/Html/Catalog/Filter/Tree/Standard.php 2 patches
Indentation   +470 added lines, -470 removed lines patch added patch discarded remove patch
@@ -19,475 +19,475 @@
 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/catalog/filter/tree/standard/subparts
26
-	 * List of HTML sub-clients rendered within the catalog filter tree 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/catalog/filter/tree/standard/subparts';
59
-	private $subPartNames = array();
60
-	private $tags = array();
61
-	private $expire;
62
-	private $cache;
63
-
64
-
65
-	/**
66
-	 * Returns the HTML code for insertion into the body.
67
-	 *
68
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
69
-	 * @param array &$tags Result array for the list of tags that are associated to the output
70
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
71
-	 * @return string HTML code
72
-	 */
73
-	public function getBody( $uid = '', array &$tags = array(), &$expire = null )
74
-	{
75
-		$view = $this->setViewParams( $this->getView(), $tags, $expire );
76
-
77
-		$html = '';
78
-		foreach( $this->getSubClients() as $subclient ) {
79
-			$html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
80
-		}
81
-		$view->treeBody = $html;
82
-
83
-		/** client/html/catalog/filter/tree/standard/template-body
84
-		 * Relative path to the HTML body template of the catalog filter tree client.
85
-		 *
86
-		 * The template file contains the HTML code and processing instructions
87
-		 * to generate the result shown in the body of the frontend. The
88
-		 * configuration string is the path to the template file relative
89
-		 * to the templates directory (usually in client/html/templates).
90
-		 *
91
-		 * You can overwrite the template file configuration in extensions and
92
-		 * provide alternative templates. These alternative templates should be
93
-		 * named like the default one but with the string "standard" replaced by
94
-		 * an unique name. You may use the name of your project for this. If
95
-		 * you've implemented an alternative client class as well, "standard"
96
-		 * should be replaced by the name of the new class.
97
-		 *
98
-		 * @param string Relative path to the template creating code for the HTML page body
99
-		 * @since 2014.03
100
-		 * @category Developer
101
-		 * @see client/html/catalog/filter/tree/standard/template-header
102
-		 */
103
-		$tplconf = 'client/html/catalog/filter/tree/standard/template-body';
104
-		$default = 'catalog/filter/tree-body-default.php';
105
-
106
-		return $view->render( $view->config( $tplconf, $default ) );
107
-	}
108
-
109
-
110
-	/**
111
-	 * Returns the HTML string for insertion into the header.
112
-	 *
113
-	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
114
-	 * @param array &$tags Result array for the list of tags that are associated to the output
115
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
116
-	 * @return string|null String including HTML tags for the header on error
117
-	 */
118
-	public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
119
-	{
120
-		$view = $this->setViewParams( $this->getView(), $tags, $expire );
121
-
122
-		$html = '';
123
-		foreach( $this->getSubClients() as $subclient ) {
124
-			$html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
125
-		}
126
-		$view->treeHeader = $html;
127
-
128
-		/** client/html/catalog/filter/tree/standard/template-header
129
-		 * Relative path to the HTML header template of the catalog filter tree client.
130
-		 *
131
-		 * The template file contains the HTML code and processing instructions
132
-		 * to generate the HTML code that is inserted into the HTML page header
133
-		 * of the rendered page in the frontend. The configuration string is the
134
-		 * path to the template file relative to the templates directory (usually
135
-		 * in client/html/templates).
136
-		 *
137
-		 * You can overwrite the template file configuration in extensions and
138
-		 * provide alternative templates. These alternative templates should be
139
-		 * named like the default one but with the string "standard" replaced by
140
-		 * an unique name. You may use the name of your project for this. If
141
-		 * you've implemented an alternative client class as well, "standard"
142
-		 * should be replaced by the name of the new class.
143
-		 *
144
-		 * @param string Relative path to the template creating code for the HTML page head
145
-		 * @since 2014.03
146
-		 * @category Developer
147
-		 * @see client/html/catalog/filter/tree/standard/template-body
148
-		 */
149
-		$tplconf = 'client/html/catalog/filter/tree/standard/template-header';
150
-		$default = 'catalog/filter/tree-header-default.php';
151
-
152
-		return $view->render( $view->config( $tplconf, $default ) );
153
-	}
154
-
155
-
156
-	/**
157
-	 * Returns the sub-client given by its name.
158
-	 *
159
-	 * @param string $type Name of the client type
160
-	 * @param string|null $name Name of the sub-client (Default if null)
161
-	 * @return \Aimeos\Client\Html\Iface Sub-client object
162
-	 */
163
-	public function getSubClient( $type, $name = null )
164
-	{
165
-		/** client/html/catalog/filter/tree/decorators/excludes
166
-		 * Excludes decorators added by the "common" option from the catalog filter tree html client
167
-		 *
168
-		 * Decorators extend the functionality of a class by adding new aspects
169
-		 * (e.g. log what is currently done), executing the methods of the underlying
170
-		 * class only in certain conditions (e.g. only for logged in users) or
171
-		 * modify what is returned to the caller.
172
-		 *
173
-		 * This option allows you to remove a decorator added via
174
-		 * "client/html/common/decorators/default" before they are wrapped
175
-		 * around the html client.
176
-		 *
177
-		 *  client/html/catalog/filter/tree/decorators/excludes = array( 'decorator1' )
178
-		 *
179
-		 * This would remove the decorator named "decorator1" from the list of
180
-		 * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
181
-		 * "client/html/common/decorators/default" to the html client.
182
-		 *
183
-		 * @param array List of decorator names
184
-		 * @since 2015.08
185
-		 * @category Developer
186
-		 * @see client/html/common/decorators/default
187
-		 * @see client/html/catalog/filter/tree/decorators/global
188
-		 * @see client/html/catalog/filter/tree/decorators/local
189
-		 */
190
-
191
-		/** client/html/catalog/filter/tree/decorators/global
192
-		 * Adds a list of globally available decorators only to the catalog filter tree html client
193
-		 *
194
-		 * Decorators extend the functionality of a class by adding new aspects
195
-		 * (e.g. log what is currently done), executing the methods of the underlying
196
-		 * class only in certain conditions (e.g. only for logged in users) or
197
-		 * modify what is returned to the caller.
198
-		 *
199
-		 * This option allows you to wrap global decorators
200
-		 * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
201
-		 *
202
-		 *  client/html/catalog/filter/tree/decorators/global = array( 'decorator1' )
203
-		 *
204
-		 * This would add the decorator named "decorator1" defined by
205
-		 * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
206
-		 *
207
-		 * @param array List of decorator names
208
-		 * @since 2015.08
209
-		 * @category Developer
210
-		 * @see client/html/common/decorators/default
211
-		 * @see client/html/catalog/filter/tree/decorators/excludes
212
-		 * @see client/html/catalog/filter/tree/decorators/local
213
-		 */
214
-
215
-		/** client/html/catalog/filter/tree/decorators/local
216
-		 * Adds a list of local decorators only to the catalog filter tree html client
217
-		 *
218
-		 * Decorators extend the functionality of a class by adding new aspects
219
-		 * (e.g. log what is currently done), executing the methods of the underlying
220
-		 * class only in certain conditions (e.g. only for logged in users) or
221
-		 * modify what is returned to the caller.
222
-		 *
223
-		 * This option allows you to wrap local decorators
224
-		 * ("\Aimeos\Client\Html\Catalog\Decorator\*") around the html client.
225
-		 *
226
-		 *  client/html/catalog/filter/tree/decorators/local = array( 'decorator2' )
227
-		 *
228
-		 * This would add the decorator named "decorator2" defined by
229
-		 * "\Aimeos\Client\Html\Catalog\Decorator\Decorator2" only to the html client.
230
-		 *
231
-		 * @param array List of decorator names
232
-		 * @since 2015.08
233
-		 * @category Developer
234
-		 * @see client/html/common/decorators/default
235
-		 * @see client/html/catalog/filter/tree/decorators/excludes
236
-		 * @see client/html/catalog/filter/tree/decorators/global
237
-		 */
238
-
239
-		return $this->createSubClient( 'catalog/filter/tree/' . $type, $name );
240
-	}
241
-
242
-
243
-	/**
244
-	 * Returns the list of sub-client names configured for the client.
245
-	 *
246
-	 * @return array List of HTML client names
247
-	 */
248
-	protected function getSubClientNames()
249
-	{
250
-		return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
251
-	}
252
-
253
-
254
-	/**
255
-	 * Sets the necessary parameter values in the view.
256
-	 *
257
-	 * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
258
-	 * @param array &$tags Result array for the list of tags that are associated to the output
259
-	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
260
-	 * @return \Aimeos\MW\View\Iface Modified view object
261
-	 */
262
-	protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
263
-	{
264
-		if( !isset( $this->cache ) )
265
-		{
266
-			$catItems = array();
267
-			$context = $this->getContext();
268
-			$controller = \Aimeos\Controller\Frontend\Factory::createController( $context, 'catalog' );
269
-
270
-			$currentid = (string) $view->param( 'f_catid', '' );
271
-			$currentid = ( $currentid != '' ? $currentid : null );
272
-
273
-			/** client/html/catalog/filter/tree/startid
274
-			 * The ID of the category node that should be the root of the displayed category tree
275
-			 *
276
-			 * If you want to display only a part of your category tree, you can
277
-			 * configure the ID of the category node from which rendering the
278
-			 * remaining sub-tree should start.
279
-			 *
280
-			 * In most cases you can set this value via the administration interface
281
-			 * of the shop application. In that case you often can configure the
282
-			 * start ID individually for each catalog filter.
283
-			 *
284
-			 * @param string Category ID
285
-			 * @since 2014.03
286
-			 * @category User
287
-			 * @category Developer
288
-			 * @see client/html/catalog/filter/tree/levels-always
289
-			 * @see client/html/catalog/filter/tree/levels-only
290
-			 * @see client/html/catalog/filter/tree/domains
291
-			 */
292
-			$startid = $view->config( 'client/html/catalog/filter/tree/startid', '' );
293
-			$startid = ( $startid != '' ? $startid : null );
294
-
295
-			/** client/html/catalog/filter/tree/domains
296
-			 * List of domain names whose items should be fetched with the filter categories
297
-			 *
298
-			 * The templates rendering the categories in the catalog filter usually
299
-			 * add the images and texts associated to each item. If you want to
300
-			 * display additional content, you can configure your own list of
301
-			 * domains (attribute, media, price, product, text, etc. are domains)
302
-			 * whose items are fetched from the storage. Please keep in mind that
303
-			 * the more domains you add to the configuration, the more time is
304
-			 * required for fetching the content!
305
-			 *
306
-			 * @param array List of domain item names
307
-			 * @since 2014.03
308
-			 * @category Developer
309
-			 * @see client/html/catalog/filter/tree/startid
310
-			 * @see client/html/catalog/filter/tree/levels-always
311
-			 * @see client/html/catalog/filter/tree/levels-only
312
-			 */
313
-			$ref = $view->config( 'client/html/catalog/filter/tree/domains', array( 'text', 'media' ) );
314
-
315
-
316
-			if( $currentid !== null ) {
317
-				$catItems = $this->filterCatalogPath( $controller->getCatalogPath( $currentid ), $startid );
318
-			}
319
-
320
-			if( ( $node = reset( $catItems ) ) === false )
321
-			{
322
-				$node = $controller->getCatalogTree( $startid, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_ONE );
323
-				$catItems = array( $node->getId() => $node );
324
-			}
325
-
326
-
327
-			$catIds = array_keys( $catItems );
328
-			$search = $this->addSearchConditions( $controller->createCatalogFilter(), $catIds, $node->getId() );
329
-			$level = \Aimeos\MW\Tree\Manager\Base::LEVEL_TREE;
330
-
331
-			$view->treeCatalogPath = $catItems;
332
-			$view->treeCatalogTree = $controller->getCatalogTree( $startid, $ref, $level, $search );
333
-			$view->treeCatalogIds = $this->getCatalogIds( $view->treeCatalogTree, $catItems, $currentid );
334
-			$view->treeFilterParams = $this->getClientParams( $view->param(), array( 'f' ) );
335
-
336
-			$this->addMetaItemCatalog( $view->treeCatalogTree, $this->expire, $this->tags );
337
-
338
-			$this->cache = $view;
339
-		}
340
-
341
-		$expire = $this->expires( $this->expire, $expire );
342
-		$tags = array_merge( $tags, $this->tags );
343
-
344
-		return $this->cache;
345
-	}
346
-
347
-
348
-	/**
349
-	 * Adds the cache tags to the given list and sets a new expiration date if necessary based on the given catalog tree.
350
-	 *
351
-	 * @param \Aimeos\MShop\Catalog\Item\Iface $tree Tree node, maybe with sub-nodes
352
-	 * @param string|null &$expire Expiration date that will be overwritten if an earlier date is found
353
-	 * @param array &$tags List of tags the new tags will be added to
354
-	 */
355
-	protected function addMetaItemCatalog( \Aimeos\MShop\Catalog\Item\Iface $tree, &$expire, array &$tags = array() )
356
-	{
357
-		$this->addMetaItem( $tree, 'catalog', $expire, $tags );
358
-
359
-		foreach( $tree->getChildren() as $child ) {
360
-			$this->addMetaItemCatalog( $child, $expire, $tags );
361
-		}
362
-	}
363
-
364
-
365
-	/**
366
-	 * Adds the conditions for searching the catalog nodes
367
-	 *
368
-	 * @param \Aimeos\MW\Criteria\Iface $search Search object
369
-	 * @return \Aimeos\MW\Criteria\Iface Enhanced search object
370
-	 */
371
-	protected function addSearchConditions( \Aimeos\MW\Criteria\Iface $search, array $catIds, $catId )
372
-	{
373
-		$config = $this->getContext()->getConfig();
374
-
375
-		$expr = $search->compare( '==', 'catalog.parentid', $catIds );
376
-		$expr = $search->combine( '||', array( $expr, $search->compare( '==', 'catalog.id', $catId ) ) );
377
-
378
-		/** client/html/catalog/filter/tree/levels-always
379
-		 * The number of levels in the category tree that should be always displayed
380
-		 *
381
-		 * Usually, only the root node and the first level of the category
382
-		 * tree is shown in the frontend. Only if the user clicks on a
383
-		 * node in the first level, the page reloads and the sub-nodes of
384
-		 * the chosen category are rendered as well.
385
-		 *
386
-		 * Using this configuration option you can enforce the given number
387
-		 * of levels to be always displayed. The root node uses level 0, the
388
-		 * categories below level 1 and so on.
389
-		 *
390
-		 * In most cases you can set this value via the administration interface
391
-		 * of the shop application. In that case you often can configure the
392
-		 * levels individually for each catalog filter.
393
-		 *
394
-		 * @param integer Number of tree levels
395
-		 * @since 2014.03
396
-		 * @category User
397
-		 * @category Developer
398
-		 * @see client/html/catalog/filter/tree/startid
399
-		 * @see client/html/catalog/filter/tree/levels-only
400
-		 * @see client/html/catalog/filter/tree/domains
401
-		*/
402
-		if( ( $levels = $config->get( 'client/html/catalog/filter/tree/levels-always' ) ) != null ) {
403
-			$expr = $search->combine( '||', array( $expr, $search->compare( '<=', 'catalog.level', $levels ) ) );
404
-		}
405
-
406
-		/** client/html/catalog/filter/tree/levels-only
407
-		 * No more than this number of levels in the category tree should be displayed
408
-		 *
409
-		 * If the user clicks on a category node, the page reloads and the
410
-		 * sub-nodes of the chosen category are rendered as well.
411
-		 * Using this configuration option you can enforce that no more than
412
-		 * the given number of levels will be displayed at all. The root
413
-		 * node uses level 0, the categories below level 1 and so on.
414
-		 *
415
-		 * In most cases you can set this value via the administration interface
416
-		 * of the shop application. In that case you often can configure the
417
-		 * levels individually for each catalog filter.
418
-		 *
419
-		 * @param integer Number of tree levels
420
-		 * @since 2014.03
421
-		 * @category User
422
-		 * @category Developer
423
-		 * @see client/html/catalog/filter/tree/startid
424
-		 * @see client/html/catalog/filter/tree/levels-always
425
-		 * @see client/html/catalog/filter/tree/domains
426
-		 */
427
-		if( ( $levels = $config->get( 'client/html/catalog/filter/tree/levels-only' ) ) != null ) {
428
-			$expr = $search->combine( '&&', array( $expr, $search->compare( '<=', 'catalog.level', $levels ) ) );
429
-		}
430
-
431
-		$search->setConditions( $expr );
432
-
433
-		return $search;
434
-	}
435
-
436
-
437
-	/**
438
-	 * Returns the category IDs of the given catalog tree.
439
-	 *
440
-	 * Only the IDs of the children of the current category are returned.
441
-	 *
442
-	 * @param \Aimeos\MShop\Catalog\Item\Iface $tree Catalog node as entry point of the tree
443
-	 * @param array $path Associative list of category IDs as keys and the catalog
444
-	 * 	nodes from the currently selected category up to the root node
445
-	 * @param string $currentId Currently selected category
446
-	 * @return array List of category IDs
447
-	 */
448
-	protected function getCatalogIds( \Aimeos\MShop\Catalog\Item\Iface $tree, array $path, $currentId )
449
-	{
450
-		if( $tree->getId() == $currentId )
451
-		{
452
-			$ids = array();
453
-			foreach( $tree->getChildren() as $item ) {
454
-				$ids[] = $item->getId();
455
-			}
456
-
457
-			return $ids;
458
-		}
459
-
460
-		foreach( $tree->getChildren() as $child )
461
-		{
462
-			if( isset( $path[$child->getId()] ) ) {
463
-				return $this->getCatalogIds( $child, $path, $currentId );
464
-			}
465
-		}
466
-
467
-		return array();
468
-	}
469
-
470
-
471
-	/**
472
-	 * Filters the items in the path to the root of the catalog tree
473
-	 *
474
-	 * @param array $catItems Associative list of catalog IDs as keys and items implementing \Aimeos\Catalog\Item\Iface as values
475
-	 * @param string $startid Catalog ID the subtree starts from
476
-	 * @return array Filtered associative list of catalog items
477
-	 */
478
-	protected function filterCatalogPath( array $catItems, $startid )
479
-	{
480
-		if( $startid )
481
-		{
482
-			foreach( $catItems as $key => $item )
483
-			{
484
-				if( $key == $startid ) {
485
-					break;
486
-				}
487
-				unset( $catItems[$key] );
488
-			}
489
-		}
490
-
491
-		return $catItems;
492
-	}
25
+    /** client/html/catalog/filter/tree/standard/subparts
26
+     * List of HTML sub-clients rendered within the catalog filter tree 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/catalog/filter/tree/standard/subparts';
59
+    private $subPartNames = array();
60
+    private $tags = array();
61
+    private $expire;
62
+    private $cache;
63
+
64
+
65
+    /**
66
+     * Returns the HTML code for insertion into the body.
67
+     *
68
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
69
+     * @param array &$tags Result array for the list of tags that are associated to the output
70
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
71
+     * @return string HTML code
72
+     */
73
+    public function getBody( $uid = '', array &$tags = array(), &$expire = null )
74
+    {
75
+        $view = $this->setViewParams( $this->getView(), $tags, $expire );
76
+
77
+        $html = '';
78
+        foreach( $this->getSubClients() as $subclient ) {
79
+            $html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
80
+        }
81
+        $view->treeBody = $html;
82
+
83
+        /** client/html/catalog/filter/tree/standard/template-body
84
+         * Relative path to the HTML body template of the catalog filter tree client.
85
+         *
86
+         * The template file contains the HTML code and processing instructions
87
+         * to generate the result shown in the body of the frontend. The
88
+         * configuration string is the path to the template file relative
89
+         * to the templates directory (usually in client/html/templates).
90
+         *
91
+         * You can overwrite the template file configuration in extensions and
92
+         * provide alternative templates. These alternative templates should be
93
+         * named like the default one but with the string "standard" replaced by
94
+         * an unique name. You may use the name of your project for this. If
95
+         * you've implemented an alternative client class as well, "standard"
96
+         * should be replaced by the name of the new class.
97
+         *
98
+         * @param string Relative path to the template creating code for the HTML page body
99
+         * @since 2014.03
100
+         * @category Developer
101
+         * @see client/html/catalog/filter/tree/standard/template-header
102
+         */
103
+        $tplconf = 'client/html/catalog/filter/tree/standard/template-body';
104
+        $default = 'catalog/filter/tree-body-default.php';
105
+
106
+        return $view->render( $view->config( $tplconf, $default ) );
107
+    }
108
+
109
+
110
+    /**
111
+     * Returns the HTML string for insertion into the header.
112
+     *
113
+     * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
114
+     * @param array &$tags Result array for the list of tags that are associated to the output
115
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
116
+     * @return string|null String including HTML tags for the header on error
117
+     */
118
+    public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
119
+    {
120
+        $view = $this->setViewParams( $this->getView(), $tags, $expire );
121
+
122
+        $html = '';
123
+        foreach( $this->getSubClients() as $subclient ) {
124
+            $html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
125
+        }
126
+        $view->treeHeader = $html;
127
+
128
+        /** client/html/catalog/filter/tree/standard/template-header
129
+         * Relative path to the HTML header template of the catalog filter tree client.
130
+         *
131
+         * The template file contains the HTML code and processing instructions
132
+         * to generate the HTML code that is inserted into the HTML page header
133
+         * of the rendered page in the frontend. The configuration string is the
134
+         * path to the template file relative to the templates directory (usually
135
+         * in client/html/templates).
136
+         *
137
+         * You can overwrite the template file configuration in extensions and
138
+         * provide alternative templates. These alternative templates should be
139
+         * named like the default one but with the string "standard" replaced by
140
+         * an unique name. You may use the name of your project for this. If
141
+         * you've implemented an alternative client class as well, "standard"
142
+         * should be replaced by the name of the new class.
143
+         *
144
+         * @param string Relative path to the template creating code for the HTML page head
145
+         * @since 2014.03
146
+         * @category Developer
147
+         * @see client/html/catalog/filter/tree/standard/template-body
148
+         */
149
+        $tplconf = 'client/html/catalog/filter/tree/standard/template-header';
150
+        $default = 'catalog/filter/tree-header-default.php';
151
+
152
+        return $view->render( $view->config( $tplconf, $default ) );
153
+    }
154
+
155
+
156
+    /**
157
+     * Returns the sub-client given by its name.
158
+     *
159
+     * @param string $type Name of the client type
160
+     * @param string|null $name Name of the sub-client (Default if null)
161
+     * @return \Aimeos\Client\Html\Iface Sub-client object
162
+     */
163
+    public function getSubClient( $type, $name = null )
164
+    {
165
+        /** client/html/catalog/filter/tree/decorators/excludes
166
+         * Excludes decorators added by the "common" option from the catalog filter tree html client
167
+         *
168
+         * Decorators extend the functionality of a class by adding new aspects
169
+         * (e.g. log what is currently done), executing the methods of the underlying
170
+         * class only in certain conditions (e.g. only for logged in users) or
171
+         * modify what is returned to the caller.
172
+         *
173
+         * This option allows you to remove a decorator added via
174
+         * "client/html/common/decorators/default" before they are wrapped
175
+         * around the html client.
176
+         *
177
+         *  client/html/catalog/filter/tree/decorators/excludes = array( 'decorator1' )
178
+         *
179
+         * This would remove the decorator named "decorator1" from the list of
180
+         * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
181
+         * "client/html/common/decorators/default" to the html client.
182
+         *
183
+         * @param array List of decorator names
184
+         * @since 2015.08
185
+         * @category Developer
186
+         * @see client/html/common/decorators/default
187
+         * @see client/html/catalog/filter/tree/decorators/global
188
+         * @see client/html/catalog/filter/tree/decorators/local
189
+         */
190
+
191
+        /** client/html/catalog/filter/tree/decorators/global
192
+         * Adds a list of globally available decorators only to the catalog filter tree html client
193
+         *
194
+         * Decorators extend the functionality of a class by adding new aspects
195
+         * (e.g. log what is currently done), executing the methods of the underlying
196
+         * class only in certain conditions (e.g. only for logged in users) or
197
+         * modify what is returned to the caller.
198
+         *
199
+         * This option allows you to wrap global decorators
200
+         * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
201
+         *
202
+         *  client/html/catalog/filter/tree/decorators/global = array( 'decorator1' )
203
+         *
204
+         * This would add the decorator named "decorator1" defined by
205
+         * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
206
+         *
207
+         * @param array List of decorator names
208
+         * @since 2015.08
209
+         * @category Developer
210
+         * @see client/html/common/decorators/default
211
+         * @see client/html/catalog/filter/tree/decorators/excludes
212
+         * @see client/html/catalog/filter/tree/decorators/local
213
+         */
214
+
215
+        /** client/html/catalog/filter/tree/decorators/local
216
+         * Adds a list of local decorators only to the catalog filter tree html client
217
+         *
218
+         * Decorators extend the functionality of a class by adding new aspects
219
+         * (e.g. log what is currently done), executing the methods of the underlying
220
+         * class only in certain conditions (e.g. only for logged in users) or
221
+         * modify what is returned to the caller.
222
+         *
223
+         * This option allows you to wrap local decorators
224
+         * ("\Aimeos\Client\Html\Catalog\Decorator\*") around the html client.
225
+         *
226
+         *  client/html/catalog/filter/tree/decorators/local = array( 'decorator2' )
227
+         *
228
+         * This would add the decorator named "decorator2" defined by
229
+         * "\Aimeos\Client\Html\Catalog\Decorator\Decorator2" only to the html client.
230
+         *
231
+         * @param array List of decorator names
232
+         * @since 2015.08
233
+         * @category Developer
234
+         * @see client/html/common/decorators/default
235
+         * @see client/html/catalog/filter/tree/decorators/excludes
236
+         * @see client/html/catalog/filter/tree/decorators/global
237
+         */
238
+
239
+        return $this->createSubClient( 'catalog/filter/tree/' . $type, $name );
240
+    }
241
+
242
+
243
+    /**
244
+     * Returns the list of sub-client names configured for the client.
245
+     *
246
+     * @return array List of HTML client names
247
+     */
248
+    protected function getSubClientNames()
249
+    {
250
+        return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
251
+    }
252
+
253
+
254
+    /**
255
+     * Sets the necessary parameter values in the view.
256
+     *
257
+     * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
258
+     * @param array &$tags Result array for the list of tags that are associated to the output
259
+     * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
260
+     * @return \Aimeos\MW\View\Iface Modified view object
261
+     */
262
+    protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
263
+    {
264
+        if( !isset( $this->cache ) )
265
+        {
266
+            $catItems = array();
267
+            $context = $this->getContext();
268
+            $controller = \Aimeos\Controller\Frontend\Factory::createController( $context, 'catalog' );
269
+
270
+            $currentid = (string) $view->param( 'f_catid', '' );
271
+            $currentid = ( $currentid != '' ? $currentid : null );
272
+
273
+            /** client/html/catalog/filter/tree/startid
274
+             * The ID of the category node that should be the root of the displayed category tree
275
+             *
276
+             * If you want to display only a part of your category tree, you can
277
+             * configure the ID of the category node from which rendering the
278
+             * remaining sub-tree should start.
279
+             *
280
+             * In most cases you can set this value via the administration interface
281
+             * of the shop application. In that case you often can configure the
282
+             * start ID individually for each catalog filter.
283
+             *
284
+             * @param string Category ID
285
+             * @since 2014.03
286
+             * @category User
287
+             * @category Developer
288
+             * @see client/html/catalog/filter/tree/levels-always
289
+             * @see client/html/catalog/filter/tree/levels-only
290
+             * @see client/html/catalog/filter/tree/domains
291
+             */
292
+            $startid = $view->config( 'client/html/catalog/filter/tree/startid', '' );
293
+            $startid = ( $startid != '' ? $startid : null );
294
+
295
+            /** client/html/catalog/filter/tree/domains
296
+             * List of domain names whose items should be fetched with the filter categories
297
+             *
298
+             * The templates rendering the categories in the catalog filter usually
299
+             * add the images and texts associated to each item. If you want to
300
+             * display additional content, you can configure your own list of
301
+             * domains (attribute, media, price, product, text, etc. are domains)
302
+             * whose items are fetched from the storage. Please keep in mind that
303
+             * the more domains you add to the configuration, the more time is
304
+             * required for fetching the content!
305
+             *
306
+             * @param array List of domain item names
307
+             * @since 2014.03
308
+             * @category Developer
309
+             * @see client/html/catalog/filter/tree/startid
310
+             * @see client/html/catalog/filter/tree/levels-always
311
+             * @see client/html/catalog/filter/tree/levels-only
312
+             */
313
+            $ref = $view->config( 'client/html/catalog/filter/tree/domains', array( 'text', 'media' ) );
314
+
315
+
316
+            if( $currentid !== null ) {
317
+                $catItems = $this->filterCatalogPath( $controller->getCatalogPath( $currentid ), $startid );
318
+            }
319
+
320
+            if( ( $node = reset( $catItems ) ) === false )
321
+            {
322
+                $node = $controller->getCatalogTree( $startid, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_ONE );
323
+                $catItems = array( $node->getId() => $node );
324
+            }
325
+
326
+
327
+            $catIds = array_keys( $catItems );
328
+            $search = $this->addSearchConditions( $controller->createCatalogFilter(), $catIds, $node->getId() );
329
+            $level = \Aimeos\MW\Tree\Manager\Base::LEVEL_TREE;
330
+
331
+            $view->treeCatalogPath = $catItems;
332
+            $view->treeCatalogTree = $controller->getCatalogTree( $startid, $ref, $level, $search );
333
+            $view->treeCatalogIds = $this->getCatalogIds( $view->treeCatalogTree, $catItems, $currentid );
334
+            $view->treeFilterParams = $this->getClientParams( $view->param(), array( 'f' ) );
335
+
336
+            $this->addMetaItemCatalog( $view->treeCatalogTree, $this->expire, $this->tags );
337
+
338
+            $this->cache = $view;
339
+        }
340
+
341
+        $expire = $this->expires( $this->expire, $expire );
342
+        $tags = array_merge( $tags, $this->tags );
343
+
344
+        return $this->cache;
345
+    }
346
+
347
+
348
+    /**
349
+     * Adds the cache tags to the given list and sets a new expiration date if necessary based on the given catalog tree.
350
+     *
351
+     * @param \Aimeos\MShop\Catalog\Item\Iface $tree Tree node, maybe with sub-nodes
352
+     * @param string|null &$expire Expiration date that will be overwritten if an earlier date is found
353
+     * @param array &$tags List of tags the new tags will be added to
354
+     */
355
+    protected function addMetaItemCatalog( \Aimeos\MShop\Catalog\Item\Iface $tree, &$expire, array &$tags = array() )
356
+    {
357
+        $this->addMetaItem( $tree, 'catalog', $expire, $tags );
358
+
359
+        foreach( $tree->getChildren() as $child ) {
360
+            $this->addMetaItemCatalog( $child, $expire, $tags );
361
+        }
362
+    }
363
+
364
+
365
+    /**
366
+     * Adds the conditions for searching the catalog nodes
367
+     *
368
+     * @param \Aimeos\MW\Criteria\Iface $search Search object
369
+     * @return \Aimeos\MW\Criteria\Iface Enhanced search object
370
+     */
371
+    protected function addSearchConditions( \Aimeos\MW\Criteria\Iface $search, array $catIds, $catId )
372
+    {
373
+        $config = $this->getContext()->getConfig();
374
+
375
+        $expr = $search->compare( '==', 'catalog.parentid', $catIds );
376
+        $expr = $search->combine( '||', array( $expr, $search->compare( '==', 'catalog.id', $catId ) ) );
377
+
378
+        /** client/html/catalog/filter/tree/levels-always
379
+         * The number of levels in the category tree that should be always displayed
380
+         *
381
+         * Usually, only the root node and the first level of the category
382
+         * tree is shown in the frontend. Only if the user clicks on a
383
+         * node in the first level, the page reloads and the sub-nodes of
384
+         * the chosen category are rendered as well.
385
+         *
386
+         * Using this configuration option you can enforce the given number
387
+         * of levels to be always displayed. The root node uses level 0, the
388
+         * categories below level 1 and so on.
389
+         *
390
+         * In most cases you can set this value via the administration interface
391
+         * of the shop application. In that case you often can configure the
392
+         * levels individually for each catalog filter.
393
+         *
394
+         * @param integer Number of tree levels
395
+         * @since 2014.03
396
+         * @category User
397
+         * @category Developer
398
+         * @see client/html/catalog/filter/tree/startid
399
+         * @see client/html/catalog/filter/tree/levels-only
400
+         * @see client/html/catalog/filter/tree/domains
401
+         */
402
+        if( ( $levels = $config->get( 'client/html/catalog/filter/tree/levels-always' ) ) != null ) {
403
+            $expr = $search->combine( '||', array( $expr, $search->compare( '<=', 'catalog.level', $levels ) ) );
404
+        }
405
+
406
+        /** client/html/catalog/filter/tree/levels-only
407
+         * No more than this number of levels in the category tree should be displayed
408
+         *
409
+         * If the user clicks on a category node, the page reloads and the
410
+         * sub-nodes of the chosen category are rendered as well.
411
+         * Using this configuration option you can enforce that no more than
412
+         * the given number of levels will be displayed at all. The root
413
+         * node uses level 0, the categories below level 1 and so on.
414
+         *
415
+         * In most cases you can set this value via the administration interface
416
+         * of the shop application. In that case you often can configure the
417
+         * levels individually for each catalog filter.
418
+         *
419
+         * @param integer Number of tree levels
420
+         * @since 2014.03
421
+         * @category User
422
+         * @category Developer
423
+         * @see client/html/catalog/filter/tree/startid
424
+         * @see client/html/catalog/filter/tree/levels-always
425
+         * @see client/html/catalog/filter/tree/domains
426
+         */
427
+        if( ( $levels = $config->get( 'client/html/catalog/filter/tree/levels-only' ) ) != null ) {
428
+            $expr = $search->combine( '&&', array( $expr, $search->compare( '<=', 'catalog.level', $levels ) ) );
429
+        }
430
+
431
+        $search->setConditions( $expr );
432
+
433
+        return $search;
434
+    }
435
+
436
+
437
+    /**
438
+     * Returns the category IDs of the given catalog tree.
439
+     *
440
+     * Only the IDs of the children of the current category are returned.
441
+     *
442
+     * @param \Aimeos\MShop\Catalog\Item\Iface $tree Catalog node as entry point of the tree
443
+     * @param array $path Associative list of category IDs as keys and the catalog
444
+     * 	nodes from the currently selected category up to the root node
445
+     * @param string $currentId Currently selected category
446
+     * @return array List of category IDs
447
+     */
448
+    protected function getCatalogIds( \Aimeos\MShop\Catalog\Item\Iface $tree, array $path, $currentId )
449
+    {
450
+        if( $tree->getId() == $currentId )
451
+        {
452
+            $ids = array();
453
+            foreach( $tree->getChildren() as $item ) {
454
+                $ids[] = $item->getId();
455
+            }
456
+
457
+            return $ids;
458
+        }
459
+
460
+        foreach( $tree->getChildren() as $child )
461
+        {
462
+            if( isset( $path[$child->getId()] ) ) {
463
+                return $this->getCatalogIds( $child, $path, $currentId );
464
+            }
465
+        }
466
+
467
+        return array();
468
+    }
469
+
470
+
471
+    /**
472
+     * Filters the items in the path to the root of the catalog tree
473
+     *
474
+     * @param array $catItems Associative list of catalog IDs as keys and items implementing \Aimeos\Catalog\Item\Iface as values
475
+     * @param string $startid Catalog ID the subtree starts from
476
+     * @return array Filtered associative list of catalog items
477
+     */
478
+    protected function filterCatalogPath( array $catItems, $startid )
479
+    {
480
+        if( $startid )
481
+        {
482
+            foreach( $catItems as $key => $item )
483
+            {
484
+                if( $key == $startid ) {
485
+                    break;
486
+                }
487
+                unset( $catItems[$key] );
488
+            }
489
+        }
490
+
491
+        return $catItems;
492
+    }
493 493
 }
Please login to merge, or discard this patch.
Spacing   +57 added lines, -57 removed lines patch added patch discarded remove patch
@@ -70,13 +70,13 @@  discard block
 block discarded – undo
70 70
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
71 71
 	 * @return string HTML code
72 72
 	 */
73
-	public function getBody( $uid = '', array &$tags = array(), &$expire = null )
73
+	public function getBody($uid = '', array &$tags = array(), &$expire = null)
74 74
 	{
75
-		$view = $this->setViewParams( $this->getView(), $tags, $expire );
75
+		$view = $this->setViewParams($this->getView(), $tags, $expire);
76 76
 
77 77
 		$html = '';
78
-		foreach( $this->getSubClients() as $subclient ) {
79
-			$html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
78
+		foreach ($this->getSubClients() as $subclient) {
79
+			$html .= $subclient->setView($view)->getBody($uid, $tags, $expire);
80 80
 		}
81 81
 		$view->treeBody = $html;
82 82
 
@@ -103,7 +103,7 @@  discard block
 block discarded – undo
103 103
 		$tplconf = 'client/html/catalog/filter/tree/standard/template-body';
104 104
 		$default = 'catalog/filter/tree-body-default.php';
105 105
 
106
-		return $view->render( $view->config( $tplconf, $default ) );
106
+		return $view->render($view->config($tplconf, $default));
107 107
 	}
108 108
 
109 109
 
@@ -115,13 +115,13 @@  discard block
 block discarded – undo
115 115
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
116 116
 	 * @return string|null String including HTML tags for the header on error
117 117
 	 */
118
-	public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
118
+	public function getHeader($uid = '', array &$tags = array(), &$expire = null)
119 119
 	{
120
-		$view = $this->setViewParams( $this->getView(), $tags, $expire );
120
+		$view = $this->setViewParams($this->getView(), $tags, $expire);
121 121
 
122 122
 		$html = '';
123
-		foreach( $this->getSubClients() as $subclient ) {
124
-			$html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
123
+		foreach ($this->getSubClients() as $subclient) {
124
+			$html .= $subclient->setView($view)->getHeader($uid, $tags, $expire);
125 125
 		}
126 126
 		$view->treeHeader = $html;
127 127
 
@@ -149,7 +149,7 @@  discard block
 block discarded – undo
149 149
 		$tplconf = 'client/html/catalog/filter/tree/standard/template-header';
150 150
 		$default = 'catalog/filter/tree-header-default.php';
151 151
 
152
-		return $view->render( $view->config( $tplconf, $default ) );
152
+		return $view->render($view->config($tplconf, $default));
153 153
 	}
154 154
 
155 155
 
@@ -160,7 +160,7 @@  discard block
 block discarded – undo
160 160
 	 * @param string|null $name Name of the sub-client (Default if null)
161 161
 	 * @return \Aimeos\Client\Html\Iface Sub-client object
162 162
 	 */
163
-	public function getSubClient( $type, $name = null )
163
+	public function getSubClient($type, $name = null)
164 164
 	{
165 165
 		/** client/html/catalog/filter/tree/decorators/excludes
166 166
 		 * Excludes decorators added by the "common" option from the catalog filter tree html client
@@ -236,7 +236,7 @@  discard block
 block discarded – undo
236 236
 		 * @see client/html/catalog/filter/tree/decorators/global
237 237
 		 */
238 238
 
239
-		return $this->createSubClient( 'catalog/filter/tree/' . $type, $name );
239
+		return $this->createSubClient('catalog/filter/tree/'.$type, $name);
240 240
 	}
241 241
 
242 242
 
@@ -247,7 +247,7 @@  discard block
 block discarded – undo
247 247
 	 */
248 248
 	protected function getSubClientNames()
249 249
 	{
250
-		return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
250
+		return $this->getContext()->getConfig()->get($this->subPartPath, $this->subPartNames);
251 251
 	}
252 252
 
253 253
 
@@ -259,16 +259,16 @@  discard block
 block discarded – undo
259 259
 	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
260 260
 	 * @return \Aimeos\MW\View\Iface Modified view object
261 261
 	 */
262
-	protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
262
+	protected function setViewParams(\Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null)
263 263
 	{
264
-		if( !isset( $this->cache ) )
264
+		if (!isset($this->cache))
265 265
 		{
266 266
 			$catItems = array();
267 267
 			$context = $this->getContext();
268
-			$controller = \Aimeos\Controller\Frontend\Factory::createController( $context, 'catalog' );
268
+			$controller = \Aimeos\Controller\Frontend\Factory::createController($context, 'catalog');
269 269
 
270
-			$currentid = (string) $view->param( 'f_catid', '' );
271
-			$currentid = ( $currentid != '' ? $currentid : null );
270
+			$currentid = (string) $view->param('f_catid', '');
271
+			$currentid = ($currentid != '' ? $currentid : null);
272 272
 
273 273
 			/** client/html/catalog/filter/tree/startid
274 274
 			 * The ID of the category node that should be the root of the displayed category tree
@@ -289,8 +289,8 @@  discard block
 block discarded – undo
289 289
 			 * @see client/html/catalog/filter/tree/levels-only
290 290
 			 * @see client/html/catalog/filter/tree/domains
291 291
 			 */
292
-			$startid = $view->config( 'client/html/catalog/filter/tree/startid', '' );
293
-			$startid = ( $startid != '' ? $startid : null );
292
+			$startid = $view->config('client/html/catalog/filter/tree/startid', '');
293
+			$startid = ($startid != '' ? $startid : null);
294 294
 
295 295
 			/** client/html/catalog/filter/tree/domains
296 296
 			 * List of domain names whose items should be fetched with the filter categories
@@ -310,36 +310,36 @@  discard block
 block discarded – undo
310 310
 			 * @see client/html/catalog/filter/tree/levels-always
311 311
 			 * @see client/html/catalog/filter/tree/levels-only
312 312
 			 */
313
-			$ref = $view->config( 'client/html/catalog/filter/tree/domains', array( 'text', 'media' ) );
313
+			$ref = $view->config('client/html/catalog/filter/tree/domains', array('text', 'media'));
314 314
 
315 315
 
316
-			if( $currentid !== null ) {
317
-				$catItems = $this->filterCatalogPath( $controller->getCatalogPath( $currentid ), $startid );
316
+			if ($currentid !== null) {
317
+				$catItems = $this->filterCatalogPath($controller->getCatalogPath($currentid), $startid);
318 318
 			}
319 319
 
320
-			if( ( $node = reset( $catItems ) ) === false )
320
+			if (($node = reset($catItems)) === false)
321 321
 			{
322
-				$node = $controller->getCatalogTree( $startid, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_ONE );
323
-				$catItems = array( $node->getId() => $node );
322
+				$node = $controller->getCatalogTree($startid, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_ONE);
323
+				$catItems = array($node->getId() => $node);
324 324
 			}
325 325
 
326 326
 
327
-			$catIds = array_keys( $catItems );
328
-			$search = $this->addSearchConditions( $controller->createCatalogFilter(), $catIds, $node->getId() );
327
+			$catIds = array_keys($catItems);
328
+			$search = $this->addSearchConditions($controller->createCatalogFilter(), $catIds, $node->getId());
329 329
 			$level = \Aimeos\MW\Tree\Manager\Base::LEVEL_TREE;
330 330
 
331 331
 			$view->treeCatalogPath = $catItems;
332
-			$view->treeCatalogTree = $controller->getCatalogTree( $startid, $ref, $level, $search );
333
-			$view->treeCatalogIds = $this->getCatalogIds( $view->treeCatalogTree, $catItems, $currentid );
334
-			$view->treeFilterParams = $this->getClientParams( $view->param(), array( 'f' ) );
332
+			$view->treeCatalogTree = $controller->getCatalogTree($startid, $ref, $level, $search);
333
+			$view->treeCatalogIds = $this->getCatalogIds($view->treeCatalogTree, $catItems, $currentid);
334
+			$view->treeFilterParams = $this->getClientParams($view->param(), array('f'));
335 335
 
336
-			$this->addMetaItemCatalog( $view->treeCatalogTree, $this->expire, $this->tags );
336
+			$this->addMetaItemCatalog($view->treeCatalogTree, $this->expire, $this->tags);
337 337
 
338 338
 			$this->cache = $view;
339 339
 		}
340 340
 
341
-		$expire = $this->expires( $this->expire, $expire );
342
-		$tags = array_merge( $tags, $this->tags );
341
+		$expire = $this->expires($this->expire, $expire);
342
+		$tags = array_merge($tags, $this->tags);
343 343
 
344 344
 		return $this->cache;
345 345
 	}
@@ -352,12 +352,12 @@  discard block
 block discarded – undo
352 352
 	 * @param string|null &$expire Expiration date that will be overwritten if an earlier date is found
353 353
 	 * @param array &$tags List of tags the new tags will be added to
354 354
 	 */
355
-	protected function addMetaItemCatalog( \Aimeos\MShop\Catalog\Item\Iface $tree, &$expire, array &$tags = array() )
355
+	protected function addMetaItemCatalog(\Aimeos\MShop\Catalog\Item\Iface $tree, &$expire, array &$tags = array())
356 356
 	{
357
-		$this->addMetaItem( $tree, 'catalog', $expire, $tags );
357
+		$this->addMetaItem($tree, 'catalog', $expire, $tags);
358 358
 
359
-		foreach( $tree->getChildren() as $child ) {
360
-			$this->addMetaItemCatalog( $child, $expire, $tags );
359
+		foreach ($tree->getChildren() as $child) {
360
+			$this->addMetaItemCatalog($child, $expire, $tags);
361 361
 		}
362 362
 	}
363 363
 
@@ -368,12 +368,12 @@  discard block
 block discarded – undo
368 368
 	 * @param \Aimeos\MW\Criteria\Iface $search Search object
369 369
 	 * @return \Aimeos\MW\Criteria\Iface Enhanced search object
370 370
 	 */
371
-	protected function addSearchConditions( \Aimeos\MW\Criteria\Iface $search, array $catIds, $catId )
371
+	protected function addSearchConditions(\Aimeos\MW\Criteria\Iface $search, array $catIds, $catId)
372 372
 	{
373 373
 		$config = $this->getContext()->getConfig();
374 374
 
375
-		$expr = $search->compare( '==', 'catalog.parentid', $catIds );
376
-		$expr = $search->combine( '||', array( $expr, $search->compare( '==', 'catalog.id', $catId ) ) );
375
+		$expr = $search->compare('==', 'catalog.parentid', $catIds);
376
+		$expr = $search->combine('||', array($expr, $search->compare('==', 'catalog.id', $catId)));
377 377
 
378 378
 		/** client/html/catalog/filter/tree/levels-always
379 379
 		 * The number of levels in the category tree that should be always displayed
@@ -399,8 +399,8 @@  discard block
 block discarded – undo
399 399
 		 * @see client/html/catalog/filter/tree/levels-only
400 400
 		 * @see client/html/catalog/filter/tree/domains
401 401
 		*/
402
-		if( ( $levels = $config->get( 'client/html/catalog/filter/tree/levels-always' ) ) != null ) {
403
-			$expr = $search->combine( '||', array( $expr, $search->compare( '<=', 'catalog.level', $levels ) ) );
402
+		if (($levels = $config->get('client/html/catalog/filter/tree/levels-always')) != null) {
403
+			$expr = $search->combine('||', array($expr, $search->compare('<=', 'catalog.level', $levels)));
404 404
 		}
405 405
 
406 406
 		/** client/html/catalog/filter/tree/levels-only
@@ -424,11 +424,11 @@  discard block
 block discarded – undo
424 424
 		 * @see client/html/catalog/filter/tree/levels-always
425 425
 		 * @see client/html/catalog/filter/tree/domains
426 426
 		 */
427
-		if( ( $levels = $config->get( 'client/html/catalog/filter/tree/levels-only' ) ) != null ) {
428
-			$expr = $search->combine( '&&', array( $expr, $search->compare( '<=', 'catalog.level', $levels ) ) );
427
+		if (($levels = $config->get('client/html/catalog/filter/tree/levels-only')) != null) {
428
+			$expr = $search->combine('&&', array($expr, $search->compare('<=', 'catalog.level', $levels)));
429 429
 		}
430 430
 
431
-		$search->setConditions( $expr );
431
+		$search->setConditions($expr);
432 432
 
433 433
 		return $search;
434 434
 	}
@@ -445,22 +445,22 @@  discard block
 block discarded – undo
445 445
 	 * @param string $currentId Currently selected category
446 446
 	 * @return array List of category IDs
447 447
 	 */
448
-	protected function getCatalogIds( \Aimeos\MShop\Catalog\Item\Iface $tree, array $path, $currentId )
448
+	protected function getCatalogIds(\Aimeos\MShop\Catalog\Item\Iface $tree, array $path, $currentId)
449 449
 	{
450
-		if( $tree->getId() == $currentId )
450
+		if ($tree->getId() == $currentId)
451 451
 		{
452 452
 			$ids = array();
453
-			foreach( $tree->getChildren() as $item ) {
453
+			foreach ($tree->getChildren() as $item) {
454 454
 				$ids[] = $item->getId();
455 455
 			}
456 456
 
457 457
 			return $ids;
458 458
 		}
459 459
 
460
-		foreach( $tree->getChildren() as $child )
460
+		foreach ($tree->getChildren() as $child)
461 461
 		{
462
-			if( isset( $path[$child->getId()] ) ) {
463
-				return $this->getCatalogIds( $child, $path, $currentId );
462
+			if (isset($path[$child->getId()])) {
463
+				return $this->getCatalogIds($child, $path, $currentId);
464 464
 			}
465 465
 		}
466 466
 
@@ -475,16 +475,16 @@  discard block
 block discarded – undo
475 475
 	 * @param string $startid Catalog ID the subtree starts from
476 476
 	 * @return array Filtered associative list of catalog items
477 477
 	 */
478
-	protected function filterCatalogPath( array $catItems, $startid )
478
+	protected function filterCatalogPath(array $catItems, $startid)
479 479
 	{
480
-		if( $startid )
480
+		if ($startid)
481 481
 		{
482
-			foreach( $catItems as $key => $item )
482
+			foreach ($catItems as $key => $item)
483 483
 			{
484
-				if( $key == $startid ) {
484
+				if ($key == $startid) {
485 485
 					break;
486 486
 				}
487
-				unset( $catItems[$key] );
487
+				unset($catItems[$key]);
488 488
 			}
489 489
 		}
490 490
 
Please login to merge, or discard this patch.
client/html/tests/Client/Html/Catalog/Filter/Tree/StandardTest.php 2 patches
Indentation   +73 added lines, -73 removed lines patch added patch discarded remove patch
@@ -10,106 +10,106 @@
 block discarded – undo
10 10
  */
11 11
 class StandardTest extends \PHPUnit_Framework_TestCase
12 12
 {
13
-	private $context;
14
-	private $object;
13
+    private $context;
14
+    private $object;
15 15
 
16 16
 
17
-	protected function setUp()
18
-	{
19
-		$paths = \TestHelperHtml::getHtmlTemplatePaths();
20
-		$this->context = \TestHelperHtml::getContext();
17
+    protected function setUp()
18
+    {
19
+        $paths = \TestHelperHtml::getHtmlTemplatePaths();
20
+        $this->context = \TestHelperHtml::getContext();
21 21
 
22
-		$this->object = new \Aimeos\Client\Html\Catalog\Filter\Tree\Standard( $this->context, $paths );
23
-		$this->object->setView( \TestHelperHtml::getView() );
24
-	}
22
+        $this->object = new \Aimeos\Client\Html\Catalog\Filter\Tree\Standard( $this->context, $paths );
23
+        $this->object->setView( \TestHelperHtml::getView() );
24
+    }
25 25
 
26 26
 
27
-	protected function tearDown()
28
-	{
29
-		unset( $this->context, $this->object );
30
-	}
27
+    protected function tearDown()
28
+    {
29
+        unset( $this->context, $this->object );
30
+    }
31 31
 
32 32
 
33
-	public function testGetHeader()
34
-	{
35
-		$tags = array();
36
-		$expire = null;
37
-		$output = $this->object->getHeader( 1, $tags, $expire );
33
+    public function testGetHeader()
34
+    {
35
+        $tags = array();
36
+        $expire = null;
37
+        $output = $this->object->getHeader( 1, $tags, $expire );
38 38
 
39
-		$this->assertNotNull( $output );
40
-		$this->assertEquals( '2022-01-01 00:00:00', $expire );
41
-		$this->assertEquals( 3, count( $tags ) );
42
-	}
39
+        $this->assertNotNull( $output );
40
+        $this->assertEquals( '2022-01-01 00:00:00', $expire );
41
+        $this->assertEquals( 3, count( $tags ) );
42
+    }
43 43
 
44 44
 
45
-	public function testGetBody()
46
-	{
47
-		$catalogManager = \Aimeos\MShop\Catalog\Manager\Factory::createManager( \TestHelperHtml::getContext() );
48
-		$node = $catalogManager->getTree( null, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_LIST );
45
+    public function testGetBody()
46
+    {
47
+        $catalogManager = \Aimeos\MShop\Catalog\Manager\Factory::createManager( \TestHelperHtml::getContext() );
48
+        $node = $catalogManager->getTree( null, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_LIST );
49 49
 
50
-		$view = $this->object->getView();
51
-		$helper = new \Aimeos\MW\View\Helper\Param\Standard( $view, array( 'f_catid' => $node->getChild( 1 )->getId() ) );
52
-		$view->addHelper( 'param', $helper );
50
+        $view = $this->object->getView();
51
+        $helper = new \Aimeos\MW\View\Helper\Param\Standard( $view, array( 'f_catid' => $node->getChild( 1 )->getId() ) );
52
+        $view->addHelper( 'param', $helper );
53 53
 
54
-		$tags = array();
55
-		$expire = null;
56
-		$output = $this->object->getBody( 1, $tags, $expire );
54
+        $tags = array();
55
+        $expire = null;
56
+        $output = $this->object->getBody( 1, $tags, $expire );
57 57
 
58
-		$this->assertContains( 'Groups', $output );
59
-		$this->assertContains( 'Neu', $output );
60
-		$this->assertContains( 'level-2', $output );
58
+        $this->assertContains( 'Groups', $output );
59
+        $this->assertContains( 'Neu', $output );
60
+        $this->assertContains( 'level-2', $output );
61 61
 
62
-		$this->assertEquals( '2022-01-01 00:00:00', $expire );
63
-		$this->assertEquals( 5, count( $tags ) );
64
-	}
62
+        $this->assertEquals( '2022-01-01 00:00:00', $expire );
63
+        $this->assertEquals( 5, count( $tags ) );
64
+    }
65 65
 
66 66
 
67
-	public function testGetBodyLevelsAlways()
68
-	{
69
-		$catalogManager = \Aimeos\MShop\Catalog\Manager\Factory::createManager( \TestHelperHtml::getContext() );
70
-		$node = $catalogManager->getTree( null, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_ONE );
67
+    public function testGetBodyLevelsAlways()
68
+    {
69
+        $catalogManager = \Aimeos\MShop\Catalog\Manager\Factory::createManager( \TestHelperHtml::getContext() );
70
+        $node = $catalogManager->getTree( null, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_ONE );
71 71
 
72
-		$this->context->getConfig()->set( 'client/html/catalog/filter/tree/levels-always', 2 );
72
+        $this->context->getConfig()->set( 'client/html/catalog/filter/tree/levels-always', 2 );
73 73
 
74
-		$view = $this->object->getView();
75
-		$helper = new \Aimeos\MW\View\Helper\Param\Standard( $view, array( 'f_catid' => $node->getId() ) );
76
-		$view->addHelper( 'param', $helper );
74
+        $view = $this->object->getView();
75
+        $helper = new \Aimeos\MW\View\Helper\Param\Standard( $view, array( 'f_catid' => $node->getId() ) );
76
+        $view->addHelper( 'param', $helper );
77 77
 
78
-		$tags = array();
79
-		$expire = null;
80
-		$output = $this->object->getBody( 1, $tags, $expire );
78
+        $tags = array();
79
+        $expire = null;
80
+        $output = $this->object->getBody( 1, $tags, $expire );
81 81
 
82
-		$this->assertContains( 'level-2', $output );
83
-		$this->assertEquals( '2022-01-01 00:00:00', $expire );
84
-		$this->assertEquals( 8, count( $tags ) );
85
-	}
82
+        $this->assertContains( 'level-2', $output );
83
+        $this->assertEquals( '2022-01-01 00:00:00', $expire );
84
+        $this->assertEquals( 8, count( $tags ) );
85
+    }
86 86
 
87 87
 
88
-	public function testGetBodyLevelsOnly()
89
-	{
90
-		$catalogManager = \Aimeos\MShop\Catalog\Manager\Factory::createManager( \TestHelperHtml::getContext() );
91
-		$node = $catalogManager->getTree( null, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_TREE );
88
+    public function testGetBodyLevelsOnly()
89
+    {
90
+        $catalogManager = \Aimeos\MShop\Catalog\Manager\Factory::createManager( \TestHelperHtml::getContext() );
91
+        $node = $catalogManager->getTree( null, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_TREE );
92 92
 
93
-		$this->context->getConfig()->set( 'client/html/catalog/filter/tree/levels-only', 1 );
93
+        $this->context->getConfig()->set( 'client/html/catalog/filter/tree/levels-only', 1 );
94 94
 
95
-		$view = $this->object->getView();
96
-		$helper = new \Aimeos\MW\View\Helper\Param\Standard( $view, array( 'f_catid' => $node->getChild( 0 )->getId() ) );
97
-		$view->addHelper( 'param', $helper );
95
+        $view = $this->object->getView();
96
+        $helper = new \Aimeos\MW\View\Helper\Param\Standard( $view, array( 'f_catid' => $node->getChild( 0 )->getId() ) );
97
+        $view->addHelper( 'param', $helper );
98 98
 
99
-		$tags = array();
100
-		$expire = null;
101
-		$output = $this->object->getBody( 1, $tags, $expire );
99
+        $tags = array();
100
+        $expire = null;
101
+        $output = $this->object->getBody( 1, $tags, $expire );
102 102
 
103
-		$this->assertNotContains( 'level-2', $output );
104
-		$this->assertEquals( '2022-01-01 00:00:00', $expire );
105
-		$this->assertEquals( 3, count( $tags ) );
106
-	}
103
+        $this->assertNotContains( 'level-2', $output );
104
+        $this->assertEquals( '2022-01-01 00:00:00', $expire );
105
+        $this->assertEquals( 3, count( $tags ) );
106
+    }
107 107
 
108 108
 
109
-	public function testGetSubClient()
110
-	{
111
-		$this->setExpectedException( '\\Aimeos\\Client\\Html\\Exception' );
112
-		$this->object->getSubClient( 'invalid', 'invalid' );
113
-	}
109
+    public function testGetSubClient()
110
+    {
111
+        $this->setExpectedException( '\\Aimeos\\Client\\Html\\Exception' );
112
+        $this->object->getSubClient( 'invalid', 'invalid' );
113
+    }
114 114
 
115 115
 }
Please login to merge, or discard this patch.
Spacing   +37 added lines, -37 removed lines patch added patch discarded remove patch
@@ -19,14 +19,14 @@  discard block
 block discarded – undo
19 19
 		$paths = \TestHelperHtml::getHtmlTemplatePaths();
20 20
 		$this->context = \TestHelperHtml::getContext();
21 21
 
22
-		$this->object = new \Aimeos\Client\Html\Catalog\Filter\Tree\Standard( $this->context, $paths );
23
-		$this->object->setView( \TestHelperHtml::getView() );
22
+		$this->object = new \Aimeos\Client\Html\Catalog\Filter\Tree\Standard($this->context, $paths);
23
+		$this->object->setView(\TestHelperHtml::getView());
24 24
 	}
25 25
 
26 26
 
27 27
 	protected function tearDown()
28 28
 	{
29
-		unset( $this->context, $this->object );
29
+		unset($this->context, $this->object);
30 30
 	}
31 31
 
32 32
 
@@ -34,82 +34,82 @@  discard block
 block discarded – undo
34 34
 	{
35 35
 		$tags = array();
36 36
 		$expire = null;
37
-		$output = $this->object->getHeader( 1, $tags, $expire );
37
+		$output = $this->object->getHeader(1, $tags, $expire);
38 38
 
39
-		$this->assertNotNull( $output );
40
-		$this->assertEquals( '2022-01-01 00:00:00', $expire );
41
-		$this->assertEquals( 3, count( $tags ) );
39
+		$this->assertNotNull($output);
40
+		$this->assertEquals('2022-01-01 00:00:00', $expire);
41
+		$this->assertEquals(3, count($tags));
42 42
 	}
43 43
 
44 44
 
45 45
 	public function testGetBody()
46 46
 	{
47
-		$catalogManager = \Aimeos\MShop\Catalog\Manager\Factory::createManager( \TestHelperHtml::getContext() );
48
-		$node = $catalogManager->getTree( null, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_LIST );
47
+		$catalogManager = \Aimeos\MShop\Catalog\Manager\Factory::createManager(\TestHelperHtml::getContext());
48
+		$node = $catalogManager->getTree(null, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_LIST);
49 49
 
50 50
 		$view = $this->object->getView();
51
-		$helper = new \Aimeos\MW\View\Helper\Param\Standard( $view, array( 'f_catid' => $node->getChild( 1 )->getId() ) );
52
-		$view->addHelper( 'param', $helper );
51
+		$helper = new \Aimeos\MW\View\Helper\Param\Standard($view, array('f_catid' => $node->getChild(1)->getId()));
52
+		$view->addHelper('param', $helper);
53 53
 
54 54
 		$tags = array();
55 55
 		$expire = null;
56
-		$output = $this->object->getBody( 1, $tags, $expire );
56
+		$output = $this->object->getBody(1, $tags, $expire);
57 57
 
58
-		$this->assertContains( 'Groups', $output );
59
-		$this->assertContains( 'Neu', $output );
60
-		$this->assertContains( 'level-2', $output );
58
+		$this->assertContains('Groups', $output);
59
+		$this->assertContains('Neu', $output);
60
+		$this->assertContains('level-2', $output);
61 61
 
62
-		$this->assertEquals( '2022-01-01 00:00:00', $expire );
63
-		$this->assertEquals( 5, count( $tags ) );
62
+		$this->assertEquals('2022-01-01 00:00:00', $expire);
63
+		$this->assertEquals(5, count($tags));
64 64
 	}
65 65
 
66 66
 
67 67
 	public function testGetBodyLevelsAlways()
68 68
 	{
69
-		$catalogManager = \Aimeos\MShop\Catalog\Manager\Factory::createManager( \TestHelperHtml::getContext() );
70
-		$node = $catalogManager->getTree( null, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_ONE );
69
+		$catalogManager = \Aimeos\MShop\Catalog\Manager\Factory::createManager(\TestHelperHtml::getContext());
70
+		$node = $catalogManager->getTree(null, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_ONE);
71 71
 
72
-		$this->context->getConfig()->set( 'client/html/catalog/filter/tree/levels-always', 2 );
72
+		$this->context->getConfig()->set('client/html/catalog/filter/tree/levels-always', 2);
73 73
 
74 74
 		$view = $this->object->getView();
75
-		$helper = new \Aimeos\MW\View\Helper\Param\Standard( $view, array( 'f_catid' => $node->getId() ) );
76
-		$view->addHelper( 'param', $helper );
75
+		$helper = new \Aimeos\MW\View\Helper\Param\Standard($view, array('f_catid' => $node->getId()));
76
+		$view->addHelper('param', $helper);
77 77
 
78 78
 		$tags = array();
79 79
 		$expire = null;
80
-		$output = $this->object->getBody( 1, $tags, $expire );
80
+		$output = $this->object->getBody(1, $tags, $expire);
81 81
 
82
-		$this->assertContains( 'level-2', $output );
83
-		$this->assertEquals( '2022-01-01 00:00:00', $expire );
84
-		$this->assertEquals( 8, count( $tags ) );
82
+		$this->assertContains('level-2', $output);
83
+		$this->assertEquals('2022-01-01 00:00:00', $expire);
84
+		$this->assertEquals(8, count($tags));
85 85
 	}
86 86
 
87 87
 
88 88
 	public function testGetBodyLevelsOnly()
89 89
 	{
90
-		$catalogManager = \Aimeos\MShop\Catalog\Manager\Factory::createManager( \TestHelperHtml::getContext() );
91
-		$node = $catalogManager->getTree( null, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_TREE );
90
+		$catalogManager = \Aimeos\MShop\Catalog\Manager\Factory::createManager(\TestHelperHtml::getContext());
91
+		$node = $catalogManager->getTree(null, array(), \Aimeos\MW\Tree\Manager\Base::LEVEL_TREE);
92 92
 
93
-		$this->context->getConfig()->set( 'client/html/catalog/filter/tree/levels-only', 1 );
93
+		$this->context->getConfig()->set('client/html/catalog/filter/tree/levels-only', 1);
94 94
 
95 95
 		$view = $this->object->getView();
96
-		$helper = new \Aimeos\MW\View\Helper\Param\Standard( $view, array( 'f_catid' => $node->getChild( 0 )->getId() ) );
97
-		$view->addHelper( 'param', $helper );
96
+		$helper = new \Aimeos\MW\View\Helper\Param\Standard($view, array('f_catid' => $node->getChild(0)->getId()));
97
+		$view->addHelper('param', $helper);
98 98
 
99 99
 		$tags = array();
100 100
 		$expire = null;
101
-		$output = $this->object->getBody( 1, $tags, $expire );
101
+		$output = $this->object->getBody(1, $tags, $expire);
102 102
 
103
-		$this->assertNotContains( 'level-2', $output );
104
-		$this->assertEquals( '2022-01-01 00:00:00', $expire );
105
-		$this->assertEquals( 3, count( $tags ) );
103
+		$this->assertNotContains('level-2', $output);
104
+		$this->assertEquals('2022-01-01 00:00:00', $expire);
105
+		$this->assertEquals(3, count($tags));
106 106
 	}
107 107
 
108 108
 
109 109
 	public function testGetSubClient()
110 110
 	{
111
-		$this->setExpectedException( '\\Aimeos\\Client\\Html\\Exception' );
112
-		$this->object->getSubClient( 'invalid', 'invalid' );
111
+		$this->setExpectedException('\\Aimeos\\Client\\Html\\Exception');
112
+		$this->object->getSubClient('invalid', 'invalid');
113 113
 	}
114 114
 
115 115
 }
Please login to merge, or discard this patch.