Passed
Push — master ( 9a0039...2f021f )
by Aimeos
07:58
created

Standard::delete()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 0
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2017-2021
6
 * @package Controller
7
 * @subpackage Frontend
8
 */
9
10
11
namespace Aimeos\Controller\Frontend\Customer;
12
13
14
/**
15
 * Default implementation of the customer frontend controller
16
 *
17
 * @package Controller
18
 * @subpackage Frontend
19
 */
20
class Standard
21
	extends \Aimeos\Controller\Frontend\Base
22
	implements Iface, \Aimeos\Controller\Frontend\Common\Iface
23
{
24
	private $domains = [];
25
	private $manager;
26
	private $item;
27
28
29
	/**
30
	 * Initializes the controller
31
	 *
32
	 * @param \Aimeos\MShop\Context\Item\Iface $context Common MShop context object
33
	 */
34
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context )
35
	{
36
		parent::__construct( $context );
37
38
		$this->manager = \Aimeos\MShop::create( $context, 'customer' );
39
40
		if( ( $userid = $context->getUserId() ) === null )
41
		{
42
			/** controller/frontend/customer/groupids
43
			 * List of groups new customers should be assigned to
44
			 *
45
			 * Newly created customers will be assigned automatically to the groups
46
			 * given by their IDs. This is especially useful if those groups limit
47
			 * functionality for those users.
48
			 *
49
			 * @param array List of group IDs
50
			 * @since 2017.07
51
			 * @category User
52
			 * @category Developer
53
			 */
54
			$groupIds = (array) $context->getConfig()->get( 'controller/frontend/customer/groupids', [] );
55
			$this->item = $this->manager->create()->setGroups( $groupIds );
56
		}
57
		else
58
		{
59
			$this->item = $this->manager->get( $userid, [], true );
60
		}
61
	}
62
63
64
	/**
65
	 * Clones objects in controller and resets values
66
	 */
67
	public function __clone()
68
	{
69
		$this->item = clone $this->item;
70
	}
71
72
73
	/**
74
	 * Creates a new customer item object pre-filled with the given values but not yet stored
75
	 *
76
	 * @param array $values Values added to the customer item (new or existing) like "customer.code"
77
	 * @return \Aimeos\Controller\Frontend\Customer\Iface Customer controller for fluent interface
78
	 * @since 2019.04
79
	 */
80
	public function add( array $values ) : Iface
81
	{
82
		foreach( $values as $key => $value )
83
		{
84
			if( is_scalar( $value ) ) {
85
				$values[$key] = strip_tags( (string) $value ); // prevent XSS
86
			}
87
		}
88
89
		$addrItem = $this->item->getPaymentAddress();
90
91
		if( $code = $values['customer.code'] ?? null ) {
92
			$this->item->setCode( $code );
93
		}
94
95
		if( $password = $values['customer.password'] ?? null ) {
96
			$this->item = $item->setPassword( $password );
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $item seems to be never defined.
Loading history...
97
		}
98
99
		if( $this->item->getLabel() === '' )
100
		{
101
			$label = $addrItem->getLastname();
102
103
			if( ( $firstName = $addrItem->getFirstname() ) !== '' ) {
104
				$label = $firstName . ' ' . $label;
105
			}
106
107
			if( ( $company = $addrItem->getCompany() ) !== '' ) {
108
				$label .= ' (' . $company . ')';
109
			}
110
111
			$this->item->setLabel( $label );
112
		}
113
114
		$this->item->fromArray( $values );
115
		return $this;
116
	}
117
118
119
	/**
120
	 * Adds the given address item to the customer object (not yet stored)
121
	 *
122
	 * @param \Aimeos\MShop\Common\Item\Address\Iface $item Address item to add
123
	 * @param int|null $idx Key in the list of address items or null to add the item at the end
124
	 * @return \Aimeos\Controller\Frontend\Customer\Iface Customer controller for fluent interface
125
	 * @since 2019.04
126
	 */
127
	public function addAddressItem( \Aimeos\MShop\Common\Item\Address\Iface $item, int $idx = null ) : Iface
128
	{
129
		$this->item = $this->item->addAddressItem( $item, $idx );
130
		return $this;
131
	}
