/home/sharedstore/public_html/prestashop.sharedstore.ma/classes
NameSizeModeActions
assets/-0755rm
cache/-0755rm
checkout/-0755rm
container/-0755rm
controller/-0755rm
db/-0755rm
exception/-0755rm
form/-0755rm
helper/-0755rm
lang/-0755rm
log/-0755rm
module/-0755rm
order/-0755rm
pdf/-0755rm
product/-0755rm
proxy/-0755rm
range/-0755rm
shop/-0755rm
Smarty/-0755rm
stock/-0755rm
tax/-0755rm
tree/-0755rm
webservice/-0755rm
.htaccess1700644editdlrm
Access.php135730644editdlrm
Address.php230160644editdlrm
AddressChecksumCore.php18510644editdlrm
AddressFormat.php251250644editdlrm
Alias.php51060644editdlrm
Attachment.php115570644editdlrm
AttributeGroup.php134880644editdlrm
Carrier.php656660644editdlrm
Cart.php2211920644editdlrm
CartRule.php927650644editdlrm
Category.php891710644editdlrm
Chart.php41770644editdlrm
ChecksumInterface.php12460644editdlrm
CMS.php117250644editdlrm
CMSCategory.php241480644editdlrm
CMSRole.php18220644editdlrm
Combination.php180120644editdlrm
Configuration.php271560644editdlrm
ConfigurationKPI.php91260644editdlrm
ConfigurationTest.php123090644editdlrm
Connection.php102620644editdlrm
ConnectionsSource.php49260644editdlrm
Contact.php39540644editdlrm
Context.php183720644editdlrm
Cookie.php178060644editdlrm
Country.php167230644editdlrm
CSV.php31100644editdlrm
Currency.php371670644editdlrm
Curve.php29930644editdlrm
Customer.php535760644editdlrm
CustomerAddress.php12950644editdlrm
CustomerMessage.php58900644editdlrm
CustomerSession.php27180644editdlrm
CustomerThread.php98480644editdlrm
Customization.php137540644editdlrm
CustomizationField.php29150644editdlrm
DateRange.php25320644editdlrm
Delivery.php33170644editdlrm
Dispatcher.php488100644editdlrm
Employee.php240620644editdlrm
EmployeeSession.php27180644editdlrm
Feature.php116560644editdlrm
FeatureFlag.php32260644editdlrm
FeatureValue.php82490644editdlrm
FileUploader.php38200644editdlrm
Gender.php28760644editdlrm
Group.php153230644editdlrm
GroupReduction.php90250644editdlrm
Guest.php85290644editdlrm
Hook.php509090644editdlrm
Image.php324180644editdlrm
ImageManager.php325490644editdlrm
ImageType.php71550644editdlrm
index.php13720644editdlrm
Language.php635470644editdlrm
Link.php638140644editdlrm
LocalizationPack.php236040644editdlrm
Mail.php343120644editdlrm
Manufacturer.php217450644editdlrm
ManufacturerAddress.php13110644editdlrm
Media.php330210644editdlrm
Message.php66260644editdlrm
Meta.php185770644editdlrm
Notification.php84700644editdlrm
ObjectModel.php837730644editdlrm
Pack.php263320644editdlrm
Page.php45980644editdlrm
PaymentFree.php13620644editdlrm
PaymentModule.php611970644editdlrm
PhpEncryption.php32910644editdlrm
PhpEncryptionEngine.php39490644editdlrm
PrestaShopAutoload.php97990644editdlrm
PrestaShopBackup.php117020644editdlrm
PrestaShopCollection.php217900644editdlrm
PrestaShopLogger.php85230644editdlrm
Product.php3180560644editdlrm
ProductAssembler.php68950644editdlrm
ProductAttribute.php138080644editdlrm
ProductDownload.php102940644editdlrm
ProductPresenterFactory.php39830644editdlrm
ProductSale.php130000644editdlrm
ProductSupplier.php85370644editdlrm
Profile.php85230644editdlrm
QqUploadedFileForm.php53170644editdlrm
QqUploadedFileXhr.php58250644editdlrm
QuickAccess.php64020644editdlrm
RequestSql.php214530644editdlrm
Risk.php24190644editdlrm
Search.php592290644editdlrm
SearchEngine.php28790644editdlrm
SpecificPrice.php332120644editdlrm
SpecificPriceRule.php143740644editdlrm
State.php79560644editdlrm
Store.php63940644editdlrm
Supplier.php183390644editdlrm
SupplierAddress.php12990644editdlrm
Tab.php222230644editdlrm
Tag.php135580644editdlrm
Tools.php1409030644editdlrm
Translate.php120590644editdlrm
TranslatedConfiguration.php47920644editdlrm
Upgrader.php118590644editdlrm
Uploader.php111040644editdlrm
Validate.php398010644editdlrm
ValidateConstraintTranslator.php26110644editdlrm
WarehouseAddress.php13020644editdlrm
Zone.php35000644editdlrm
Edit: /home/sharedstore/public_html/prestashop.sharedstore.ma/classes/Customer.php (53576B)
* @copyright Since 2007 PrestaShop SA and Contributors * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) */ use PrestaShop\PrestaShop\Adapter\CoreException; use PrestaShop\PrestaShop\Adapter\ServiceLocator; use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\InvalidShopConstraintException; use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint; /*** * Class CustomerCore */ class CustomerCore extends ObjectModel { /** @var int Customer ID */ public $id; /** @var int Shop ID */ public $id_shop; /** @var int ShopGroup ID */ public $id_shop_group; /** @var string Secure key */ public $secure_key; /** @var string protected note */ public $note; /** @var int Gender ID */ public $id_gender = 0; /** @var int Default group ID */ public $id_default_group; /** @var int Current language used by the customer */ public $id_lang; /** @var string Lastname */ public $lastname; /** @var string Firstname */ public $firstname; /** @var string Birthday (yyyy-mm-dd) */ public $birthday = null; /** @var string e-mail */ public $email; /** @var bool Newsletter subscription */ public $newsletter; /** @var string Newsletter ip registration */ public $ip_registration_newsletter; /** @var string Newsletter registration date */ public $newsletter_date_add; /** @var bool Opt-in subscription */ public $optin; /** @var string WebSite * */ public $website; /** @var string Company */ public $company; /** @var string SIRET */ public $siret; /** @var string APE */ public $ape; /** @var float Outstanding allow amount (B2B opt) */ public $outstanding_allow_amount = 0; /** @var int Show public prices (B2B opt) */ public $show_public_prices = 0; /** @var int Risk ID (B2B opt) */ public $id_risk; /** @var int Max payment day */ public $max_payment_days = 0; /** @var string Password */ public $passwd; /** @var string Datetime Password */ public $last_passwd_gen; /** @var bool Status */ public $active = true; /** @var bool Status */ public $is_guest = false; /** @var bool True if carrier has been deleted (staying in database as deleted) */ public $deleted = false; /** @var string|null Object creation date */ public $date_add; /** @var string Object last modification date */ public $date_upd; public $years; public $days; public $months; /** @var int customer id_country as determined by geolocation */ public $geoloc_id_country; /** @var int customer id_state as determined by geolocation */ public $geoloc_id_state; /** @var string customer postcode as determined by geolocation */ public $geoloc_postcode; /** @var bool is the customer logged in */ public $logged = false; /** @var int id_guest meaning the guest table, not the guest customer */ public $id_guest; public $groupBox; /** @var string|null Unique token for forgot password feature */ public $reset_password_token; /** @var string|null token validity date for forgot password feature */ public $reset_password_validity; protected $webserviceParameters = [ 'objectMethods' => [ 'add' => 'addWs', 'update' => 'updateWs', ], 'fields' => [ 'id_default_group' => ['xlink_resource' => 'groups'], 'id_lang' => ['xlink_resource' => 'languages'], 'newsletter_date_add' => [], 'ip_registration_newsletter' => [], 'last_passwd_gen' => ['setter' => null], 'secure_key' => ['setter' => null], 'deleted' => [], 'passwd' => ['setter' => 'setWsPasswd'], ], 'associations' => [ 'groups' => ['resource' => 'group'], ], ]; /** * @see ObjectModel::$definition */ public static $definition = [ 'table' => 'customer', 'primary' => 'id_customer', 'fields' => [ 'secure_key' => ['type' => self::TYPE_STRING, 'validate' => 'isMd5', 'copy_post' => false], 'lastname' => ['type' => self::TYPE_STRING, 'validate' => 'isCustomerName', 'required' => true, 'size' => 255], 'firstname' => ['type' => self::TYPE_STRING, 'validate' => 'isCustomerName', 'required' => true, 'size' => 255], 'email' => ['type' => self::TYPE_STRING, 'validate' => 'isEmail', 'required' => true, 'size' => 255], 'passwd' => ['type' => self::TYPE_STRING, 'validate' => 'isHashedPassword', 'required' => true, 'size' => 255], 'last_passwd_gen' => ['type' => self::TYPE_STRING, 'copy_post' => false], 'id_gender' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId'], 'birthday' => ['type' => self::TYPE_DATE, 'validate' => 'isBirthDate'], 'newsletter' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], 'newsletter_date_add' => ['type' => self::TYPE_DATE, 'copy_post' => false], 'ip_registration_newsletter' => ['type' => self::TYPE_STRING, 'copy_post' => false], 'optin' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], 'website' => ['type' => self::TYPE_STRING, 'validate' => 'isUrl'], 'company' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName'], 'siret' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName'], 'ape' => ['type' => self::TYPE_STRING, 'validate' => 'isApe'], 'outstanding_allow_amount' => ['type' => self::TYPE_FLOAT, 'validate' => 'isFloat', 'copy_post' => false], 'show_public_prices' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'copy_post' => false], 'id_risk' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'copy_post' => false], 'max_payment_days' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'copy_post' => false], 'active' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'copy_post' => false], 'deleted' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'copy_post' => false], 'note' => ['type' => self::TYPE_HTML, 'size' => 4194303, 'copy_post' => false], 'is_guest' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'copy_post' => false], 'id_shop' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'copy_post' => false], 'id_shop_group' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'copy_post' => false], 'id_default_group' => ['type' => self::TYPE_INT, 'copy_post' => false], 'id_lang' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'copy_post' => false], 'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate', 'copy_post' => false], 'date_upd' => ['type' => self::TYPE_DATE, 'validate' => 'isDate', 'copy_post' => false], 'reset_password_token' => ['type' => self::TYPE_STRING, 'validate' => 'isSha1', 'size' => 40, 'copy_post' => false], 'reset_password_validity' => ['type' => self::TYPE_DATE, 'validate' => 'isDateOrNull', 'copy_post' => false], ], ]; protected static $_defaultGroupId = []; protected static $_customerHasAddress = []; protected static $_customer_groups = []; /** * CustomerCore constructor. * * @param int|null $id */ public function __construct($id = null) { // It sets default value for customer group even when customer does not exist $this->id_default_group = (int) Configuration::get('PS_CUSTOMER_GROUP'); parent::__construct($id); } /** * Adds current Customer as a new Object to the database. * * @param bool $autoDate Automatically set `date_upd` and `date_add` columns * @param bool $nullValues Whether we want to use NULL values instead of empty quotes values * * @return bool Indicates whether the Customer has been successfully added * * @throws PrestaShopDatabaseException * @throws PrestaShopException */ public function add($autoDate = true, $nullValues = true) { $this->id_shop = ($this->id_shop) ? $this->id_shop : Context::getContext()->shop->id; $this->id_shop_group = ($this->id_shop_group) ? $this->id_shop_group : Context::getContext()->shop->id_shop_group; $this->id_lang = ($this->id_lang) ? $this->id_lang : Context::getContext()->language->id; $this->birthday = (empty($this->years) ? $this->birthday : (int) $this->years . '-' . (int) $this->months . '-' . (int) $this->days); $this->secure_key = md5(uniqid((string) mt_rand(0, mt_getrandmax()), true)); $this->last_passwd_gen = date('Y-m-d H:i:s', strtotime('-' . Configuration::get('PS_PASSWD_TIME_FRONT') . 'minutes')); if ($this->newsletter && !Validate::isDate($this->newsletter_date_add)) { $this->newsletter_date_add = date('Y-m-d H:i:s'); } // Set default group of the customer depending on prestashop configuration if ($this->id_default_group == Configuration::get('PS_CUSTOMER_GROUP')) { if ($this->is_guest) { $this->id_default_group = (int) Configuration::get('PS_GUEST_GROUP'); } else { $this->id_default_group = (int) Configuration::get('PS_CUSTOMER_GROUP'); } } /* Can't create a guest customer, if this feature is disabled */ if ($this->is_guest && !Configuration::get('PS_GUEST_CHECKOUT_ENABLED')) { return false; } $success = parent::add($autoDate, $nullValues); // Update the group assignments themselves $this->updateGroup($this->groupBox); return $success; } /** * Adds current Customer as a new Object to the database. * * @param bool $autodate Automatically set `date_upd` and `date_add` columns * @param bool $null_values Whether we want to use NULL values instead of empty quotes values * * @return bool Indicates whether the Customer has been successfully added * * @throws PrestaShopDatabaseException * @throws PrestaShopException */ public function addWs($autodate = true, $null_values = false) { if (Customer::customerExists($this->email)) { WebserviceRequest::getInstance()->setError( 500, $this->trans( 'The email is already used, please choose another one', [], 'Admin.Notifications.Error' ), 140 ); return false; } return $this->add($autodate, $null_values); } /** * Updates the current Customer in the database. * * @param bool $nullValues Whether we want to use NULL values instead of empty quotes values * * @return bool Indicates whether the Customer has been successfully updated * * @throws PrestaShopDatabaseException * @throws PrestaShopException */ public function update($nullValues = false) { $this->birthday = (empty($this->years) ? $this->birthday : (int) $this->years . '-' . (int) $this->months . '-' . (int) $this->days); if ($this->newsletter && !Validate::isDate($this->newsletter_date_add)) { $this->newsletter_date_add = date('Y-m-d H:i:s'); } if (isset(Context::getContext()->controller) && Context::getContext()->controller->controller_type == 'admin') { $this->updateGroup($this->groupBox); } if ($this->deleted) { $addresses = $this->getAddresses((int) Configuration::get('PS_LANG_DEFAULT')); foreach ($addresses as $address) { $obj = new Address((int) $address['id_address']); $obj->deleted = true; $obj->save(); } } try { return parent::update(true); } catch (\PrestaShopException $exception) { $message = $exception->getMessage(); error_log($message); return false; } } /** * Updates the current Customer in the database. * * @param bool $nullValues Whether we want to use NULL values instead of empty quotes values * * @return bool Indicates whether the Customer has been successfully updated * * @throws PrestaShopDatabaseException * @throws PrestaShopException */ public function updateWs($nullValues = false) { if (Customer::customerExists($this->email) && Customer::customerExists($this->email, true) !== (int) $this->id ) { WebserviceRequest::getInstance()->setError( 500, $this->trans( 'The email is already used, please choose another one', [], 'Admin.Notifications.Error' ), 141 ); return false; } return $this->update($nullValues = false); } /** * Deletes current Customer from the database. * * @return bool True if delete was successful * * @throws PrestaShopException */ public function delete() { if (!count(Order::getCustomerOrders((int) $this->id))) { $addresses = $this->getAddresses((int) Configuration::get('PS_LANG_DEFAULT')); foreach ($addresses as $address) { $obj = new Address((int) $address['id_address']); $obj->delete(); } } Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'customer_group` WHERE `id_customer` = ' . (int) $this->id); Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'message WHERE id_customer=' . (int) $this->id); Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'specific_price WHERE id_customer=' . (int) $this->id); $carts = Db::getInstance()->executeS('SELECT id_cart FROM ' . _DB_PREFIX_ . 'cart WHERE id_customer=' . (int) $this->id . ' AND id_cart NOT IN (SELECT id_cart FROM `' . _DB_PREFIX_ . 'orders`)'); if ($carts) { foreach ($carts as $cart) { Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'cart WHERE id_cart=' . (int) $cart['id_cart']); Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'cart_product WHERE id_cart=' . (int) $cart['id_cart']); } } $cts = Db::getInstance()->executeS('SELECT id_customer_thread FROM ' . _DB_PREFIX_ . 'customer_thread WHERE id_customer=' . (int) $this->id); if ($cts) { foreach ($cts as $ct) { Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'customer_thread WHERE id_customer_thread=' . (int) $ct['id_customer_thread']); Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'customer_message WHERE id_customer_thread=' . (int) $ct['id_customer_thread']); } } CartRule::deleteByIdCustomer((int) $this->id); return parent::delete(); } /** * Return customers list. * * @param bool|null $onlyActive Returns only active customers when `true` * * @return array Customers */ public static function getCustomers($onlyActive = null) { return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS( ' SELECT `id_customer`, `email`, `firstname`, `lastname` FROM `' . _DB_PREFIX_ . 'customer` WHERE 1 ' . Shop::addSqlRestriction(Shop::SHARE_CUSTOMER) . ($onlyActive ? ' AND `active` = 1' : '') . ' ORDER BY `id_customer` ASC' ); } /** * Return customer instance from its e-mail (optionally check password). * * @param string $email e-mail * @param string $plaintextPassword Password is also checked if specified * @param bool $ignoreGuest to ignore guest customers * * @return bool|Customer|CustomerCore Customer instance * * @throws \InvalidArgumentException if given input is not valid */ public function getByEmail($email, $plaintextPassword = null, $ignoreGuest = true) { if (!Validate::isEmail($email)) { throw new \InvalidArgumentException(sprintf( 'Cannot get customer by email as %s is not a valid email', $email )); } $shopGroup = Shop::getGroupFromShop(Shop::getContextShopID(), false); $sql = new DbQuery(); $sql->select('c.`passwd`'); $sql->from('customer', 'c'); $sql->where('c.`email` = \'' . pSQL($email) . '\''); if (Shop::getContext() == Shop::CONTEXT_SHOP && $shopGroup['share_customer']) { $sql->where('c.`id_shop_group` = ' . (int) Shop::getContextShopGroupID()); } else { $sql->where('c.`id_shop` IN (' . implode(', ', Shop::getContextListShopID(Shop::SHARE_CUSTOMER)) . ')'); } if ($ignoreGuest) { $sql->where('c.`is_guest` = 0'); } $sql->where('c.`deleted` = 0'); $passwordHash = Db::getInstance()->getValue($sql); try { /** @var \PrestaShop\PrestaShop\Core\Crypto\Hashing $crypto */ $crypto = ServiceLocator::get('\\PrestaShop\\PrestaShop\\Core\\Crypto\\Hashing'); } catch (CoreException $e) { return false; } $shouldCheckPassword = null !== $plaintextPassword; if ($shouldCheckPassword && !$crypto->checkHash($plaintextPassword, $passwordHash)) { return false; } $sql = new DbQuery(); $sql->select('c.*'); $sql->from('customer', 'c'); $sql->where('c.`email` = \'' . pSQL($email) . '\''); if (Shop::getContext() == Shop::CONTEXT_SHOP && $shopGroup['share_customer']) { $sql->where('c.`id_shop_group` = ' . (int) Shop::getContextShopGroupID()); } else { $sql->where('c.`id_shop` IN (' . implode(', ', Shop::getContextListShopID(Shop::SHARE_CUSTOMER)) . ')'); } if ($ignoreGuest) { $sql->where('c.`is_guest` = 0'); } $sql->where('c.`deleted` = 0'); $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql); if (!$result) { return false; } $this->id = $result['id_customer']; foreach ($result as $key => $value) { if (property_exists($this, $key)) { $this->{$key} = $value; } } if ($shouldCheckPassword && !$crypto->isFirstHash($plaintextPassword, $passwordHash)) { $this->passwd = $crypto->hash($plaintextPassword); $this->update(); } return $this; } /** * Retrieve customers by email address. * * @param string $email * * @return array Customers */ public static function getCustomersByEmail($email) { $sql = 'SELECT * FROM `' . _DB_PREFIX_ . 'customer` WHERE `email` = \'' . pSQL($email) . '\' ' . Shop::addSqlRestriction(Shop::SHARE_CUSTOMER); return Db::getInstance()->executeS($sql); } /** * Check id the customer is active or not. * * @param int $idCustomer * * @return bool Customer validity */ public static function isBanned($idCustomer) { if (!Validate::isUnsignedId($idCustomer)) { return true; } $cacheId = 'Customer::isBanned_' . (int) $idCustomer; if (!Cache::isStored($cacheId)) { $result = (bool) !Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(' SELECT `id_customer` FROM `' . _DB_PREFIX_ . 'customer` WHERE `id_customer` = \'' . (int) $idCustomer . '\' AND active = 1 AND `deleted` = 0'); Cache::store($cacheId, $result); return $result; } return Cache::retrieve($cacheId); } /** * Check if e-mail is already registered in database. * * @param string $email e-mail * @param bool $returnId If true the method returns the Customer ID, or boolean * @param bool $ignoreGuest to ignore guest customers * * @return bool|int Customer ID if found * `false` otherwise */ public static function customerExists($email, $returnId = false, $ignoreGuest = true) { if (!Validate::isEmail($email)) { return false; } $result = Db::getInstance()->getValue(' SELECT `id_customer` FROM `' . _DB_PREFIX_ . 'customer` WHERE `email` = \'' . pSQL($email) . '\' ' . Shop::addSqlRestriction(Shop::SHARE_CUSTOMER) . ' ' . ($ignoreGuest ? ' AND `is_guest` = 0' : ''), false); return $returnId ? (int) $result : (bool) $result; } /** * Check if an address is owned by a customer. * * @param int $idCustomer Customer ID * @param int $idAddress Address ID * * @return bool result */ public static function customerHasAddress($idCustomer, $idAddress) { $key = (int) $idCustomer . '-' . (int) $idAddress; if (!array_key_exists($key, self::$_customerHasAddress)) { self::$_customerHasAddress[$key] = (bool) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' SELECT `id_address` FROM `' . _DB_PREFIX_ . 'address` WHERE `id_customer` = ' . (int) $idCustomer . ' AND `id_address` = ' . (int) $idAddress . ' AND `deleted` = 0'); } return self::$_customerHasAddress[$key]; } public static function resetStaticCache() { self::$_customerHasAddress = []; self::$_customer_groups = []; self::$_defaultGroupId = []; } /** * Reset Address cache. * * @param int $idCustomer Customer ID * @param int $idAddress Address ID */ public static function resetAddressCache($idCustomer = null, $idAddress = null) { if ($idCustomer === null || $idAddress === null) { self::$_customerHasAddress = []; self::$_customer_groups = []; self::$_defaultGroupId = []; } $key = (int) $idCustomer . '-' . (int) $idAddress; if (array_key_exists($key, self::$_customerHasAddress)) { unset(self::$_customerHasAddress[$key]); } } /** * Return customer addresses. * * @param int $idLang Language ID * * @return array Addresses */ public function getAddresses($idLang) { $group = Context::getContext()->shop->getGroup(); $shareOrder = isset($group->share_order) ? (bool) $group->share_order : false; $cacheId = 'Customer::getAddresses' . '-' . (int) $this->id . '-' . (int) $idLang . '-' . ($shareOrder ? 1 : 0); if (!Cache::isStored($cacheId)) { $sql = 'SELECT DISTINCT a.*, cl.`name` AS country, s.name AS state, s.iso_code AS state_iso FROM `' . _DB_PREFIX_ . 'address` a LEFT JOIN `' . _DB_PREFIX_ . 'country` c ON (a.`id_country` = c.`id_country`) LEFT JOIN `' . _DB_PREFIX_ . 'country_lang` cl ON (c.`id_country` = cl.`id_country`) LEFT JOIN `' . _DB_PREFIX_ . 'state` s ON (s.`id_state` = a.`id_state`) ' . ($shareOrder ? '' : Shop::addSqlAssociation('country', 'c')) . ' WHERE `id_lang` = ' . (int) $idLang . ' AND `id_customer` = ' . (int) $this->id . ' AND a.`deleted` = 0'; $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); Cache::store($cacheId, $result); return $result; } return Cache::retrieve($cacheId); } /** * Get simplified Addresses arrays. * * @param int|null $idLang Language ID * * @return array */ public function getSimpleAddresses($idLang = null) { if (!$this->id) { return []; } if (null === $idLang) { $idLang = Context::getContext()->language->id; } $sql = $this->getSimpleAddressSql(null, $idLang); $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); $addresses = []; foreach ($result as $addr) { $addresses[$addr['id']] = $addr; } return $addresses; } /** * Get Address as array. * * @param int $idAddress Address ID * @param int|null $idLang Language ID * * @return array|false|mysqli_result|PDOStatement|resource|null */ public function getSimpleAddress($idAddress, $idLang = null) { if (!$this->id || !(int) $idAddress || !$idAddress) { return [ 'id' => '', 'alias' => '', 'firstname' => '', 'lastname' => '', 'company' => '', 'address1' => '', 'address2' => '', 'postcode' => '', 'city' => '', 'id_state' => '', 'state' => '', 'state_iso' => '', 'id_country' => '', 'country' => '', 'country_iso' => '', 'other' => '', 'phone' => '', 'phone_mobile' => '', 'vat_number' => '', 'dni' => '', ]; } $sql = $this->getSimpleAddressSql($idAddress, $idLang); $res = Db::getInstance()->executeS($sql); if (count($res) === 1) { return $res[0]; } else { return $res; } } /** * Get SQL query to retrieve Address in an array. * * @param int|null $idAddress Address ID * @param int|null $idLang Language ID * * @return string */ public function getSimpleAddressSql($idAddress = null, $idLang = null) { if (null === $idLang) { $idLang = Context::getContext()->language->id; } $shareOrder = (bool) Context::getContext()->shop->getGroup()->share_order; $sql = 'SELECT DISTINCT a.`id_address` AS `id`, a.`alias`, a.`firstname`, a.`lastname`, a.`company`, a.`address1`, a.`address2`, a.`postcode`, a.`city`, a.`id_state`, s.name AS state, s.`iso_code` AS state_iso, a.`id_country`, cl.`name` AS country, co.`iso_code` AS country_iso, a.`other`, a.`phone`, a.`phone_mobile`, a.`vat_number`, a.`dni` FROM `' . _DB_PREFIX_ . 'address` a LEFT JOIN `' . _DB_PREFIX_ . 'country` co ON (a.`id_country` = co.`id_country`) LEFT JOIN `' . _DB_PREFIX_ . 'country_lang` cl ON (co.`id_country` = cl.`id_country`) LEFT JOIN `' . _DB_PREFIX_ . 'state` s ON (s.`id_state` = a.`id_state`) ' . ($shareOrder ? '' : Shop::addSqlAssociation('country', 'co')) . ' WHERE `id_lang` = ' . (int) $idLang . ' AND `id_customer` = ' . (int) $this->id . ' AND a.`deleted` = 0 AND a.`active` = 1'; if (null !== $idAddress) { $sql .= ' AND a.`id_address` = ' . (int) $idAddress; } $sql .= ' ORDER BY a.`alias`'; return $sql; } /** * Count the number of addresses for a customer. * * @param int $idCustomer Customer ID * * @return int Number of addresses */ public static function getAddressesTotalById($idCustomer) { return (int) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue( 'SELECT COUNT(`id_address`) FROM `' . _DB_PREFIX_ . 'address` WHERE `id_customer` = ' . (int) $idCustomer . ' AND `deleted` = 0' ); } /** * Check if customer password is the right one. * * @param int $idCustomer Customer ID * @param string $passwordHash Hashed password * * @return bool result */ public static function checkPassword($idCustomer, $passwordHash) { if (!Validate::isUnsignedId($idCustomer)) { die(Tools::displayError('Customer ID is invalid.')); } // Check that customers password hasn't changed since last login $context = Context::getContext(); if ($passwordHash != $context->cookie->__get('passwd')) { return false; } $cacheId = 'Customer::checkPassword' . (int) $idCustomer . '-' . $passwordHash; if (!Cache::isStored($cacheId)) { $sql = new DbQuery(); $sql->select('c.`id_customer`'); $sql->from('customer', 'c'); $sql->where('c.`id_customer` = ' . (int) $idCustomer); $sql->where('c.`passwd` = \'' . pSQL($passwordHash) . '\''); $result = (bool) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql); Cache::store($cacheId, $result); return $result; } return Cache::retrieve($cacheId); } /** * Light back office search for customers. * * @param string $query Searched string * @param int|null $limit Limit query results * @param ShopConstraint|null $shopConstraint provide specific shop constraint or else it will use context shops for search * * @return array|false|mysqli_result|PDOStatement|resource|null Corresponding customers * * @throws PrestaShopDatabaseException */ public static function searchByName($query, $limit = null, ?ShopConstraint $shopConstraint = null) { $sql = 'SELECT c.*, GROUP_CONCAT(cg.id_group SEPARATOR \',\') AS group_ids FROM `' . _DB_PREFIX_ . 'customer` c LEFT JOIN `' . _DB_PREFIX_ . 'customer_group` cg ON c.id_customer = cg.id_customer WHERE 1'; if ($shopConstraint) { if ($shopConstraint->getShopGroupId()) { throw new InvalidShopConstraintException('Shop group constraint is not supported'); } if ($shopConstraint->getShopId()) { // filter by shop_id if its not all shops constraint $sql .= sprintf(' AND c.id_shop = %d', $shopConstraint->getShopId()->getValue()); } } $search_items = explode(' ', $query); $research_fields = ['c.id_customer', 'c.firstname', 'c.lastname', 'c.email']; if (Configuration::get('PS_B2B_ENABLE')) { $research_fields[] = 'c.company'; } $items = []; foreach ($research_fields as $field) { foreach ($search_items as $item) { $items[$item][] = $field . ' LIKE \'%' . pSQL($item) . '%\' '; } } foreach ($items as $likes) { $sql .= ' AND (' . implode(' OR ', $likes) . ') '; } if (!$shopConstraint) { // this is for backwards compatibility, it uses shop context if specific shopConstraint is not provided $sql .= Shop::addSqlRestriction(Shop::SHARE_CUSTOMER); } $sql .= ' GROUP BY c.id_customer '; if ($limit) { $sql .= ' LIMIT 0, ' . (int) $limit; } return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); } /** * Search for customers by ip address. * * @param string $ip Searched string * * @return array|false|mysqli_result|PDOStatement|resource|null */ public static function searchByIp($ip) { return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT DISTINCT c.* FROM `' . _DB_PREFIX_ . 'customer` c LEFT JOIN `' . _DB_PREFIX_ . 'guest` g ON g.id_customer = c.id_customer LEFT JOIN `' . _DB_PREFIX_ . 'connections` co ON g.id_guest = co.id_guest WHERE co.`ip_address` = \'' . (int) ip2long(trim($ip)) . '\''); } /** * Return several useful statistics about customer. * * @return array Stats */ public function getStats() { $result = Db::getInstance()->getRow(' SELECT COUNT(`id_order`) AS nb_orders, SUM(`total_paid` / o.`conversion_rate`) AS total_orders FROM `' . _DB_PREFIX_ . 'orders` o WHERE o.`id_customer` = ' . (int) $this->id . ' AND o.valid = 1'); $result2 = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(' SELECT c.`date_add` AS last_visit FROM `' . _DB_PREFIX_ . 'connections` c LEFT JOIN `' . _DB_PREFIX_ . 'guest` g USING (id_guest) WHERE g.`id_customer` = ' . (int) $this->id . ' ORDER BY c.`date_add` DESC '); $result3 = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(' SELECT (YEAR(CURRENT_DATE)-YEAR(c.`birthday`)) - (RIGHT(CURRENT_DATE, 5)id) { return []; } return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT m.*, l.name as language FROM `' . _DB_PREFIX_ . 'mail` m LEFT JOIN `' . _DB_PREFIX_ . 'lang` l ON m.id_lang = l.id_lang WHERE `recipient` = "' . pSQL($this->email) . '" ORDER BY m.date_add DESC LIMIT 10'); } /** * Get last 10 Connections of the Customer. * * @return array|false|mysqli_result|PDOStatement|resource|null */ public function getLastConnections() { if (!$this->id) { return []; } return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS( ' 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 FROM `' . _DB_PREFIX_ . 'guest` g LEFT JOIN `' . _DB_PREFIX_ . 'connections` c ON c.id_guest = g.id_guest LEFT JOIN `' . _DB_PREFIX_ . 'connections_page` cp ON c.id_connections = cp.id_connections WHERE g.`id_customer` = ' . (int) $this->id . ' GROUP BY c.`id_connections` ORDER BY c.date_add DESC LIMIT 10' ); } /** * Check if Customer ID exists. * * @param int $idCustomer Customer ID * * @return int|null Customer ID if found */ public static function customerIdExistsStatic($idCustomer) { $cacheId = 'Customer::customerIdExistsStatic' . (int) $idCustomer; if (!Cache::isStored($cacheId)) { $result = (int) Db::getInstance()->getValue(' SELECT `id_customer` FROM ' . _DB_PREFIX_ . 'customer c WHERE c.`id_customer` = ' . (int) $idCustomer); Cache::store($cacheId, $result); return $result; } return Cache::retrieve($cacheId); } /** * Update customer groups associated to the object. * * @param array $list groups */ public function updateGroup($list) { Hook::exec('actionCustomerBeforeUpdateGroup', ['id_customer' => $this->id, 'groups' => $list]); // If some groups are provided, respect this. If not, automatically add the default group of the customer if (!empty($list)) { $this->cleanGroups(); $this->addGroups($list); } else { $this->addGroups([$this->id_default_group]); } } /** * Remove this Customer ID from Customer Groups. * * @return bool Indicates whether the Customer ID has been successfully removed * from the Customer Group Db table */ public function cleanGroups() { return Db::getInstance()->delete('customer_group', 'id_customer = ' . (int) $this->id); } /** * Add the Customer to the given Customer Groups. * * @param array $groups Customer Group IDs */ public function addGroups($groups) { Hook::exec('actionCustomerAddGroups', ['id_customer' => $this->id, 'groups' => $groups]); foreach ($groups as $group) { $row = ['id_customer' => (int) $this->id, 'id_group' => (int) $group]; Db::getInstance()->insert('customer_group', $row, false, true, Db::INSERT_IGNORE); } } /** * Get Groups that have the given Customer ID. * * @param int $idCustomer Customer ID * * @return array|mixed */ public static function getGroupsStatic($idCustomer) { if (!Group::isFeatureActive()) { return [Configuration::get('PS_CUSTOMER_GROUP')]; } if ($idCustomer == 0) { self::$_customer_groups[$idCustomer] = [(int) Configuration::get('PS_UNIDENTIFIED_GROUP')]; } if (!isset(self::$_customer_groups[$idCustomer])) { self::$_customer_groups[$idCustomer] = []; $result = Db::getInstance()->executeS(' SELECT cg.`id_group` FROM ' . _DB_PREFIX_ . 'customer_group cg WHERE cg.`id_customer` = ' . (int) $idCustomer); foreach ($result as $group) { self::$_customer_groups[$idCustomer][] = (int) $group['id_group']; } } return self::$_customer_groups[$idCustomer]; } /** * Get Groups of this Customer * * @return array|mixed */ public function getGroups() { return Customer::getGroupsStatic((int) $this->id); } /** * Get Products bought by this Customer. * * @return array|false|mysqli_result|PDOStatement|resource|null */ public function getBoughtProducts() { return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT * FROM `' . _DB_PREFIX_ . 'orders` o LEFT JOIN `' . _DB_PREFIX_ . 'order_detail` od ON o.id_order = od.id_order WHERE o.valid = 1 AND o.`id_customer` = ' . (int) $this->id); } /** * Get Default Customer Group ID. * * @param int $idCustomer Customer ID * * @return mixed|string|null */ public static function getDefaultGroupId($idCustomer) { if (!Group::isFeatureActive()) { static $psCustomerGroup = null; if ($psCustomerGroup === null) { $psCustomerGroup = Configuration::get('PS_CUSTOMER_GROUP'); } return $psCustomerGroup; } if (!isset(self::$_defaultGroupId[(int) $idCustomer])) { self::$_defaultGroupId[(int) $idCustomer] = Db::getInstance()->getValue( ' SELECT `id_default_group` FROM `' . _DB_PREFIX_ . 'customer` WHERE `id_customer` = ' . (int) $idCustomer ); } return self::$_defaultGroupId[(int) $idCustomer]; } /** * Get current country or default country * * @param int $idCustomer * @param Cart|null $cart * * @return int Country ID */ public static function getCurrentCountry($idCustomer, Cart $cart = null) { if (!$cart) { $cart = Context::getContext()->cart; } if (!$cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')}) { $idAddress = (int) Db::getInstance()->getValue(sprintf( 'SELECT `id_address` FROM `%saddress` WHERE `id_customer` = %d AND `deleted` = 0 ORDER BY `id_address`', _DB_PREFIX_, (int) $idCustomer )); } else { $idAddress = $cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')}; } $ids = Address::getCountryAndState($idAddress); return (int) ($ids['id_country'] ?? Configuration::get('PS_COUNTRY_DEFAULT')); } /** * Is the current Customer a Guest? * * @return bool Indicates whether the Customer is a Guest */ public function isGuest() { return (bool) $this->is_guest; } /** * Transform the Guest to a Customer. * * @param int $idLang Language ID * @param string|null $password Password * * @return bool Indicates if a process has been successful */ public function transformToCustomer($idLang, $password = null) { // If it's not a guest, wrong call if (!$this->isGuest()) { return false; } // If a customer with the same email already exists, wrong call if (Customer::customerExists($this->email)) { return false; } $this->is_guest = false; /* * If this is an anonymous conversion and we want the customer to set his own password, * we set a random one for now. */ if (empty($password)) { $password = Tools::passwdGen(16, 'RANDOM'); } else { if (!Validate::isAcceptablePasswordLength($password) || !Validate::isAcceptablePasswordScore($password)) { return false; } } /** @var \PrestaShop\PrestaShop\Core\Crypto\Hashing $crypto */ $crypto = ServiceLocator::get('\\PrestaShop\\PrestaShop\\Core\\Crypto\\Hashing'); $this->passwd = $crypto->hash($password); /* * Now, we need to update his group. The guest should have had a PS_GUEST_GROUP previously, but if * not, no biggie, it's gonna be fixed now. * * We will remove all entries from customer_group table and add a customer group from configuration. * We also need to set it as his default group. */ $this->cleanGroups(); $this->addGroups([Configuration::get('PS_CUSTOMER_GROUP')]); $this->id_default_group = (int) Configuration::get('PS_CUSTOMER_GROUP'); $this->stampResetPasswordToken(); if (!$this->update()) { return false; } $language = new Language($idLang); if (!Validate::isLoadedObject($language)) { $language = Context::getContext()->language; } /* * Now, we will send out an email where he can set his new password. * * TODO: * This 'guest_to_customer' email should be sent only if password was not provided. Otherwise, * it should send an 'account' email without an URL. This is what CustomerPersister does. (These functions * should be unified.) * * OrderConfirmationController and GuestTrackingController call this logic with a password and user still * receives the below email, that should be changed. */ $vars = [ '{firstname}' => $this->firstname, '{lastname}' => $this->lastname, '{email}' => $this->email, '{url}' => Context::getContext()->link->getPageLink( 'password', null, null, sprintf( 'token=%s&id_customer=%s&reset_token=%s', $this->secure_key, (int) $this->id, $this->reset_password_token ) ), ]; Mail::Send( (int) $idLang, 'guest_to_customer', Context::getContext()->getTranslator()->trans( 'Your guest account has been transformed into a customer account', [], 'Emails.Subject', $language->locale ), $vars, $this->email, $this->firstname . ' ' . $this->lastname, null, null, null, null, _PS_MAIL_DIR_, false, (int) $this->id_shop ); return true; } /** * Set password * (for webservice). * * @param string $passwd Password * * @return bool Indictes whether the password has been successfully set */ public function setWsPasswd($passwd) { /** @var \PrestaShop\PrestaShop\Core\Crypto\Hashing $crypto */ $crypto = ServiceLocator::get('\\PrestaShop\\PrestaShop\\Core\\Crypto\\Hashing'); if ($this->id == 0 || $this->passwd != $passwd) { $this->passwd = $crypto->hash($passwd); } return true; } /** * Check customer information and return customer validity. * * @since 1.5.0 * * @param bool $withGuest * * @return bool customer validity */ public function isLogged($withGuest = false) { if (!$withGuest && $this->is_guest == 1) { return false; } /* Customer is valid only if it can be load and if object password is the same as database one */ return $this->logged == true && $this->id && Validate::isUnsignedId($this->id) && Customer::checkPassword($this->id, $this->passwd) && Context::getContext()->cookie->isSessionAlive() ; } /** * Logout. * * @since 1.5.0 */ public function logout() { Hook::exec('actionCustomerLogoutBefore', ['customer' => $this]); // Cookie class will handle complete destroying of the cookie and sending it out to the client if (isset(Context::getContext()->cookie)) { Context::getContext()->cookie->logout(); } $this->logged = false; Hook::exec('actionCustomerLogoutAfter', ['customer' => $this]); } /** * Soft logout, delete everything that links to the customer * but leave there affiliate's information. * * @since 1.5.0 */ public function mylogout() { Hook::exec('actionCustomerLogoutBefore', ['customer' => $this]); // Cookie class will remove all customer information from the cookie and update that cookie if (isset(Context::getContext()->cookie)) { Context::getContext()->cookie->mylogout(); } $this->logged = false; Hook::exec('actionCustomerLogoutAfter', ['customer' => $this]); } /** * Get last empty Cart for this Customer, when last cart is not empty return false. * * @param bool|true $withOrder Only return a Cart that have been converted into an Order * * @return bool|int */ public function getLastEmptyCart($withOrder = true) { $carts = Cart::getCustomerCarts((int) $this->id, $withOrder); if (!count($carts)) { return false; } $cart = array_shift($carts); $cart = new Cart((int) $cart['id_cart']); return $cart->nbProducts() === 0 ? (int) $cart->id : false; } /** * Validate controller and check password * * @param bool $htmlentities * * @return array * * @deprecated 8.1.0 The password check has been moved in controllers and this method is not called anywhere since 1.7.0 */ public function validateController($htmlentities = true) { $errors = parent::validateController($htmlentities); /** @var \PrestaShop\PrestaShop\Core\Crypto\Hashing $crypto */ $crypto = ServiceLocator::get('\\PrestaShop\\PrestaShop\\Core\\Crypto\\Hashing'); if ($value = Tools::getValue('passwd')) { $this->passwd = $crypto->hash($value); } return $errors; } /** * Get outstanding amount. * * @return float Outstanding amount */ public function getOutstanding() { $query = new DbQuery(); $query->select('SUM(oi.total_paid_tax_incl)'); $query->from('order_invoice', 'oi'); $query->leftJoin('orders', 'o', 'oi.id_order = o.id_order'); $query->groupBy('o.id_customer'); $query->where('o.id_customer = ' . (int) $this->id); $totalPaid = (float) Db::getInstance()->getValue($query->build()); $query = new DbQuery(); $query->select('SUM(op.amount)'); $query->from('order_payment', 'op'); $query->leftJoin('order_invoice_payment', 'oip', 'op.id_order_payment = oip.id_order_payment'); $query->leftJoin('orders', 'o', 'oip.id_order = o.id_order'); $query->groupBy('o.id_customer'); $query->where('o.id_customer = ' . (int) $this->id); $totalRest = (float) Db::getInstance()->getValue($query->build()); return $totalPaid - $totalRest; } /** * Get Customer Groups * (for webservice). * * @return array|false|mysqli_result|PDOStatement|resource|null */ public function getWsGroups() { return Db::getInstance()->executeS( ' SELECT cg.`id_group` as id FROM ' . _DB_PREFIX_ . 'customer_group cg ' . Shop::addSqlAssociation('group', 'cg') . ' WHERE cg.`id_customer` = ' . (int) $this->id ); } /** * Set Customer Groups * (for webservice). * * @param array $result * * @return bool */ public function setWsGroups($result) { $groups = []; foreach ($result as $row) { $groups[] = $row['id']; } $this->cleanGroups(); $this->addGroups($groups); return true; } /** * @see ObjectModel::getWebserviceObjectList() */ public function getWebserviceObjectList($sqlJoin, $sqlFilter, $sqlSort, $sqlLimit) { $sqlFilter .= Shop::addSqlRestriction(Shop::SHARE_CUSTOMER, 'main'); return parent::getWebserviceObjectList($sqlJoin, $sqlFilter, $sqlSort, $sqlLimit); } /** * Fill Reset password unique token with random sha1 and its validity date. For forgot password feature. */ public function stampResetPasswordToken() { $salt = $this->id . '-' . $this->secure_key; $this->reset_password_token = sha1(time() . $salt); $validity = (int) Configuration::get('PS_PASSWD_RESET_VALIDITY') ?: 1440; $this->reset_password_validity = date('Y-m-d H:i:s', strtotime('+' . $validity . ' minutes')); } /** * 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). */ public function hasRecentResetPasswordToken() { if (!$this->reset_password_token) { return false; } // TODO maybe use another 'recent' value for this test. For instance, equals password validity value. if (!$this->reset_password_validity || strtotime($this->reset_password_validity) < time()) { return false; } return true; } /** * Returns the valid reset password token if it validity date is > now(). */ public function getValidResetPasswordToken() { if (!$this->reset_password_token) { return false; } if (!$this->reset_password_validity || strtotime($this->reset_password_validity) < time()) { return false; } return $this->reset_password_token; } /** * Delete reset password token data. */ public function removeResetPasswordToken() { $this->reset_password_token = null; $this->reset_password_validity = null; } }