Passed
Push — master ( eb83f5...a05d1e )
by Aimeos
04:09
created

Standard::getAddressStrings()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 48
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 23
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 48
rs 9.552
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2020
6
 * @package Client
7
 * @subpackage Html
8
 */
9
10
11
namespace Aimeos\Client\Html\Supplier\Detail;
12
13
14
/**
15
 * Default implementation of supplier detail section HTML clients.
16
 *
17
 * @package Client
18
 * @subpackage Html
19
 */
20
class Standard
21
	extends \Aimeos\Client\Html\Common\Client\Factory\Base
22
	implements \Aimeos\Client\Html\Common\Client\Factory\Iface
23
{
24
	/** client/html/supplier/detail/subparts
25
	 * List of HTML sub-clients rendered within the supplier detail section
26
	 *
27
	 * The output of the frontend is composed of the code generated by the HTML
28
	 * clients. Each HTML client can consist of serveral (or none) sub-clients
29
	 * that are responsible for rendering certain sub-parts of the output. The
30
	 * sub-clients can contain HTML clients themselves and therefore a
31
	 * hierarchical tree of HTML clients is composed. Each HTML client creates
32
	 * the output that is placed inside the container of its parent.
33
	 *
34
	 * At first, always the HTML code generated by the parent is printed, then
35
	 * the HTML code of its sub-clients. The order of the HTML sub-clients
36
	 * determines the order of the output of these sub-clients inside the parent
37
	 * container. If the configured list of clients is
38
	 *
39
	 *  array( "subclient1", "subclient2" )
40
	 *
41
	 * you can easily change the order of the output by reordering the subparts:
42
	 *
43
	 *  client/html/<clients>/subparts = array( "subclient1", "subclient2" )
44
	 *
45
	 * You can also remove one or more parts if they shouldn't be rendered:
46
	 *
47
	 *  client/html/<clients>/subparts = array( "subclient1" )
48
	 *
49
	 * As the clients only generates structural HTML, the layout defined via CSS
50
	 * should support adding, removing or reordering content by a fluid like
51
	 * design.
52
	 *
53
	 * @param array List of sub-client names
54
	 * @since 2020.10
55
	 * @category Developer
56
	 */
57
	private $subPartPath = 'client/html/supplier/detail/subparts';
58
59
	/** client/html/supplier/detail/navigator/name
60
	 * Name of the navigator part used by the supplier detail client implementation
61
	 *
62
	 * Use "Myname" if your class is named "\Aimeos\Client\Html\Supplier\Detail\Breadcrumb\Myname".
63
	 * The name is case-sensitive and you should avoid camel case names like "MyName".
64
	 *
65
	 * @param string Last part of the client class name
66
	 * @since 2014.09
67
	 * @category Developer
68
	 */
69
	private $subPartNames = [];
70
71
	private $tags = [];
72
	private $expire;
73
	private $view;
74
75
76
	/**
77
	 * Returns the HTML code for insertion into the body.
78
	 *
79
	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
80
	 * @return string HTML code
81
	 */
82
	public function getBody( string $uid = '' ) : string
83
	{
84
		$prefixes = ['f_supid'];
85
		$context = $this->getContext();
86
87
		/** client/html/supplier/detail/cache
88
		 * Enables or disables caching only for the supplier detail component
89
		 *
90
		 * Disable caching for components can be useful if you would have too much
91
		 * entries to cache or if the component contains non-cacheable parts that
92
		 * can't be replaced using the modifyBody() and modifyHeader() methods.
93
		 *
94
		 * @param boolean True to enable caching, false to disable
95
		 * @category Developer
96
		 * @category User
97
		 * @see client/html/supplier/detail/cache
98
		 * @see client/html/supplier/filter/cache
99
		 * @see client/html/supplier/lists/cache
100
		 */
101
102
		/** client/html/supplier/detail
103
		 * All parameters defined for the supplier detail component and its subparts
104
		 *
105
		 * This returns all settings related to the detail component.
106
		 * Please refer to the single settings for details.
107
		 *
108
		 * @param array Associative list of name/value settings
109
		 * @category Developer
110
		 * @see client/html/supplier#detail
111
		 */
112
		$confkey = 'client/html/supplier/detail';
113
114
		if( ( $html = $this->getCached( 'body', $uid, $prefixes, $confkey ) ) === null )
115
		{
116
			$view = $this->getView();
117
118
			/** client/html/supplier/detail/template-body
119
			 * Relative path to the HTML body template of the supplier detail client.
120
			 *
121
			 * The template file contains the HTML code and processing instructions
122
			 * to generate the result shown in the body of the frontend. The
123
			 * configuration string is the path to the template file relative
124
			 * to the templates directory (usually in client/html/templates).
125
			 *
126
			 * You can overwrite the template file configuration in extensions and
127
			 * provide alternative templates. These alternative templates should be
128
			 * named like the default one but with the string "standard" replaced by
129
			 * an unique name. You may use the name of your project for this. If
130
			 * you've implemented an alternative client class as well, "standard"
131
			 * should be replaced by the name of the new class.
132
			 *
133
			 * @param string Relative path to the template creating code for the HTML page body
134
			 * @since 2020.10
135
			 * @category Developer
136
			 * @see client/html/supplier/detail/template-header
137
			 */
138
			$tplconf = 'client/html/supplier/detail/template-body';
139
			$default = 'supplier/detail/body-standard';
140
141
			try
142
			{
143
				$html = '';
144
145
				if( !isset( $this->view ) ) {
146
					$view = $this->view = $this->getObject()->addData( $view, $this->tags, $this->expire );
147
				}
148
149
				foreach( $this->getSubClients() as $subclient ) {
150
					$html .= $subclient->setView( $view )->getBody( $uid );
151
				}
152
				$view->detailBody = $html;
153
154
				$html = $view->render( $view->config( $tplconf, $default ) );
155
				$this->setCached( 'body', $uid, $prefixes, $confkey, $html, $this->tags, $this->expire );
156
157
				return $html;
158
			}
159
			catch( \Aimeos\Client\Html\Exception $e )
160
			{
161
				$error = array( $context->getI18n()->dt( 'client', $e->getMessage() ) );
162
				$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
163
			}
164
			catch( \Aimeos\Controller\Frontend\Exception $e )
165
			{
166
				$error = array( $context->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
167
				$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
168
			}
169
			catch( \Aimeos\MShop\Exception $e )
170
			{
171
				$error = array( $context->getI18n()->dt( 'mshop', $e->getMessage() ) );
172
				$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
173
			}
174
			catch( \Exception $e )
175
			{
176
				$error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
177
				$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
178
				$this->logException( $e );
179
			}
180
181
			$html = $view->render( $view->config( $tplconf, $default ) );
182
		}
183
		else
184
		{
185
			$html = $this->modifyBody( $html, $uid );
186
		}
187
188
		return $html;
189
	}
190
191
192
	/**
193
	 * Returns the HTML string for insertion into the header.
194
	 *
195
	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
196
	 * @return string|null String including HTML tags for the header on error
197
	 */
198
	public function getHeader( string $uid = '' ) : ?string
199
	{
200
		$prefixes = ['f_supid'];
201
		$confkey = 'client/html/supplier/detail';
202
203
		if( ( $html = $this->getCached( 'header', $uid, $prefixes, $confkey ) ) === null )
204
		{
205
			$view = $this->getView();
206
207
			/** client/html/supplier/detail/template-header
208
			 * Relative path to the HTML header template of the supplier detail client.
209
			 *
210
			 * The template file contains the HTML code and processing instructions
211
			 * to generate the HTML code that is inserted into the HTML page header
212
			 * of the rendered page in the frontend. The configuration string is the
213
			 * path to the template file relative to the templates directory (usually
214
			 * in client/html/templates).
215
			 *
216
			 * You can overwrite the template file configuration in extensions and
217
			 * provide alternative templates. These alternative templates should be
218
			 * named like the default one but with the string "standard" replaced by
219
			 * an unique name. You may use the name of your project for this. If
220
			 * you've implemented an alternative client class as well, "standard"
221
			 * should be replaced by the name of the new class.
222
			 *
223
			 * @param string Relative path to the template creating code for the HTML page head
224
			 * @since 2020.10
225
			 * @category Developer
226
			 * @see client/html/supplier/detail/template-body
227
			 */
228
			$tplconf = 'client/html/supplier/detail/template-header';
229
			$default = 'supplier/detail/header-standard';
230
231
			try
232
			{
233
				$html = '';
234
235
				if( !isset( $this->view ) ) {
236
					$view = $this->view = $this->getObject()->addData( $view, $this->tags, $this->expire );
237
				}
238
239
				foreach( $this->getSubClients() as $subclient ) {
240
					$html .= $subclient->setView( $view )->getHeader( $uid );
241
				}
242
				$view->detailHeader = $html;
243
244
				$html = $view->render( $view->config( $tplconf, $default ) );
245
				$this->setCached( 'header', $uid, $prefixes, $confkey, $html, $this->tags, $this->expire );
246
247
				return $html;
248
			}
249
			catch( \Exception $e )
250
			{
251
				$this->logException( $e );
252
			}
253
		}
254
		else
255
		{
256
			$html = $this->modifyHeader( $html, $uid );
257
		}
258
259
		return $html;
260
	}
261
262
263
	/**
264
	 * Returns the sub-client given by its name.
265
	 *
266
	 * @param string $type Name of the client type
267
	 * @param string|null $name Name of the sub-client (Default if null)
268
	 * @return \Aimeos\Client\Html\Iface Sub-client object
269
	 */
270
	public function getSubClient( string $type, string $name = null ) : \Aimeos\Client\Html\Iface
271
	{
272
		/** client/html/supplier/detail/decorators/excludes
273
		 * Excludes decorators added by the "common" option from the supplier detail html client
274
		 *
275
		 * Decorators extend the functionality of a class by adding new aspects
276
		 * (e.g. log what is currently done), executing the methods of the underlying
277
		 * class only in certain conditions (e.g. only for logged in users) or
278
		 * modify what is returned to the caller.
279
		 *
280
		 * This option allows you to remove a decorator added via
281
		 * "client/html/common/decorators/default" before they are wrapped
282
		 * around the html client.
283
		 *
284
		 *  client/html/supplier/detail/decorators/excludes = array( 'decorator1' )
285
		 *
286
		 * This would remove the decorator named "decorator1" from the list of
287
		 * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
288
		 * "client/html/common/decorators/default" to the html client.
289
		 *
290
		 * @param array List of decorator names
291
		 * @since 2020.10
292
		 * @category Developer
293
		 * @see client/html/common/decorators/default
294
		 * @see client/html/supplier/detail/decorators/global
295
		 * @see client/html/supplier/detail/decorators/local
296
		 */
297
298
		/** client/html/supplier/detail/decorators/global
299
		 * Adds a list of globally available decorators only to the supplier detail html client
300
		 *
301
		 * Decorators extend the functionality of a class by adding new aspects
302
		 * (e.g. log what is currently done), executing the methods of the underlying
303
		 * class only in certain conditions (e.g. only for logged in users) or
304
		 * modify what is returned to the caller.
305
		 *
306
		 * This option allows you to wrap global decorators
307
		 * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
308
		 *
309
		 *  client/html/supplier/detail/decorators/global = array( 'decorator1' )
310
		 *
311
		 * This would add the decorator named "decorator1" defined by
312
		 * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
313
		 *
314
		 * @param array List of decorator names
315
		 * @since 2020.10
316
		 * @category Developer
317
		 * @see client/html/common/decorators/default
318
		 * @see client/html/supplier/detail/decorators/excludes
319
		 * @see client/html/supplier/detail/decorators/local
320
		 */
321
322
		/** client/html/supplier/detail/decorators/local
323
		 * Adds a list of local decorators only to the supplier detail html client
324
		 *
325
		 * Decorators extend the functionality of a class by adding new aspects
326
		 * (e.g. log what is currently done), executing the methods of the underlying
327
		 * class only in certain conditions (e.g. only for logged in users) or
328
		 * modify what is returned to the caller.
329
		 *
330
		 * This option allows you to wrap local decorators
331
		 * ("\Aimeos\Client\Html\Supplier\Decorator\*") around the html client.
332
		 *
333
		 *  client/html/supplier/detail/decorators/local = array( 'decorator2' )
334
		 *
335
		 * This would add the decorator named "decorator2" defined by
336
		 * "\Aimeos\Client\Html\Supplier\Decorator\Decorator2" only to the html client.
337
		 *
338
		 * @param array List of decorator names
339
		 * @since 2020.10
340
		 * @category Developer
341
		 * @see client/html/common/decorators/default
342
		 * @see client/html/supplier/detail/decorators/excludes
343
		 * @see client/html/supplier/detail/decorators/global
344
		 */
345
		return $this->createSubClient( 'supplier/detail/' . $type, $name );
346
	}
347
348
349
	/**
350
	 * Processes the input, e.g. store given values.
351
	 *
352
	 * A view must be available and this method doesn't generate any output
353
	 * besides setting view variables if necessary.
354
	 */
355
	public function process()
356
	{
357
		$view = $this->getView();
358
		$context = $this->getContext();
359
360
		try
361
		{
362
			parent::process();
363
		}
364
		catch( \Aimeos\Client\Html\Exception $e )
365
		{
366
			$error = array( $context->getI18n()->dt( 'client', $e->getMessage() ) );
367
			$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
368
		}
369
		catch( \Aimeos\Controller\Frontend\Exception $e )
370
		{
371
			$error = array( $context->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
372
			$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
373
		}
374
		catch( \Aimeos\MShop\Exception $e )
375
		{
376
			$error = array( $context->getI18n()->dt( 'mshop', $e->getMessage() ) );
377
			$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
378
		}
379
		catch( \Exception $e )
380
		{
381
			$error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
382
			$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
383
			$this->logException( $e );
384
		}
385
	}
386
387
388
	/**
389
	 * Returns the list of sub-client names configured for the client.
390
	 *
391
	 * @return array List of HTML client names
392
	 */
393
	protected function getSubClientNames() : array
394
	{
395
		return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
396
	}
397
398
399
	/**
400
	 * Sets the necessary parameter values in the view.
401
	 *
402
	 * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
403
	 * @param array &$tags Result array for the list of tags that are associated to the output
404
	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
405
	 * @return \Aimeos\MW\View\Iface Modified view object
406
	 */
407
	public function addData( \Aimeos\MW\View\Iface $view, array &$tags = [], string &$expire = null ) : \Aimeos\MW\View\Iface
408
	{
409
		$context = $this->getContext();
410
		$config = $context->getConfig();
411
412
		/** client/html/supplier/detail/supid-default
413
		 * The default supplier ID used if none is given as parameter
414
		 *
415
		 * You can configure the default supplier ID if no ID is passed in the
416
		 * URL using this configuration.
417
		 *
418
		 * @param string Supplier ID
419
		 * @since 2021.01
420
		 * @see client/html/catalog/lists/catid-default
421
		 */
422
		if( $supid = $view->param( 'f_supid', $config->get( 'client/html/supplier/detail/supid-default' ) ) )
423
		{
424
			$controller = \Aimeos\Controller\Frontend::create( $context, 'supplier' );
425
426
			/** client/html/supplier/detail/domains
427
			 * A list of domain names whose items should be available in the supplier detail view template
428
			 *
429
			 * The templates rendering the supplier detail section use the texts and
430
			 * maybe images and attributes associated to the categories. You can
431
			 * configure your own list of domains (attribute, media, price, product,
432
			 * text, etc. are domains) whose items are fetched from the storage.
433
			 * Please keep in mind that the more domains you add to the configuration,
434
			 * the more time is required for fetching the content!
435
			 *
436
			 * @param array List of domain names
437
			 * @since 2020.10
438
			 */
439
			$domains = $config->get( 'client/html/supplier/detail/domains', ['supplier/address', 'media', 'text'] );
440
441
			$supplier = $controller->uses( $domains )->get( $supid );
442
443
			$this->addMetaItems( $supplier, $expire, $tags );
444
445
			$view->detailSupplierItem = $supplier;
446
			$view->detailSupplierAddresses = $this->getAddressStrings( $view, $supplier->getAddressItems() );
447
		}
448
449
		return parent::addData( $view, $tags, $expire );
450
	}
451
452
453
	/**
454
	 * Returns the addresses as list of strings
455
	 *
456
	 * @param \Aimeos\MW\View\Iface $view View object
457
	 * @param iterable $addresses List of address items implementing \Aimeos\MShop\Common\Item\Address\Iface
458
	 * @return \Aimeos\Map List of address strings
459
	 */
460
	protected function getAddressStrings( \Aimeos\MW\View\Iface $view, iterable $addresses ) : \Aimeos\Map
461
	{
462
		$list = [];
463
464
		foreach( $addresses as $id => $addr )
465
		{
466
			$list[$id] = preg_replace( "/\n+/m", "\n", trim( sprintf(
467
				/// Address format with company (%1$s), salutation (%2$s), title (%3$s), first name (%4$s), last name (%5$s),
468
				/// address part one (%6$s, e.g street), address part two (%7$s, e.g house number), address part three (%8$s, e.g additional information),
469
				/// postal/zip code (%9$s), city (%10$s), state (%11$s), country (%12$s), language (%13$s),
470
				/// e-mail (%14$s), phone (%15$s), facsimile/telefax (%16$s), web site (%17$s), vatid (%18$s)
471
				$view->translate( 'client', '%1$s
472
%2$s %3$s %4$s %5$s
473
%6$s %7$s
474
%8$s
475
%9$s %10$s
476
%11$s
477
%12$s
478
%13$s
479
%14$s
480
%15$s
481
%16$s
482
%17$s
483
%18$s
484
'
485
				),
486
				$addr->getCompany(),
487
				$view->translate( 'mshop/code', (string) $addr->getSalutation() ),
488
				$addr->getTitle(),
489
				$addr->getFirstName(),
490
				$addr->getLastName(),
491
				$addr->getAddress1(),
492
				$addr->getAddress2(),
493
				$addr->getAddress3(),
494
				$addr->getPostal(),
495
				$addr->getCity(),
496
				$addr->getState(),
497
				$view->translate( 'country', (string) $addr->getCountryId() ),
498
				$view->translate( 'language', (string) $addr->getLanguageId() ),
499
				$addr->getEmail(),
500
				$addr->getTelephone(),
501
				$addr->getTelefax(),
502
				$addr->getWebsite(),
503
				$addr->getVatID()
504
			) ) );
505
		}
506
507
		return map( $list );
508
	}
509
}
510