132
133
134
	/**
135
	 * Adds the given list item to the customer object (not yet stored)
136
	 *
137
	 * @param string $domain Domain name the referenced item belongs to
138
	 * @param \Aimeos\MShop\Common\Item\Lists\Iface $item List item to add
139
	 * @param \Aimeos\MShop\Common\Item\Iface|null $refItem Referenced item to add or null if list item contains refid value
140
	 * @return \Aimeos\Controller\Frontend\Customer\Iface Customer controller for fluent interface
141
	 * @since 2019.04
142
	 */
143
	public function addListItem( string $domain, \Aimeos\MShop\Common\Item\Lists\Iface $item,
144
		\Aimeos\MShop\Common\Item\Iface $refItem = null ) : Iface
145
	{
146
		if( $domain === 'customer/group' ) {
147
			throw new Exception( sprintf( 'You are not allowed to manage groups' ) );
148
		}
149
150
		$this->item = $this->item->addListItem( $domain, $item, $refItem );
151
		return $this;
152
	}
153
154
155
	/**
156
	 * Adds the given property item to the customer object (not yet stored)
157
	 *
158
	 * @param \Aimeos\MShop\Common\Item\Property\Iface $item Property item to add
159
	 * @return \Aimeos\Controller\Frontend\Customer\Iface Customer controller for fluent interface
160
	 * @since 2019.04
161
	 */
162
	public function addPropertyItem( \Aimeos\MShop\Common\Item\Property\Iface $item ) : Iface
163
	{
164
		$this->item = $this->item->addPropertyItem( $item );
165
		return $this;
166
	}
167
168
169
	/**
170
	 * Creates a new address item object pre-filled with the given values
171
	 *
172
	 * @param array $values Associative list of key/value pairs for populating the item
173
	 * @return \Aimeos\MShop\Customer\Item\Address\Iface Address item
174
	 * @since 2019.04
175
	 */
176
	public function createAddressItem( array $values = [] ) : \Aimeos\MShop\Customer\Item\Address\Iface
177
	{
178
		return $this->manager->createAddressItem()->fromArray( $values );
0 ignored issues
show
Bug introduced by
The method createAddressItem() does not exist on Aimeos\MShop\Common\Manager\Iface. Did you maybe mean create()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

178
		return $this->manager->/** @scrutinizer ignore-call */ createAddressItem()->fromArray( $values );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
179
	}
180
181
182
	/**
183
	 * Creates a new list item object pre-filled with the given values
184
	 *
185
	 * @param array $values Associative list of key/value pairs for populating the item
186
	 * @return \Aimeos\MShop\Common\Item\Lists\Iface List item
187
	 * @since 2019.04
188
	 */
189
	public function createListItem( array $values = [] ) : \Aimeos\MShop\Common\Item\Lists\Iface
190
	{
191
		return $this->manager->createListItem()->fromArray( $values );
0 ignored issues
show
Bug introduced by
The method createListItem() does not exist on Aimeos\MShop\Common\Manager\Iface. Did you maybe mean create()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

191
		return $this->manager->/** @scrutinizer ignore-call */ createListItem()->fromArray( $values );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
192
	}
193
194
195
	/**
196
	 * Creates a new property item object pre-filled with the given values
197
	 *
198
	 * @param array $values Associative list of key/value pairs for populating the item
199
	 * @return \Aimeos\MShop\Common\Item\Property\Iface Property item
200
	 * @since 2019.04
201
	 */
202
	public function createPropertyItem( array $values = [] ) : \Aimeos\MShop\Common\Item\Property\Iface
203
	{
204
		return $this->manager->createPropertyItem()->fromArray( $values );
0 ignored issues
show
Bug introduced by
The method createPropertyItem() does not exist on Aimeos\MShop\Common\Manager\Iface. Did you maybe mean create()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

204
		return $this->manager->/** @scrutinizer ignore-call */ createPropertyItem()->fromArray( $values );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
205
	}
206
207
208
	/**
209
	 * Deletes a customer item that belongs to the current authenticated user
210
	 *
211
	 * @return \Aimeos\Controller\Frontend\Customer\Iface Customer controller for fluent interface
212
	 * @since 2019.04
213
	 */
214
	public function delete() : Iface
