· 6 years ago · May 31, 2019, 01:28 PM
1<?php
2/**
3 * 2007-2018 PrestaShop.
4 *
5 * NOTICE OF LICENSE
6 *
7 * This source file is subject to the Open Software License (OSL 3.0)
8 * that is bundled with this package in the file LICENSE.txt.
9 * It is also available through the world-wide-web at this URL:
10 * https://opensource.org/licenses/OSL-3.0
11 * If you did not receive a copy of the license and are unable to
12 * obtain it through the world-wide-web, please send an email
13 * to license@prestashop.com so we can send you a copy immediately.
14 *
15 * DISCLAIMER
16 *
17 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
18 * versions in the future. If you wish to customize PrestaShop for your
19 * needs please refer to http://www.prestashop.com for more information.
20 *
21 * @author PrestaShop SA <contact@prestashop.com>
22 * @copyright 2007-2018 PrestaShop SA
23 * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
24 * International Registered Trademark & Property of PrestaShop SA
25 */
26use PrestaShop\PrestaShop\Adapter\ServiceLocator;
27use PrestaShop\PrestaShop\Adapter\CoreException;
28
29/***
30 * Class CustomerCore
31 */
32class CustomerCore extends ObjectModel
33{
34 /** @var int $id Customer ID */
35 public $id;
36
37 /** @var int $id_shop Shop ID */
38 public $id_shop;
39
40 /** @var int $id_shop_group ShopGroup ID */
41 public $id_shop_group;
42
43 /** @var string Secure key */
44 public $secure_key;
45
46 /** @var string protected note */
47 public $note;
48
49 /** @var int Gender ID */
50 public $id_gender = 0;
51
52 /** @var int Default group ID */
53 public $id_default_group;
54
55 /** @var int Current language used by the customer */
56 public $id_lang;
57
58 /** @var string Lastname */
59 public $lastname;
60
61 /** @var string Firstname */
62 public $firstname;
63
64 /** @var string Birthday (yyyy-mm-dd) */
65 public $birthday = null;
66
67 /** @var string e-mail */
68 public $email;
69
70 /** @var bool Newsletter subscription */
71 public $newsletter;
72
73 /** @var string Newsletter ip registration */
74 public $ip_registration_newsletter;
75
76 /** @var string Newsletter registration date */
77 public $newsletter_date_add;
78
79 /** @var bool Opt-in subscription */
80 public $optin;
81
82 /** @var string WebSite * */
83 public $website;
84
85 /** @var string Company */
86 public $company;
87
88 /** @var string SIRET */
89 public $siret;
90
91 /** @var string APE */
92 public $ape;
93
94 /** @var float Outstanding allow amount (B2B opt) */
95 public $outstanding_allow_amount = 0;
96
97 /** @var int Show public prices (B2B opt) */
98 public $show_public_prices = 0;
99
100 /** @var int Risk ID (B2B opt) */
101 public $id_risk;
102
103 /** @var int Max payment day */
104 public $max_payment_days = 0;
105
106 /** @var int Password */
107 public $passwd;
108
109 /** @var string Datetime Password */
110 public $last_passwd_gen;
111
112 /** @var bool Status */
113 public $active = true;
114
115 /** @var bool Status */
116 public $is_guest = 0;
117
118 /** @var bool True if carrier has been deleted (staying in database as deleted) */
119 public $deleted = 0;
120
121 /** @var string Object creation date */
122 public $date_add;
123
124 /** @var string Object last modification date */
125 public $date_upd;
126
127 public $years;
128 public $days;
129 public $months;
130
131 /** @var int customer id_country as determined by geolocation */
132 public $geoloc_id_country;
133 /** @var int customer id_state as determined by geolocation */
134 public $geoloc_id_state;
135 /** @var string customer postcode as determined by geolocation */
136 public $geoloc_postcode;
137
138 /** @var bool is the customer logged in */
139 public $logged = 0;
140
141 /** @var int id_guest meaning the guest table, not the guest customer */
142 public $id_guest;
143
144 public $groupBox;
145
146 public $pec;
147 public $spid;
148
149 /** @var string Unique token for forgot passsword feature */
150 public $reset_password_token;
151
152 /** @var string token validity date for forgot password feature */
153 public $reset_password_validity;
154
155 protected $webserviceParameters = array(
156 'fields' => array(
157 'id_default_group' => array('xlink_resource' => 'groups'),
158 'id_lang' => array('xlink_resource' => 'languages'),
159 'newsletter_date_add' => array(),
160 'ip_registration_newsletter' => array(),
161 'last_passwd_gen' => array('setter' => null),
162 'secure_key' => array('setter' => null),
163 'deleted' => array(),
164 'passwd' => array('setter' => 'setWsPasswd'),
165 ),
166 'associations' => array(
167 'groups' => array('resource' => 'group'),
168 ),
169 );
170
171 /**
172 * @see ObjectModel::$definition
173 */
174 public static $definition = array(
175 'table' => 'customer',
176 'primary' => 'id_customer',
177 'fields' => array(
178 'secure_key' => array('type' => self::TYPE_STRING, 'validate' => 'isMd5', 'copy_post' => false),
179 'lastname' => array('type' => self::TYPE_STRING, 'validate' => 'isCustomerName', 'required' => true, 'size' => 255),
180 'firstname' => array('type' => self::TYPE_STRING, 'validate' => 'isCustomerName', 'required' => true, 'size' => 255),
181 'email' => array('type' => self::TYPE_STRING, 'validate' => 'isEmail', 'required' => true, 'size' => 255),
182 'passwd' => array('type' => self::TYPE_STRING, 'validate' => 'isPasswd', 'required' => true, 'size' => 255),
183 'last_passwd_gen' => array('type' => self::TYPE_STRING, 'copy_post' => false),
184 'id_gender' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
185 'birthday' => array('type' => self::TYPE_DATE, 'validate' => 'isBirthDate'),
186 'newsletter' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
187 'newsletter_date_add' => array('type' => self::TYPE_DATE, 'copy_post' => false),
188 'ip_registration_newsletter' => array('type' => self::TYPE_STRING, 'copy_post' => false),
189 'optin' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
190 'website' => array('type' => self::TYPE_STRING, 'validate' => 'isUrl'),
191 'company' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName'),
192 'siret' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName'),
193 'ape' => array('type' => self::TYPE_STRING, 'validate' => 'isApe'),
194 'outstanding_allow_amount' => array('type' => self::TYPE_FLOAT, 'validate' => 'isFloat', 'copy_post' => false),
195 'show_public_prices' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'copy_post' => false),
196 'id_risk' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'copy_post' => false),
197 'max_payment_days' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'copy_post' => false),
198 'active' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'copy_post' => false),
199 'deleted' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'copy_post' => false),
200 'note' => array('type' => self::TYPE_HTML, 'validate' => 'isCleanHtml', 'size' => 65000, 'copy_post' => false),
201 'is_guest' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'copy_post' => false),
202 'id_shop' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'copy_post' => false),
203 'id_shop_group' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'copy_post' => false),
204 'id_default_group' => array('type' => self::TYPE_INT, 'copy_post' => false),
205 'id_lang' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'copy_post' => false),
206 'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate', 'copy_post' => false),
207 'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate', 'copy_post' => false),
208 'reset_password_token' => array('type' => self::TYPE_STRING, 'validate' => 'isSha1', 'size' => 40, 'copy_post' => false),
209 'reset_password_validity' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull', 'copy_post' => false),
210 'pec' => array('type' => self::TYPE_STRING, 'validate' => 'isEmail', 'size' => 255),
211 'spid' => array('type' => self::TYPE_STRING, 'validate' => 'isCustomerName', 'size' => 255),
212 ),
213 );
214
215 protected static $_defaultGroupId = array();
216 protected static $_customerHasAddress = array();
217 protected static $_customer_groups = array();
218
219 /**
220 * CustomerCore constructor.
221 *
222 * @param null $id
223 */
224 public function __construct($id = null)
225 {
226 $this->id_default_group = (int) Configuration::get('PS_CUSTOMER_GROUP');
227 parent::__construct($id);
228 }
229
230 /**
231 * Adds current Customer as a new Object to the database.
232 *
233 * @param bool $autoDate Automatically set `date_upd` and `date_add` columns
234 * @param bool $nullValues Whether we want to use NULL values instead of empty quotes values
235 *
236 * @return bool Indicates whether the Customer has been successfully added
237 *
238 * @throws PrestaShopDatabaseException
239 * @throws PrestaShopException
240 */
241 public function add($autoDate = true, $nullValues = true)
242 {
243 $this->id_shop = ($this->id_shop) ? $this->id_shop : Context::getContext()->shop->id;
244 $this->id_shop_group = ($this->id_shop_group) ? $this->id_shop_group : Context::getContext()->shop->id_shop_group;
245 $this->id_lang = ($this->id_lang) ? $this->id_lang : Context::getContext()->language->id;
246 $this->birthday = (empty($this->years) ? $this->birthday : (int) $this->years . '-' . (int) $this->months . '-' . (int) $this->days);
247 $this->secure_key = md5(uniqid(rand(), true));
248 $this->last_passwd_gen = date('Y-m-d H:i:s', strtotime('-' . Configuration::get('PS_PASSWD_TIME_FRONT') . 'minutes'));
249
250 if ($this->newsletter && !Validate::isDate($this->newsletter_date_add)) {
251 $this->newsletter_date_add = date('Y-m-d H:i:s');
252 }
253
254 if ($this->id_default_group == Configuration::get('PS_CUSTOMER_GROUP')) {
255 if ($this->is_guest) {
256 $this->id_default_group = (int) Configuration::get('PS_GUEST_GROUP');
257 } else {
258 $this->id_default_group = (int) Configuration::get('PS_CUSTOMER_GROUP');
259 }
260 }
261
262 /* Can't create a guest customer, if this feature is disabled */
263 if ($this->is_guest && !Configuration::get('PS_GUEST_CHECKOUT_ENABLED')) {
264 return false;
265 }
266 $success = parent::add($autoDate, $nullValues);
267 $this->updateGroup($this->groupBox);
268
269 return $success;
270 }
271
272 /**
273 * Updates the current Customer in the database.
274 *
275 * @param bool $nullValues Whether we want to use NULL values instead of empty quotes values
276 *
277 * @return bool Indicates whether the Customer has been successfully updated
278 *
279 * @throws PrestaShopDatabaseException
280 * @throws PrestaShopException
281 */
282 public function update($nullValues = false)
283 {
284 $this->birthday = (empty($this->years) ? $this->birthday : (int) $this->years . '-' . (int) $this->months . '-' . (int) $this->days);
285
286 if ($this->newsletter && !Validate::isDate($this->newsletter_date_add)) {
287 $this->newsletter_date_add = date('Y-m-d H:i:s');
288 }
289 if (isset(Context::getContext()->controller) && Context::getContext()->controller->controller_type == 'admin') {
290 $this->updateGroup($this->groupBox);
291 }
292
293 if ($this->deleted) {
294 $addresses = $this->getAddresses((int) Configuration::get('PS_LANG_DEFAULT'));
295 foreach ($addresses as $address) {
296 $obj = new Address((int) $address['id_address']);
297 $obj->deleted = true;
298 $obj->save();
299 }
300 }
301
302 try {
303 return parent::update(true);
304 } catch (\PrestaShopException $exception) {
305 $message = $exception->getMessage();
306 error_log($message);
307
308 return false;
309 }
310 }
311
312 /**
313 * Deletes current Customer from the database.
314 *
315 * @return bool True if delete was successful
316 *
317 * @throws PrestaShopException
318 */
319 public function delete()
320 {
321 if (!count(Order::getCustomerOrders((int) $this->id))) {
322 $addresses = $this->getAddresses((int) Configuration::get('PS_LANG_DEFAULT'));
323 foreach ($addresses as $address) {
324 $obj = new Address((int) $address['id_address']);
325 $obj->delete();
326 }
327 }
328 Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'customer_group` WHERE `id_customer` = ' . (int) $this->id);
329 Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'message WHERE id_customer=' . (int) $this->id);
330 Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'specific_price WHERE id_customer=' . (int) $this->id);
331
332 $carts = Db::getInstance()->executeS('SELECT id_cart FROM ' . _DB_PREFIX_ . 'cart WHERE id_customer=' . (int) $this->id);
333 if ($carts) {
334 foreach ($carts as $cart) {
335 Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'cart WHERE id_cart=' . (int) $cart['id_cart']);
336 Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'cart_product WHERE id_cart=' . (int) $cart['id_cart']);
337 }
338 }
339
340 $cts = Db::getInstance()->executeS('SELECT id_customer_thread FROM ' . _DB_PREFIX_ . 'customer_thread WHERE id_customer=' . (int) $this->id);
341 if ($cts) {
342 foreach ($cts as $ct) {
343 Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'customer_thread WHERE id_customer_thread=' . (int) $ct['id_customer_thread']);
344 Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'customer_message WHERE id_customer_thread=' . (int) $ct['id_customer_thread']);
345 }
346 }
347
348 CartRule::deleteByIdCustomer((int) $this->id);
349
350 return parent::delete();
351 }
352
353 /**
354 * Return customers list.
355 *
356 * @param null|bool $onlyActive Returns only active customers when `true`
357 *
358 * @return array Customers
359 */
360 public static function getCustomers($onlyActive = null)
361 {
362 return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
363 SELECT `id_customer`, `email`, `firstname`, `lastname`
364 FROM `' . _DB_PREFIX_ . 'customer`
365 WHERE 1 ' . Shop::addSqlRestriction(Shop::SHARE_CUSTOMER) .
366 ($onlyActive ? ' AND `active` = 1' : '') . '
367 ORDER BY `id_customer` ASC'
368 );
369 }
370
371 /**
372 * Return customer instance from its e-mail (optionally check password).
373 *
374 * @param string $email e-mail
375 * @param string $plaintextPassword Password is also checked if specified
376 * @param bool $ignoreGuest
377 *
378 * @return bool|Customer|CustomerCore Customer instance
379 */
380 public function getByEmail($email, $plaintextPassword = null, $ignoreGuest = true)
381 {
382 if (!Validate::isEmail($email) || ($plaintextPassword && !Validate::isPasswd($plaintextPassword))) {
383 die(Tools::displayError());
384 }
385
386 $shopGroup = Shop::getGroupFromShop(Shop::getContextShopID(), false);
387
388 $sql = new DbQuery();
389 $sql->select('c.`passwd`');
390 $sql->from('customer', 'c');
391 $sql->where('c.`email` = \'' . pSQL($email) . '\'');
392 if (Shop::getContext() == Shop::CONTEXT_SHOP && $shopGroup['share_customer']) {
393 $sql->where('c.`id_shop_group` = ' . (int) Shop::getContextShopGroupID());
394 } else {
395 $sql->where('c.`id_shop` IN (' . implode(', ', Shop::getContextListShopID(Shop::SHARE_CUSTOMER)) . ')');
396 }
397
398 if ($ignoreGuest) {
399 $sql->where('c.`is_guest` = 0');
400 }
401 $sql->where('c.`deleted` = 0');
402
403 $passwordHash = Db::getInstance()->getValue($sql);
404 try {
405 /** @var \PrestaShop\PrestaShop\Core\Crypto\Hashing $crypto */
406 $crypto = ServiceLocator::get('\\PrestaShop\\PrestaShop\\Core\\Crypto\\Hashing');
407 } catch (CoreException $e) {
408 return false;
409 }
410
411 $shouldCheckPassword = !is_null($plaintextPassword);
412 if ($shouldCheckPassword && !$crypto->checkHash($plaintextPassword, $passwordHash)) {
413 return false;
414 }
415
416 $sql = new DbQuery();
417 $sql->select('c.*');
418 $sql->from('customer', 'c');
419 $sql->where('c.`email` = \'' . pSQL($email) . '\'');
420 if (Shop::getContext() == Shop::CONTEXT_SHOP && $shopGroup['share_customer']) {
421 $sql->where('c.`id_shop_group` = ' . (int) Shop::getContextShopGroupID());
422 } else {
423 $sql->where('c.`id_shop` IN (' . implode(', ', Shop::getContextListShopID(Shop::SHARE_CUSTOMER)) . ')');
424 }
425 if ($ignoreGuest) {
426 $sql->where('c.`is_guest` = 0');
427 }
428 $sql->where('c.`deleted` = 0');
429
430 $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql);
431
432 if (!$result) {
433 return false;
434 }
435
436 $this->id = $result['id_customer'];
437 foreach ($result as $key => $value) {
438 if (property_exists($this, $key)) {
439 $this->{$key} = $value;
440 }
441 }
442
443 if ($shouldCheckPassword && !$crypto->isFirstHash($plaintextPassword, $passwordHash)) {
444 $this->passwd = $crypto->hash($plaintextPassword);
445 $this->update();
446 }
447
448 return $this;
449 }
450
451 /**
452 * Retrieve customers by email address.
453 *
454 * @param string $email
455 *
456 * @return array
457 */
458 public static function getCustomersByEmail($email)
459 {
460 $sql = 'SELECT *
461 FROM `' . _DB_PREFIX_ . 'customer`
462 WHERE `email` = \'' . pSQL($email) . '\'
463 ' . Shop::addSqlRestriction(Shop::SHARE_CUSTOMER);
464
465 return Db::getInstance()->executeS($sql);
466 }
467
468 /**
469 * Check id the customer is active or not.
470 *
471 * @param int $idCustomer
472 *
473 * @return bool Customer validity
474 */
475 public static function isBanned($idCustomer)
476 {
477 if (!Validate::isUnsignedId($idCustomer)) {
478 return true;
479 }
480 $cacheId = 'Customer::isBanned_' . (int) $idCustomer;
481 if (!Cache::isStored($cacheId)) {
482 $result = (bool) !Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
483 SELECT `id_customer`
484 FROM `' . _DB_PREFIX_ . 'customer`
485 WHERE `id_customer` = \'' . (int) $idCustomer . '\'
486 AND active = 1
487 AND `deleted` = 0');
488 Cache::store($cacheId, $result);
489
490 return $result;
491 }
492
493 return Cache::retrieve($cacheId);
494 }
495
496 /**
497 * Check if e-mail is already registered in database.
498 *
499 * @param string $email e-mail
500 * @param bool $returnId
501 * @param bool $ignoreGuest To exclude guest customer
502 *
503 * @return bool|int Customer ID if found
504 * `false` otherwise
505 */
506 public static function customerExists($email, $returnId = false, $ignoreGuest = true)
507 {
508 if (!Validate::isEmail($email)) {
509 return false;
510 }
511
512 $result = Db::getInstance()->getValue('
513 SELECT `id_customer`
514 FROM `' . _DB_PREFIX_ . 'customer`
515 WHERE `email` = \'' . pSQL($email) . '\'
516 ' . Shop::addSqlRestriction(Shop::SHARE_CUSTOMER) . '
517 ' . ($ignoreGuest ? ' AND `is_guest` = 0' : ''));
518
519 return $returnId ? (int) $result : (bool) $result;
520 }
521
522 /**
523 * Check if an address is owned by a customer.
524 *
525 * @param int $idCustomer Customer ID
526 * @param int $idAddress Address ID
527 *
528 * @return bool result
529 */
530 public static function customerHasAddress($idCustomer, $idAddress)
531 {
532 $key = (int) $idCustomer . '-' . (int) $idAddress;
533 if (!array_key_exists($key, self::$_customerHasAddress)) {
534 self::$_customerHasAddress[$key] = (bool) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
535 SELECT `id_address`
536 FROM `' . _DB_PREFIX_ . 'address`
537 WHERE `id_customer` = ' . (int) $idCustomer . '
538 AND `id_address` = ' . (int) $idAddress . '
539 AND `deleted` = 0');
540 }
541
542 return self::$_customerHasAddress[$key];
543 }
544
545 /**
546 * Reset Address cache.
547 *
548 * @param int $idCustomer Customer ID
549 * @param int $idAddress Address ID
550 */
551 public static function resetAddressCache($idCustomer, $idAddress)
552 {
553 $key = (int) $idCustomer . '-' . (int) $idAddress;
554 if (array_key_exists($key, self::$_customerHasAddress)) {
555 unset(self::$_customerHasAddress[$key]);
556 }
557 }
558
559 /**
560 * Return customer addresses.
561 *
562 * @param int $idLang Language ID
563 *
564 * @return array Addresses
565 */
566 public function getAddresses($idLang)
567 {
568 $group = Context::getContext()->shop->getGroup();
569 $shareOrder = isset($group->share_order) ? (bool) $group->share_order : false;
570 $cacheId = 'Customer::getAddresses'
571 . '-' . (int) $this->id
572 . '-' . (int) $idLang
573 . '-' . ($shareOrder ? 1 : 0);
574 if (!Cache::isStored($cacheId)) {
575 $sql = 'SELECT DISTINCT a.*, cl.`name` AS country, s.name AS state, s.iso_code AS state_iso
576 FROM `' . _DB_PREFIX_ . 'address` a
577 LEFT JOIN `' . _DB_PREFIX_ . 'country` c ON (a.`id_country` = c.`id_country`)
578 LEFT JOIN `' . _DB_PREFIX_ . 'country_lang` cl ON (c.`id_country` = cl.`id_country`)
579 LEFT JOIN `' . _DB_PREFIX_ . 'state` s ON (s.`id_state` = a.`id_state`)
580 ' . ($shareOrder ? '' : Shop::addSqlAssociation('country', 'c')) . '
581 WHERE `id_lang` = ' . (int) $idLang . ' AND `id_customer` = ' . (int) $this->id . ' AND a.`deleted` = 0';
582
583 $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
584 Cache::store($cacheId, $result);
585
586 return $result;
587 }
588
589 return Cache::retrieve($cacheId);
590 }
591
592 /**
593 * Get simplified Addresses arrays.
594 *
595 * @param int|null $idLang Language ID
596 *
597 * @return array
598 */
599 public function getSimpleAddresses($idLang = null)
600 {
601 if (!$this->id) {
602 return array();
603 }
604
605 if (is_null($idLang)) {
606 $idLang = Context::getContext()->language->id;
607 }
608
609 $sql = $this->getSimpleAddressSql(null, $idLang);
610 $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
611 $addresses = array();
612 foreach ($result as $addr) {
613 $addresses[$addr['id']] = $addr;
614 }
615
616 return $addresses;
617 }
618
619 /**
620 * Get Address as array.
621 *
622 * @param int $idAddress Address ID
623 * @param int|null $idLang Language ID
624 *
625 * @return array|false|mysqli_result|null|PDOStatement|resource
626 */
627 public function getSimpleAddress($idAddress, $idLang = null)
628 {
629 if (!$this->id || !intval($idAddress) || !$idAddress) {
630 return array(
631 'id' => '',
632 'alias' => '',
633 'firstname' => '',
634 'lastname' => '',
635 'company' => '',
636 'address1' => '',
637 'address2' => '',
638 'postcode' => '',
639 'city' => '',
640 'id_state' => '',
641 'state' => '',
642 'state_iso' => '',
643 'id_country' => '',
644 'country' => '',
645 'country_iso' => '',
646 'other' => '',
647 'phone' => '',
648 'phone_mobile' => '',
649 'vat_number' => '',
650 'dni' => '',
651 );
652 }
653
654 $sql = $this->getSimpleAddressSql($idAddress, $idLang);
655 $res = Db::getInstance()->executeS($sql);
656 if (count($res) === 1) {
657 return $res[0];
658 } else {
659 return $res;
660 }
661 }
662
663 /**
664 * Get SQL query to retrieve Address in an array.
665 *
666 * @param int|null $idAddress Address ID
667 * @param int|null $idLang Language ID
668 *
669 * @return string
670 */
671 public function getSimpleAddressSql($idAddress = null, $idLang = null)
672 {
673 if (is_null($idLang)) {
674 $idLang = Context::getContext()->language->id;
675 }
676 $shareOrder = (bool) Context::getContext()->shop->getGroup()->share_order;
677
678 $sql = 'SELECT DISTINCT
679 a.`id_address` AS `id`,
680 a.`alias`,
681 a.`firstname`,
682 a.`lastname`,
683 a.`company`,
684 a.`address1`,
685 a.`address2`,
686 a.`postcode`,
687 a.`city`,
688 a.`id_state`,
689 s.name AS state,
690 s.`iso_code` AS state_iso,
691 a.`id_country`,
692 cl.`name` AS country,
693 co.`iso_code` AS country_iso,
694 a.`other`,
695 a.`phone`,
696 a.`phone_mobile`,
697 a.`vat_number`,
698 a.`dni`
699 FROM `' . _DB_PREFIX_ . 'address` a
700 LEFT JOIN `' . _DB_PREFIX_ . 'country` co ON (a.`id_country` = co.`id_country`)
701 LEFT JOIN `' . _DB_PREFIX_ . 'country_lang` cl ON (co.`id_country` = cl.`id_country`)
702 LEFT JOIN `' . _DB_PREFIX_ . 'state` s ON (s.`id_state` = a.`id_state`)
703 ' . ($shareOrder ? '' : Shop::addSqlAssociation('country', 'co')) . '
704 WHERE
705 `id_lang` = ' . (int) $idLang . '
706 AND `id_customer` = ' . (int) $this->id . '
707 AND a.`deleted` = 0
708 AND a.`active` = 1';
709
710 if (!is_null($idAddress)) {
711 $sql .= ' AND a.`id_address` = ' . (int) $idAddress;
712 }
713
714 return $sql;
715 }
716
717 /**
718 * Count the number of addresses for a customer.
719 *
720 * @param int $idCustomer Customer ID
721 *
722 * @return int Number of addresses
723 */
724 public static function getAddressesTotalById($idCustomer)
725 {
726 return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
727 SELECT COUNT(`id_address`)
728 FROM `' . _DB_PREFIX_ . 'address`
729 WHERE `id_customer` = ' . (int) $idCustomer . '
730 AND `deleted` = 0'
731 );
732 }
733
734 /**
735 * Check if customer password is the right one.
736 *
737 * @param int $idCustomer Customer ID
738 * @param string $passwordHash Hashed password
739 *
740 * @return bool result
741 */
742 public static function checkPassword($idCustomer, $passwordHash)
743 {
744 if (!Validate::isUnsignedId($idCustomer)) {
745 die(Tools::displayError());
746 }
747 $cacheId = 'Customer::checkPassword' . (int) $idCustomer . '-' . $passwordHash;
748 if (!Cache::isStored($cacheId)) {
749 $sql = new DbQuery();
750 $sql->select('c.`id_customer`');
751 $sql->from('customer', 'c');
752 $sql->where('c.`id_customer` = ' . (int) $idCustomer);
753 $sql->where('c.`passwd` = \'' . pSQL($passwordHash) . '\'');
754
755 $result = (bool) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql);
756
757 Cache::store($cacheId, $result);
758
759 return $result;
760 }
761
762 return Cache::retrieve($cacheId);
763 }
764
765 /**
766 * Light back office search for customers.
767 *
768 * @param string $query Searched string
769 * @param null|int $limit Limit query results
770 *
771 * @return array|false|mysqli_result|null|PDOStatement|resource Corresponding customers
772 *
773 * @throws PrestaShopDatabaseException
774 */
775 public static function searchByName($query, $limit = null)
776 {
777 $sql = 'SELECT *
778 FROM `' . _DB_PREFIX_ . 'customer`
779 WHERE 1';
780 $search_items = explode(' ', $query);
781 $research_fields = array('id_customer', 'firstname', 'lastname', 'email');
782 if (Configuration::get('PS_B2B_ENABLE')) {
783 $research_fields[] = 'company';
784 }
785
786 $items = array();
787 foreach ($research_fields as $field) {
788 foreach ($search_items as $item) {
789 $items[$item][] = $field . ' LIKE \'%' . pSQL($item) . '%\' ';
790 }
791 }
792
793 foreach ($items as $likes) {
794 $sql .= ' AND (' . implode(' OR ', $likes) . ') ';
795 }
796
797 $sql .= Shop::addSqlRestriction(Shop::SHARE_CUSTOMER);
798
799 if ($limit) {
800 $sql .= ' LIMIT 0, ' . (int) $limit;
801 }
802
803 return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
804 }
805
806 /**
807 * Search for customers by ip address.
808 *
809 * @param string $ip Searched string
810 *
811 * @return array|false|mysqli_result|null|PDOStatement|resource
812 */
813 public static function searchByIp($ip)
814 {
815 return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
816 SELECT DISTINCT c.*
817 FROM `' . _DB_PREFIX_ . 'customer` c
818 LEFT JOIN `' . _DB_PREFIX_ . 'guest` g ON g.id_customer = c.id_customer
819 LEFT JOIN `' . _DB_PREFIX_ . 'connections` co ON g.id_guest = co.id_guest
820 WHERE co.`ip_address` = \'' . (int) ip2long(trim($ip)) . '\'');
821 }
822
823 /**
824 * Return several useful statistics about customer.
825 *
826 * @return array Stats
827 */
828 public function getStats()
829 {
830 $result = Db::getInstance()->getRow('
831 SELECT COUNT(`id_order`) AS nb_orders, SUM(`total_paid` / o.`conversion_rate`) AS total_orders
832 FROM `' . _DB_PREFIX_ . 'orders` o
833 WHERE o.`id_customer` = ' . (int) $this->id . '
834 AND o.valid = 1');
835
836 $result2 = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
837 SELECT c.`date_add` AS last_visit
838 FROM `' . _DB_PREFIX_ . 'connections` c
839 LEFT JOIN `' . _DB_PREFIX_ . 'guest` g USING (id_guest)
840 WHERE g.`id_customer` = ' . (int) $this->id . ' ORDER BY c.`date_add` DESC ');
841
842 $result3 = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
843 SELECT (YEAR(CURRENT_DATE)-YEAR(c.`birthday`)) - (RIGHT(CURRENT_DATE, 5)<RIGHT(c.`birthday`, 5)) AS age
844 FROM `' . _DB_PREFIX_ . 'customer` c
845 WHERE c.`id_customer` = ' . (int) $this->id);
846
847 $result['last_visit'] = $result2['last_visit'];
848 $result['age'] = ($result3['age'] != date('Y') ? $result3['age'] : '--');
849
850 return $result;
851 }
852
853 /**
854 * Get last 10 emails sent to the Customer.
855 *
856 * @return array|false|mysqli_result|null|PDOStatement|resource
857 */
858 public function getLastEmails()
859 {
860 if (!$this->id) {
861 return array();
862 }
863
864 return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
865 SELECT m.*, l.name as language
866 FROM `' . _DB_PREFIX_ . 'mail` m
867 LEFT JOIN `' . _DB_PREFIX_ . 'lang` l ON m.id_lang = l.id_lang
868 WHERE `recipient` = "' . pSQL($this->email) . '"
869 ORDER BY m.date_add DESC
870 LIMIT 10');
871 }
872
873 /**
874 * Get last 10 Connections of the Customer.
875 *
876 * @return array|false|mysqli_result|null|PDOStatement|resource
877 */
878 public function getLastConnections()
879 {
880 if (!$this->id) {
881 return array();
882 }
883
884 return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
885 SELECT c.id_connections, c.date_add, COUNT(cp.id_page) AS pages, TIMEDIFF(MAX(cp.time_end), c.date_add) as time, http_referer,INET_NTOA(ip_address) as ipaddress
886 FROM `' . _DB_PREFIX_ . 'guest` g
887 LEFT JOIN `' . _DB_PREFIX_ . 'connections` c ON c.id_guest = g.id_guest
888 LEFT JOIN `' . _DB_PREFIX_ . 'connections_page` cp ON c.id_connections = cp.id_connections
889 WHERE g.`id_customer` = ' . (int) $this->id . '
890 GROUP BY c.`id_connections`
891 ORDER BY c.date_add DESC
892 LIMIT 10'
893 );
894 }
895
896 /**
897 * Check if Customer ID exists.
898 *
899 * @param int $idCustomer Customer ID
900 *
901 * @return int|null Customer ID if found
902 */
903 public static function customerIdExistsStatic($idCustomer)
904 {
905 $cacheId = 'Customer::customerIdExistsStatic' . (int) $idCustomer;
906 if (!Cache::isStored($cacheId)) {
907 $result = (int) Db::getInstance()->getValue('
908 SELECT `id_customer`
909 FROM ' . _DB_PREFIX_ . 'customer c
910 WHERE c.`id_customer` = ' . (int) $idCustomer);
911 Cache::store($cacheId, $result);
912
913 return $result;
914 }
915
916 return Cache::retrieve($cacheId);
917 }
918
919 /**
920 * Update customer groups associated to the object.
921 *
922 * @param array $list groups
923 */
924 public function updateGroup($list)
925 {
926 Hook::exec('actionCustomerBeforeUpdateGroup', array('id_customer' => $this->id, 'groups' => $list));
927 if ($list && !empty($list)) {
928 $this->cleanGroups();
929 $this->addGroups($list);
930 } else {
931 $this->addGroups(array($this->id_default_group));
932 }
933 }
934
935 /**
936 * Remove this Customer ID from Customer Groups.
937 *
938 * @return bool Indicates whether the Customer ID has been successfully removed
939 * from the Customer Group Db table
940 */
941 public function cleanGroups()
942 {
943 return Db::getInstance()->delete('customer_group', 'id_customer = ' . (int) $this->id);
944 }
945
946 /**
947 * Add the Customer to the given Customer Groups.
948 *
949 * @param array $groups Customer Group IDs
950 */
951 public function addGroups($groups)
952 {
953 Hook::exec('actionCustomerAddGroups', array('id_customer' => $this->id, 'groups' => $groups));
954 foreach ($groups as $group) {
955 $row = array('id_customer' => (int) $this->id, 'id_group' => (int) $group);
956 Db::getInstance()->insert('customer_group', $row, false, true, Db::INSERT_IGNORE);
957 }
958 }
959
960 /**
961 * Get Groups that have the given Customer ID.
962 *
963 * @param int $idCustomer Customer ID
964 *
965 * @return array|mixed
966 */
967 public static function getGroupsStatic($idCustomer)
968 {
969 if (!Group::isFeatureActive()) {
970 return array(Configuration::get('PS_CUSTOMER_GROUP'));
971 }
972
973 if ($idCustomer == 0) {
974 self::$_customer_groups[$idCustomer] = array((int) Configuration::get('PS_UNIDENTIFIED_GROUP'));
975 }
976
977 if (!isset(self::$_customer_groups[$idCustomer])) {
978 self::$_customer_groups[$idCustomer] = array();
979 $result = Db::getInstance()->executeS('
980 SELECT cg.`id_group`
981 FROM ' . _DB_PREFIX_ . 'customer_group cg
982 WHERE cg.`id_customer` = ' . (int) $idCustomer);
983 foreach ($result as $group) {
984 self::$_customer_groups[$idCustomer][] = (int) $group['id_group'];
985 }
986 }
987
988 return self::$_customer_groups[$idCustomer];
989 }
990
991 public function getGroups()
992 {
993 return Customer::getGroupsStatic((int) $this->id);
994 }
995
996 /**
997 * Get Products bought by this Customer.
998 *
999 * @return array|false|mysqli_result|null|PDOStatement|resource
1000 */
1001 public function getBoughtProducts()
1002 {
1003 return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
1004 SELECT * FROM `' . _DB_PREFIX_ . 'orders` o
1005 LEFT JOIN `' . _DB_PREFIX_ . 'order_detail` od ON o.id_order = od.id_order
1006 WHERE o.valid = 1 AND o.`id_customer` = ' . (int) $this->id);
1007 }
1008
1009 /**
1010 * Get Default Customer Group ID.
1011 *
1012 * @param int $idCustomer Customer ID
1013 *
1014 * @return mixed|null|string
1015 */
1016 public static function getDefaultGroupId($idCustomer)
1017 {
1018 if (!Group::isFeatureActive()) {
1019 static $psCustomerGroup = null;
1020 if ($psCustomerGroup === null) {
1021 $psCustomerGroup = Configuration::get('PS_CUSTOMER_GROUP');
1022 }
1023
1024 return $psCustomerGroup;
1025 }
1026
1027 if (!isset(self::$_defaultGroupId[(int) $idCustomer])) {
1028 self::$_defaultGroupId[(int) $idCustomer] = Db::getInstance()->getValue('
1029 SELECT `id_default_group`
1030 FROM `' . _DB_PREFIX_ . 'customer`
1031 WHERE `id_customer` = ' . (int) $idCustomer
1032 );
1033 }
1034
1035 return self::$_defaultGroupId[(int) $idCustomer];
1036 }
1037
1038 /**
1039 * Get current Country.
1040 *
1041 * @param int $idCustomer
1042 * @param Cart|null $cart
1043 *
1044 * @return int Country ID
1045 */
1046 public static function getCurrentCountry($idCustomer, Cart $cart = null)
1047 {
1048 if (!$cart) {
1049 $cart = Context::getContext()->cart;
1050 }
1051 if (!$cart || !$cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) {
1052 $idAddress = (int) Db::getInstance()->getValue('
1053 SELECT `id_address`
1054 FROM `' . _DB_PREFIX_ . 'address`
1055 WHERE `id_customer` = ' . (int) $idCustomer . '
1056 AND `deleted` = 0 ORDER BY `id_address`'
1057 );
1058 } else {
1059 $idAddress = $cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')};
1060 }
1061 $ids = Address::getCountryAndState($idAddress);
1062
1063 return (int) ($ids['id_country'] ? $ids['id_country'] : Configuration::get('PS_COUNTRY_DEFAULT'));
1064 }
1065
1066 /**
1067 * Is the current Customer a Guest?
1068 *
1069 * @return bool Indicates whether the Customer is a Guest
1070 */
1071 public function isGuest()
1072 {
1073 return (bool) $this->is_guest;
1074 }
1075
1076 /**
1077 * Transform the Guest to a Customer.
1078 *
1079 * @param int $idLang Language ID
1080 * @param string|null $password Password
1081 *
1082 * @return bool
1083 */
1084 public function transformToCustomer($idLang, $password = null)
1085 {
1086 if (!$this->isGuest()) {
1087 return false;
1088 }
1089 if (empty($password)) {
1090 $password = Tools::passwdGen(8, 'RANDOM');
1091 }
1092 if (!Validate::isPasswd($password)) {
1093 return false;
1094 }
1095
1096 $language = new Language($idLang);
1097 if (!Validate::isLoadedObject($language)) {
1098 $language = Context::getContext()->language;
1099 }
1100
1101 /** @var \PrestaShop\PrestaShop\Core\Crypto\Hashing $crypto */
1102 $crypto = ServiceLocator::get('\\PrestaShop\\PrestaShop\\Core\\Crypto\\Hashing');
1103 $this->is_guest = 0;
1104 $this->passwd = $crypto->hash($password);
1105 $this->cleanGroups();
1106 $this->addGroups(array(Configuration::get('PS_CUSTOMER_GROUP')));
1107 $this->id_default_group = Configuration::get('PS_CUSTOMER_GROUP');
1108 if ($this->update()) {
1109 $vars = array(
1110 '{firstname}' => $this->firstname,
1111 '{lastname}' => $this->lastname,
1112 '{email}' => $this->email,
1113 );
1114 Mail::Send(
1115 (int) $idLang,
1116 'guest_to_customer',
1117 Context::getContext()->getTranslator()->trans(
1118 'Your guest account has been transformed into a customer account',
1119 array(),
1120 'Emails.Subject',
1121 $language->locale
1122 ),
1123 $vars,
1124 $this->email,
1125 $this->firstname . ' ' . $this->lastname,
1126 null,
1127 null,
1128 null,
1129 null,
1130 _PS_MAIL_DIR_,
1131 false,
1132 (int) $this->id_shop
1133 );
1134
1135 return true;
1136 }
1137
1138 return false;
1139 }
1140
1141 /**
1142 * Set password
1143 * (for webservice).
1144 *
1145 * @param string $passwd Password
1146 *
1147 * @return bool Indictes whether the password has been successfully set
1148 */
1149 public function setWsPasswd($passwd)
1150 {
1151 /** @var \PrestaShop\PrestaShop\Core\Crypto\Hashing $crypto */
1152 $crypto = ServiceLocator::get('\\PrestaShop\\PrestaShop\\Core\\Crypto\\Hashing');
1153 if ($this->id == 0 || $this->passwd != $passwd) {
1154 $this->passwd = $crypto->hash($passwd);
1155 }
1156
1157 return true;
1158 }
1159
1160 /**
1161 * Check customer information and return customer validity.
1162 *
1163 * @since 1.5.0
1164 *
1165 * @param bool $withGuest
1166 *
1167 * @return bool customer validity
1168 */
1169 public function isLogged($withGuest = false)
1170 {
1171 if (!$withGuest && $this->is_guest == 1) {
1172 return false;
1173 }
1174
1175 /* Customer is valid only if it can be load and if object password is the same as database one */
1176 return $this->logged == 1 && $this->id && Validate::isUnsignedId($this->id) && Customer::checkPassword($this->id, $this->passwd);
1177 }
1178
1179 /**
1180 * Logout.
1181 *
1182 * @since 1.5.0
1183 */
1184 public function logout()
1185 {
1186 Hook::exec('actionCustomerLogoutBefore', array('customer' => $this));
1187
1188 if (isset(Context::getContext()->cookie)) {
1189 Context::getContext()->cookie->logout();
1190 }
1191
1192 $this->logged = 0;
1193
1194 Hook::exec('actionCustomerLogoutAfter', array('customer' => $this));
1195 }
1196
1197 /**
1198 * Soft logout, delete everything that links to the customer
1199 * but leave there affiliate's information.
1200 *
1201 * @since 1.5.0
1202 */
1203 public function mylogout()
1204 {
1205 Hook::exec('actionCustomerLogoutBefore', array('customer' => $this));
1206
1207 if (isset(Context::getContext()->cookie)) {
1208 Context::getContext()->cookie->mylogout();
1209 }
1210
1211 $this->logged = 0;
1212
1213 Hook::exec('actionCustomerLogoutAfter', array('customer' => $this));
1214 }
1215
1216 /**
1217 * Get last empty Cart for this Customer, when last cart is not empty return false.
1218 *
1219 * @param bool|true $withOrder
1220 *
1221 * @return bool|int
1222 */
1223 public function getLastEmptyCart($withOrder = true)
1224 {
1225 $carts = Cart::getCustomerCarts((int) $this->id, $withOrder);
1226 if (!count($carts)) {
1227 return false;
1228 }
1229 $cart = array_shift($carts);
1230 $cart = new Cart((int) $cart['id_cart']);
1231
1232 return $cart->nbProducts() === 0 ? (int) $cart->id : false;
1233 }
1234
1235 /**
1236 * Validate controller.
1237 *
1238 * @param bool $htmlentities
1239 *
1240 * @return array
1241 */
1242 public function validateController($htmlentities = true)
1243 {
1244 $errors = parent::validateController($htmlentities);
1245 /** @var \PrestaShop\PrestaShop\Core\Crypto\Hashing $crypto */
1246 $crypto = ServiceLocator::get('\\PrestaShop\\PrestaShop\\Core\\Crypto\\Hashing');
1247 if ($value = Tools::getValue('passwd')) {
1248 $this->passwd = $crypto->hash($value);
1249 }
1250
1251 return $errors;
1252 }
1253
1254 /**
1255 * Get outstanding amount.
1256 *
1257 * @return float Outstanding amount
1258 */
1259 public function getOutstanding()
1260 {
1261 $query = new DbQuery();
1262 $query->select('SUM(oi.total_paid_tax_incl)');
1263 $query->from('order_invoice', 'oi');
1264 $query->leftJoin('orders', 'o', 'oi.id_order = o.id_order');
1265 $query->groupBy('o.id_customer');
1266 $query->where('o.id_customer = ' . (int) $this->id);
1267 $totalPaid = (float) Db::getInstance()->getValue($query->build());
1268
1269 $query = new DbQuery();
1270 $query->select('SUM(op.amount)');
1271 $query->from('order_payment', 'op');
1272 $query->leftJoin('order_invoice_payment', 'oip', 'op.id_order_payment = oip.id_order_payment');
1273 $query->leftJoin('orders', 'o', 'oip.id_order = o.id_order');
1274 $query->groupBy('o.id_customer');
1275 $query->where('o.id_customer = ' . (int) $this->id);
1276 $totalRest = (float) Db::getInstance()->getValue($query->build());
1277
1278 return $totalPaid - $totalRest;
1279 }
1280
1281 /**
1282 * Get Customer Groups
1283 * (for webservice).
1284 *
1285 * @return array|false|mysqli_result|null|PDOStatement|resource
1286 */
1287 public function getWsGroups()
1288 {
1289 return Db::getInstance()->executeS('
1290 SELECT cg.`id_group` as id
1291 FROM ' . _DB_PREFIX_ . 'customer_group cg
1292 ' . Shop::addSqlAssociation('group', 'cg') . '
1293 WHERE cg.`id_customer` = ' . (int) $this->id
1294 );
1295 }
1296
1297 /**
1298 * Set Customer Groups
1299 * (for webservice).
1300 *
1301 * @param $result
1302 *
1303 * @return bool
1304 */
1305 public function setWsGroups($result)
1306 {
1307 $groups = array();
1308 foreach ($result as $row) {
1309 $groups[] = $row['id'];
1310 }
1311 $this->cleanGroups();
1312 $this->addGroups($groups);
1313
1314 return true;
1315 }
1316
1317 /**
1318 * @see ObjectModel::getWebserviceObjectList()
1319 */
1320 public function getWebserviceObjectList($sqlJoin, $sqlFilter, $sqlSort, $sqlLimit)
1321 {
1322 $sqlFilter .= Shop::addSqlRestriction(Shop::SHARE_CUSTOMER, 'main');
1323
1324 return parent::getWebserviceObjectList($sqlJoin, $sqlFilter, $sqlSort, $sqlLimit);
1325 }
1326
1327 /**
1328 * Fill Reset password unique token with random sha1 and its validity date. For forgot password feature.
1329 */
1330 public function stampResetPasswordToken()
1331 {
1332 $salt = $this->id . '-' . $this->secure_key;
1333 $this->reset_password_token = sha1(time() . $salt);
1334 $validity = (int) Configuration::get('PS_PASSWD_RESET_VALIDITY') ?: 1440;
1335 $this->reset_password_validity = date('Y-m-d H:i:s', strtotime('+' . $validity . ' minutes'));
1336 }
1337
1338 /**
1339 * Test if a reset password token is present and is recent enough to avoid creating a new one (in case of customer triggering the forgot password link too often).
1340 */
1341 public function hasRecentResetPasswordToken()
1342 {
1343 if (!$this->reset_password_token || $this->reset_password_token == '') {
1344 return false;
1345 }
1346
1347 // TODO maybe use another 'recent' value for this test. For instance, equals password validity value.
1348 if (!$this->reset_password_validity || strtotime($this->reset_password_validity) < time()) {
1349 return false;
1350 }
1351
1352 return true;
1353 }
1354
1355 /**
1356 * Returns the valid reset password token if it validity date is > now().
1357 */
1358 public function getValidResetPasswordToken()
1359 {
1360 if (!$this->reset_password_token || $this->reset_password_token == '') {
1361 return false;
1362 }
1363
1364 if (!$this->reset_password_validity || strtotime($this->reset_password_validity) < time()) {
1365 return false;
1366 }
1367
1368 return $this->reset_password_token;
1369 }
1370
1371 /**
1372 * Delete reset password token data.
1373 */
1374 public function removeResetPasswordToken()
1375 {
1376 $this->reset_password_token = null;
1377 $this->reset_password_validity = null;
1378 }
1379}