215
	{
216
		if( $this->item && $this->item->getId() ) {
217
			\Aimeos\MShop::create( $this->getContext(), 'customer' )->delete( $this->item->getId() );
218
		}
219
220
		return $this;
221
	}
222
223
224
	/**
225
	 * Removes the given address item from the customer object (not yet stored)
226
	 *
227
	 * @param \Aimeos\MShop\Common\Item\Address\Iface $item Address item to remove
228
	 * @return \Aimeos\Controller\Frontend\Customer\Iface Customer controller for fluent interface
229
	 */
230
	public function deleteAddressItem( \Aimeos\MShop\Common\Item\Address\Iface $item ) : Iface
231
	{
232
		$this->item = $this->item->deleteAddressItem( $item );
233
		return $this;
234
	}
235
236
237
	/**
238
	 * Removes the given list item from the customer object (not yet stored)
239
	 *
240
	 * @param string $domain Domain name the referenced item belongs to
241
	 * @param \Aimeos\MShop\Common\Item\Lists\Iface $item List item to remove
242
	 * @param \Aimeos\MShop\Common\Item\Iface|null $refItem Referenced item to remove or null if only list item should be removed
243
	 * @return \Aimeos\Controller\Frontend\Customer\Iface Customer controller for fluent interface
244
	 */
245
	public function deleteListItem( string $domain, \Aimeos\MShop\Common\Item\Lists\Iface $listItem,
246
		\Aimeos\MShop\Common\Item\Iface $refItem = null ) : Iface
247
	{
248
		if( $domain === 'customer/group' ) {
249
			throw new Exception( sprintf( 'You are not allowed to manage groups' ) );
250
		}
251
252
		$this->item = $this->item->deleteListItem( $domain, $listItem, $refItem );
253
		return $this;
254
	}
255
256
257
	/**
258
	 * Removes the given property item from the customer object (not yet stored)
259
	 *
260
	 * @param \Aimeos\MShop\Common\Item\Property\Iface $item Property item to remove
261
	 * @return \Aimeos\Controller\Frontend\Customer\Iface Customer controller for fluent interface
262
	 */
263
	public function deletePropertyItem( \Aimeos\MShop\Common\Item\Property\Iface $item ) : Iface
264
	{
265
		$this->item = $this->item->deletePropertyItem( $item );
266
		return $this;
267
	}
268
269
270
	/**
271
	 * Returns the customer item for the given customer code (usually e-mail address)
272
	 *
273
	 * This method doesn't check if the customer item belongs to the logged in user!
274
	 *
275
	 * @param string $code Unique customer code
276
	 * @return \Aimeos\MShop\Customer\Item\Iface Customer item including the referenced domains items
277
	 * @since 2019.04
278
	 */
279
	public function find( string $code ) : \Aimeos\MShop\Customer\Item\Iface
280
	{
281
		return $this->manager->find( $code, $this->domains, 'customer', null, true );
282
	}
283
284
285
	/**
286
	 * Returns the customer item for the current authenticated user
287
	 *
288
	 * @return \Aimeos\MShop\Customer\Item\Iface Customer item including the referenced domains items
289
	 * @since 2019.04
290
	 */
291
	public function get() : \Aimeos\MShop\Customer\Item\Iface
292
	{
293
		return $this->item;
294
	}
295
296
297
	/**
298
	 * Adds or updates a modified customer item in the storage
299
	 *
300
	 * @return \Aimeos\Controller\Frontend\Customer\Iface Customer controller for fluent interface
301
	 * @since 2019.04
302
	 */
303
	public function store() : Iface
304
	{
305
		( $id = $this->item->getId() ) !== null ? $this->checkId( $id ) : $this->checkLimit();
306
		$context = $this->getContext();
307
308
		if( $id === null )
309
		{
310
			$msg = $this->item->toArray();
311
312
			// Show only generated passwords in account creation e-mails
313
			if( $this->item->getPassword() === '' )
314
			{
315
				$msg['customer.password'] = substr( sha1( microtime( true ) . getmypid() . rand() ), -8 );
316
				$this->item->setPassword( $msg['customer.password'] );
317
			}
318
319
			$context->getMessageQueue( 'mq-email', 'customer/email/account' )->add( json_encode( $msg ) );
320
		}
321
322
		$this->item = $this->manager->save( $this->item );
323
		return $this;
324
	}
325
326
327
	/**
328
	 * Sets the domains that will be used when working with the customer item
329
	 *
330
	 * @param array $domains Domain names of the referenced items that should be fetched too
331
	 * @return \Aimeos\Controller\Frontend\Customer\Iface Customer controller for fluent interface
332
	 * @since 2019.04
333
	 */
334
	public function uses( array $domains ) : Iface
335
	{
336
		$this->domains = $domains;
337
338
		if( ( $id = $this->getContext()->getUserId() ) !== null ) {
339
			$this->item = $this->manager->get( $id, $domains, true );
340
		}
341
342
		return $this;
343
	}
344
345
346
	/**
347
	 * Checks if the current user is allowed to create more customer accounts
348
	 *
349
	 * @throws \Aimeos\Controller\Frontend\Customer\Exception If access isn't allowed
350
	 */
351
	protected function checkLimit()
352
	{
353
		$total = 0;
354
		$context = $this->getContext();
355
		$config = $context->getConfig();
356
357
		/** controller/frontend/customer/limit-count
358
		 * Maximum number of customers within the time frame
359
		 *
360
		 * Creating new customers is limited to avoid abuse and mitigate denial of
361
		 * service attacks. The number of customer accountss created within the
362
		 * time frame configured by "controller/frontend/customer/limit-seconds"
363
		 * are counted before a new customer account (identified by the IP address)
364
		 * is created. If the number of accounts is higher than the configured value,
365
		 * an error message will be shown to the user instead of creating a new account.
366
		 *
367
		 * @param integer Number of customer accounts allowed within the time frame
368
		 * @since 2017.07
369
		 * @category Developer
370
		 * @see controller/frontend/customer/limit-seconds
371
		 */
372
		$count = $config->get( 'controller/frontend/customer/limit-count', 3 );
373
374
		/** controller/frontend/customer/limit-seconds
375
		 * Customer account limitation time frame in seconds
376
		 *
377
		 * Creating new customer accounts is limited to avoid abuse and mitigate
378
		 * denial of service attacks. Within the configured time frame, only a
379
		 * limited number of customer accounts can be created. All accounts from
380
		 * the same source (identified by the IP address) within the last X
381
		 * seconds are counted. If the total value is higher then the number
382
		 * configured in "controller/frontend/customer/limit-count", an error
383
		 * message will be shown to the user instead of creating a new account.
384
		 *
385
		 * @param integer Number of seconds to check customer accounts within
386
		 * @since 2017.07
387
		 * @category Developer
388
		 * @see controller/frontend/customer/limit-count
389
		 */
390
		$seconds = $config->get( 'controller/frontend/customer/limit-seconds', 14400 );
391
392
		$search = $this->manager->filter()->slice( 0, 0 );
393
		$expr = [
394
			$search->compare( '==', 'customer.editor', $context->getEditor() ),
395
			$search->compare( '>=', 'customer.ctime', date( 'Y-m-d H:i:s', time() - $seconds ) ),
396
		];
397
		$search->setConditions( $search->and( $expr ) );
398
399
		$this->manager->search( $search, [], $total );
400
401
		if( $total >= $count ) {
402
			throw new \Aimeos\Controller\Frontend\Customer\Exception( sprintf( 'Temporary limit reached' ) );
403
		}
404
	}
405
406
407
	/**
408
	 * Checks if the current user is allowed to retrieve the customer data for the given ID
409
	 *
410
	 * @param string $id Unique customer ID
411
	 * @return string Unique customer ID
412
	 * @throws \Aimeos\Controller\Frontend\Customer\Exception If access isn't allowed
413
	 */
414
	protected function checkId( string $id ) : string
415
	{
416
		if( $id != $this->getContext()->getUserId() )
417
		{
418
			$msg = sprintf( 'Not allowed to access customer data for ID "%1$s"', $id );
419
			throw new \Aimeos\Controller\Frontend\Customer\Exception( $msg );
420
		}
421
422
		return $id;
423
	}
424
425
426
	/**
427
	 * Returns the manager used by the controller
428
	 *
429
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object
430
	 */
431
	protected function getManager() : \Aimeos\MShop\Common\Manager\Iface
432
	{
433
		return $this->manager;
434
	}
435
}
436