Initial commit

This commit is contained in:
2020-10-07 10:37:15 +02:00
commit ce5f440392
28157 changed files with 4429172 additions and 0 deletions

10
classes/.htaccess Executable file
View File

@@ -0,0 +1,10 @@
# Apache 2.2
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
# Apache 2.4
<IfModule mod_authz_core.c>
Require all denied
</IfModule>

436
classes/Access.php Normal file
View File

@@ -0,0 +1,436 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class AccessCore.
*/
class AccessCore extends ObjectModel
{
/** @var int Profile id which address belongs to */
public $id_profile = null;
/** @var int AuthorizationRole id which address belongs to */
public $id_authorization_role = null;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'access',
'primary' => 'id_profile',
'fields' => array(
'id_profile' => array('type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false),
'id_authorization_role' => array('type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false),
),
);
/**
* Is access granted to this Role?
*
* @param string $role Role name ("Superadministrator", "sales", "translator", etc.)
* @param int $idProfile Profile ID
*
* @return bool Whether access is granted
*
* @throws Exception
*/
public static function isGranted($role, $idProfile)
{
foreach ((array) $role as $currentRole) {
preg_match(
'/ROLE_MOD_(?P<type>[A-Z]+)_(?P<name>[A-Z0-9_]+)_(?P<auth>[A-Z]+)/',
$currentRole,
$matches
);
if (isset($matches['type']) && $matches['type'] == 'TAB') {
$joinTable = _DB_PREFIX_ . 'access';
} elseif (isset($matches['type']) && $matches['type'] == 'MODULE') {
$joinTable = _DB_PREFIX_ . 'module_access';
} else {
throw new Exception('The slug ' . $currentRole . ' is invalid');
}
$currentRole = Db::getInstance()->escape($currentRole);
$isCurrentGranted = (bool) Db::getInstance()->getRow('
SELECT t.`id_authorization_role`
FROM `' . _DB_PREFIX_ . 'authorization_role` t
LEFT JOIN ' . $joinTable . ' j
ON j.`id_authorization_role` = t.`id_authorization_role`
WHERE `slug` = "' . $currentRole . '"
AND j.`id_profile` = "' . (int) $idProfile . '"
');
if (!$isCurrentGranted) {
return false;
}
}
return true;
}
/**
* Get all roles for the Profile ID.
*
* @param int $idProfile Profile ID
*
* @return array Roles
*/
public static function getRoles($idProfile)
{
$idProfile = (int) $idProfile;
$accesses = Db::getInstance()->executeS('
SELECT r.`slug`
FROM `' . _DB_PREFIX_ . 'authorization_role` r
INNER JOIN `' . _DB_PREFIX_ . 'access` a ON a.`id_authorization_role` = r.`id_authorization_role`
WHERE a.`id_profile` = "' . $idProfile . '"
');
$accessesFromModules = Db::getInstance()->executeS('
SELECT r.`slug`
FROM `' . _DB_PREFIX_ . 'authorization_role` r
INNER JOIN `' . _DB_PREFIX_ . 'module_access` ma ON ma.`id_authorization_role` = r.`id_authorization_role`
WHERE ma.`id_profile` = "' . $idProfile . '"
');
$roles = array_merge($accesses, $accessesFromModules);
foreach ($roles as $key => $role) {
$roles[$key] = $role['slug'];
}
return $roles;
}
/**
* Find Tab ID by slug.
*
* @param string $authSlug Slug
*
* @return string Tab ID
* @todo: Find out if we should return an int instead. (breaking change)
*/
public static function findIdTabByAuthSlug($authSlug)
{
preg_match(
'/ROLE_MOD_[A-Z]+_(?P<classname>[A-Z]+)_(?P<auth>[A-Z]+)/',
$authSlug,
$matches
);
$result = Db::getInstance()->getRow('
SELECT `id_tab`
FROM `' . _DB_PREFIX_ . 'tab`
WHERE UCASE(`class_name`) = "' . $matches['classname'] . '"
');
return $result['id_tab'];
}
/**
* Find slug by Tab ID.
*
* @param int $idTab Tab ID
*
* @return string Full module slug
*/
public static function findSlugByIdTab($idTab)
{
$result = Db::getInstance()->getRow('
SELECT `class_name`
FROM `' . _DB_PREFIX_ . 'tab`
WHERE `id_tab` = "' . (int) $idTab . '"
');
return self::sluggifyTab($result);
}
/**
* Find slug by Parent Tab ID.
*
* @param int $idParentTab Tab ID
*
* @return string Full module slug
*/
public static function findSlugByIdParentTab($idParentTab)
{
return Db::getInstance()->executeS('
SELECT `class_name`
FROM `' . _DB_PREFIX_ . 'tab`
WHERE `id_parent` = "' . (int) $idParentTab . '"
');
}
/**
* Find slug by Module ID.
*
* @param int $idModule Module ID
*
* @return string Full module slug
*/
public static function findSlugByIdModule($idModule)
{
$result = Db::getInstance()->getRow('
SELECT `name`
FROM `' . _DB_PREFIX_ . 'module`
WHERE `id_module` = "' . (int) $idModule . '"
');
return self::sluggifyModule($result);
}
/**
* Sluggify tab.
*
* @param string $tab Tab class name
* @param string $authorization 'CREATE'|'READ'|'UPDATE'|'DELETE'
*
* @return string Full slug for tab
*/
public static function sluggifyTab($tab, $authorization = '')
{
return sprintf('ROLE_MOD_TAB_%s_%s', strtoupper($tab['class_name']), $authorization);
}
/**
* Sluggify module.
*
* @param string $module Module name
* @param string $authorization 'CREATE'|'READ'|'UPDATE'|'DELETE'
*
* @return string Full slug for module
*/
public static function sluggifyModule($module, $authorization = '')
{
return sprintf('ROLE_MOD_MODULE_%s_%s', strtoupper($module['name']), $authorization);
}
/**
* Get legacy authorization.
*
* @param string $legacyAuth Legacy authorization
*
* @return bool|string|array Authorization
*/
public static function getAuthorizationFromLegacy($legacyAuth)
{
$auth = array(
'add' => 'CREATE',
'view' => 'READ',
'edit' => 'UPDATE',
'configure' => 'UPDATE',
'delete' => 'DELETE',
'uninstall' => 'DELETE',
'duplicate' => array('CREATE', 'UPDATE'),
'all' => array('CREATE', 'READ', 'UPDATE', 'DELETE'),
);
return isset($auth[$legacyAuth]) ? $auth[$legacyAuth] : false;
}
/**
* Add access.
*
* @param int $idProfile Profile ID
* @param int $idRole Role ID
*
* @return string Whether access has been successfully granted ("ok", "error")
*/
public function addAccess($idProfile, $idRole)
{
$sql = '
INSERT IGNORE INTO `' . _DB_PREFIX_ . 'access` (`id_profile`, `id_authorization_role`)
VALUES (' . (int) $idProfile . ',' . (int) $idRole . ')
';
return Db::getInstance()->execute($sql) ? 'ok' : 'error';
}
/**
* Remove access.
*
* @param int $idProfile Profile ID
* @param int $idRole Role ID
*
* @return string Whether access has been successfully removed ("ok", "error")
*/
public function removeAccess($idProfile, $idRole)
{
$sql = '
DELETE FROM `' . _DB_PREFIX_ . 'access`
WHERE `id_profile` = "' . (int) $idProfile . '"
AND `id_authorization_role` = "' . (int) $idRole . '"
';
return Db::getInstance()->execute($sql) ? 'ok' : 'error';
}
/**
* Add module access.
*
* @param int $idProfile Profile ID
* @param int $idRole Role ID
*
* @return string Whether module access has been successfully granted ("ok", "error")
*/
public function addModuleAccess($idProfile, $idRole)
{
$sql = '
INSERT IGNORE INTO `' . _DB_PREFIX_ . 'module_access` (`id_profile`, `id_authorization_role`)
VALUES (' . (int) $idProfile . ',' . (int) $idRole . ')
';
return Db::getInstance()->execute($sql) ? 'ok' : 'error';
}
/**
* @param int $idProfile
* @param int $idRole
*
* @return string 'ok'|'error'
*/
public function removeModuleAccess($idProfile, $idRole)
{
$sql = '
DELETE FROM `' . _DB_PREFIX_ . 'module_access`
WHERE `id_profile` = "' . (int) $idProfile . '"
AND `id_authorization_role` = "' . (int) $idRole . '"
';
return Db::getInstance()->execute($sql) ? 'ok' : 'error';
}
/**
* Update legacy access.
*
* @param int $idProfile Profile ID
* @param int $idTab Tab ID
* @param string $lgcAuth Legacy authorization
* @param int $enabled Whether access should be granted
* @param int $addFromParent Child from parents
*
* @return string Whether legacy access has been successfully updated ("ok", "error")
*
* @throws Exception
*/
public function updateLgcAccess($idProfile, $idTab, $lgcAuth, $enabled, $addFromParent = 0)
{
$idProfile = (int) $idProfile;
$idTab = (int) $idTab;
if ($idTab == -1) {
$slug = 'ROLE_MOD_TAB_%_';
} else {
$slug = self::findSlugByIdTab($idTab);
}
$whereClauses = array();
foreach ((array) self::getAuthorizationFromLegacy($lgcAuth) as $auth) {
$slugLike = Db::getInstance()->escape($slug . $auth);
$whereClauses[] = ' `slug` LIKE "' . $slugLike . '"';
}
if ($addFromParent == 1) {
foreach (self::findSlugByIdParentTab($idTab) as $child) {
$child = self::sluggifyTab($child);
foreach ((array) self::getAuthorizationFromLegacy($lgcAuth) as $auth) {
$slugLike = Db::getInstance()->escape($child . $auth);
$whereClauses[] = ' `slug` LIKE "' . $slugLike . '"';
}
}
}
$roles = Db::getInstance()->executeS('
SELECT `id_authorization_role`
FROM `' . _DB_PREFIX_ . 'authorization_role` t
WHERE ' . implode(' OR ', $whereClauses) . '
');
if (empty($roles)) {
throw new \Exception('Cannot find role slug');
}
$res = array();
foreach ($roles as $role) {
if ($enabled) {
$res[] = $this->addAccess($idProfile, $role['id_authorization_role']);
} else {
$res[] = $this->removeAccess($idProfile, $role['id_authorization_role']);
}
}
return in_array('error', $res) ? 'error' : 'ok';
}
/**
* Update (legacy) Module access.
*
* @param int $idProfile Profile ID
* @param int $idModule Module ID
* @param string $lgcAuth Legacy authorization
* @param int $enabled Whether module access should be granted
*
* @return string Whether module access has been succesfully changed ("ok", "error")
*/
public function updateLgcModuleAccess($idProfile, $idModule, $lgcAuth, $enabled)
{
$idProfile = (int) $idProfile;
$idModule = (int) $idModule;
if ($idModule == -1) {
$slug = 'ROLE_MOD_MODULE_%_';
} else {
$slug = self::findSlugByIdModule($idModule);
}
$whereClauses = array();
foreach ((array) self::getAuthorizationFromLegacy($lgcAuth) as $auth) {
$slugLike = Db::getInstance()->escape($slug . $auth);
$whereClauses[] = ' `slug` LIKE "' . $slugLike . '"';
}
$roles = Db::getInstance()->executeS('
SELECT `id_authorization_role`
FROM `' . _DB_PREFIX_ . 'authorization_role` t
WHERE ' . implode(' OR ', $whereClauses) . '
');
$res = array();
foreach ($roles as $role) {
if ($enabled) {
$res[] = $this->addModuleAccess($idProfile, $role['id_authorization_role']);
} else {
$res[] = $this->removeModuleAccess($idProfile, $role['id_authorization_role']);
}
}
return in_array('error', $res) ? 'error' : 'ok';
}
}

615
classes/Address.php Normal file
View File

@@ -0,0 +1,615 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class AddressCore.
*/
class AddressCore extends ObjectModel
{
/** @var int Customer ID which address belongs to */
public $id_customer = null;
/** @var int Manufacturer ID which address belongs to */
public $id_manufacturer = null;
/** @var int Supplier ID which address belongs to */
public $id_supplier = null;
/**
* @since 1.5.0
*
* @var int Warehouse ID which address belongs to
*/
public $id_warehouse = null;
/** @var int Country ID */
public $id_country;
/** @var int State ID */
public $id_state;
/** @var string Country name */
public $country;
/** @var string Alias (eg. Home, Work...) */
public $alias;
/** @var string Company (optional) */
public $company;
/** @var string Lastname */
public $lastname;
/** @var string Firstname */
public $firstname;
/** @var string Address first line */
public $address1;
/** @var string Address second line (optional) */
public $address2;
/** @var string Postal code */
public $postcode;
/** @var string City */
public $city;
/** @var string Any other useful information */
public $other;
/** @var string Phone number */
public $phone;
/** @var string Mobile phone number */
public $phone_mobile;
/** @var string VAT number */
public $vat_number;
/** @var string DNI number */
public $dni;
/** @var string Object creation date */
public $date_add;
/** @var string Object last modification date */
public $date_upd;
/** @var bool True if address has been deleted (staying in database as deleted) */
public $deleted = 0;
/** @var array Zone IDs cache */
protected static $_idZones = array();
/** @var array Country IDs cache */
protected static $_idCountries = array();
/**
* @see ObjectModel::$definition
*/
// when you override this class, do not create a field with allow_null=>true
// because it will give you exception on checkout address step
public static $definition = array(
'table' => 'address',
'primary' => 'id_address',
'fields' => array(
'id_customer' => array('type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false),
'id_manufacturer' => array('type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false),
'id_supplier' => array('type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false),
'id_warehouse' => array('type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false),
'id_country' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_state' => array('type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId'),
'alias' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 32),
'company' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 255),
'lastname' => array('type' => self::TYPE_STRING, 'validate' => 'isName', 'required' => true, 'size' => 255),
'firstname' => array('type' => self::TYPE_STRING, 'validate' => 'isName', 'required' => true, 'size' => 255),
'vat_number' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName'),
'address1' => array('type' => self::TYPE_STRING, 'validate' => 'isAddress', 'required' => true, 'size' => 128),
'address2' => array('type' => self::TYPE_STRING, 'validate' => 'isAddress', 'size' => 128),
'postcode' => array('type' => self::TYPE_STRING, 'validate' => 'isPostCode', 'size' => 12),
'city' => array('type' => self::TYPE_STRING, 'validate' => 'isCityName', 'required' => true, 'size' => 64),
'other' => array('type' => self::TYPE_STRING, 'validate' => 'isMessage', 'size' => 300),
'phone' => array('type' => self::TYPE_STRING, 'validate' => 'isPhoneNumber', 'size' => 32),
'phone_mobile' => array('type' => self::TYPE_STRING, 'validate' => 'isPhoneNumber', 'size' => 32),
'dni' => array('type' => self::TYPE_STRING, 'validate' => 'isDniLite', 'size' => 16),
'deleted' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'copy_post' => false),
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate', 'copy_post' => false),
'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate', 'copy_post' => false),
),
);
/** @var array Web service parameters */
protected $webserviceParameters = array(
'objectsNodeName' => 'addresses',
'fields' => array(
'id_customer' => array('xlink_resource' => 'customers'),
'id_manufacturer' => array('xlink_resource' => 'manufacturers'),
'id_supplier' => array('xlink_resource' => 'suppliers'),
'id_warehouse' => array('xlink_resource' => 'warehouse'),
'id_country' => array('xlink_resource' => 'countries'),
'id_state' => array('xlink_resource' => 'states'),
),
);
/**
* Build an Address.
*
* @param int $id_address Existing Address ID in order to load object (optional)
*/
public function __construct($id_address = null, $id_lang = null)
{
parent::__construct($id_address);
/* Get and cache address country name */
if ($this->id) {
$this->country = Country::getNameById($id_lang ? $id_lang : Configuration::get('PS_LANG_DEFAULT'), $this->id_country);
}
}
/**
* reset static cache (eg unit testing purpose).
*/
public static function resetStaticCache()
{
static::$_idZones = array();
static::$_idCountries = array();
}
/**
* @see ObjectModel::add()
*/
public function add($autodate = true, $null_values = false)
{
if (!parent::add($autodate, $null_values)) {
return false;
}
if (Validate::isUnsignedId($this->id_customer)) {
Customer::resetAddressCache($this->id_customer, $this->id);
}
return true;
}
/**
* @see ObjectModel::update()
*/
public function update($null_values = false)
{
// Empty related caches
if (isset(self::$_idCountries[$this->id])) {
unset(self::$_idCountries[$this->id]);
}
if (isset(self::$_idZones[$this->id])) {
unset(self::$_idZones[$this->id]);
}
if (Validate::isUnsignedId($this->id_customer)) {
Customer::resetAddressCache($this->id_customer, $this->id);
}
/* Skip the required fields */
if ($this->isUsed()) {
self::$fieldsRequiredDatabase['Address'] = array();
}
return parent::update($null_values);
}
/**
* @see ObjectModel::delete()
*/
public function delete()
{
if (Validate::isUnsignedId($this->id_customer)) {
Customer::resetAddressCache($this->id_customer, $this->id);
}
if (!$this->isUsed()) {
$this->deleteCartAddress();
return parent::delete();
} else {
$this->deleted = true;
return $this->update();
}
}
/**
* removes the address from carts using it, to avoid errors on not existing address
*/
protected function deleteCartAddress()
{
// keep pending carts, but unlink it from current address
$sql = 'UPDATE ' . _DB_PREFIX_ . 'cart
SET id_address_delivery = 0
WHERE id_address_delivery = ' . $this->id;
Db::getInstance()->execute($sql);
$sql = 'UPDATE ' . _DB_PREFIX_ . 'cart
SET id_address_invoice = 0
WHERE id_address_invoice = ' . $this->id;
Db::getInstance()->execute($sql);
}
/**
* Returns fields required for an address in an array hash.
*
* @return array Hash values
*/
public static function getFieldsValidate()
{
$tmp_addr = new Address();
$out = $tmp_addr->fieldsValidate;
unset($tmp_addr);
return $out;
}
/**
* Get Zone ID for a given address.
*
* @param int $id_address Address ID for which we want to get the Zone ID
*
* @return int Zone ID
*/
public static function getZoneById($id_address)
{
if (!isset($id_address) || empty($id_address)) {
return false;
}
if (isset(self::$_idZones[$id_address])) {
return self::$_idZones[$id_address];
}
$id_zone = Hook::exec('actionGetIDZoneByAddressID', array('id_address' => $id_address));
if (is_numeric($id_zone)) {
self::$_idZones[$id_address] = (int) $id_zone;
return self::$_idZones[$id_address];
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
SELECT s.`id_zone` AS id_zone_state, c.`id_zone`
FROM `' . _DB_PREFIX_ . 'address` a
LEFT JOIN `' . _DB_PREFIX_ . 'country` c ON c.`id_country` = a.`id_country`
LEFT JOIN `' . _DB_PREFIX_ . 'state` s ON s.`id_state` = a.`id_state`
WHERE a.`id_address` = ' . (int) $id_address);
self::$_idZones[$id_address] = (int) ((int) $result['id_zone_state'] ? $result['id_zone_state'] : $result['id_zone']);
return self::$_idZones[$id_address];
}
/**
* Check if the Country is active for a given address.
*
* @param int $id_address Address ID for which we want to get the Country status
*
* @return int Country status
*/
public static function isCountryActiveById($id_address)
{
if (!isset($id_address) || empty($id_address)) {
return false;
}
$cache_id = 'Address::isCountryActiveById_' . (int) $id_address;
if (!Cache::isStored($cache_id)) {
$result = (bool) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
SELECT c.`active`
FROM `' . _DB_PREFIX_ . 'address` a
LEFT JOIN `' . _DB_PREFIX_ . 'country` c ON c.`id_country` = a.`id_country`
WHERE a.`id_address` = ' . (int) $id_address);
Cache::store($cache_id, $result);
return $result;
}
return Cache::retrieve($cache_id);
}
/**
* {@inheritdoc}
*/
public function validateField($field, $value, $id_lang = null, $skip = array(), $human_errors = false)
{
$error = parent::validateField($field, $value, $id_lang, $skip, $human_errors);
if (true !== $error || 'dni' !== $field) {
return $error;
}
// Special validation for dni, check if the country needs it
if (static::dniRequired((int) $this->id_country) && Tools::isEmpty($value)) {
if ($human_errors) {
return $this->trans(
'The %s field is required.',
[$this->displayFieldName($field, get_class($this))],
'Admin.Notifications.Error'
);
}
return $this->trans(
'Property %s is empty.',
[get_class($this) . '->' . $field],
'Admin.Notifications.Error'
);
}
return true;
}
/**
* Request to check if DNI field is required
* depending on the current selected country.
*
* @param int $idCountry
*
* @return bool
*/
public static function dniRequired($idCountry)
{
return (bool) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
'SELECT c.`need_identification_number` ' .
'FROM `' . _DB_PREFIX_ . 'country` c ' .
'WHERE c.`id_country` = ' . (int) $idCountry
);
}
/**
* Check if Address is used (at least one order placed).
*
* @return int Order count for this Address
*/
public function isUsed()
{
if ((int) $this->id <= 0) {
return false;
}
$result = (int) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
SELECT COUNT(`id_order`) AS used
FROM `' . _DB_PREFIX_ . 'orders`
WHERE `id_address_delivery` = ' . (int) $this->id . '
OR `id_address_invoice` = ' . (int) $this->id);
return $result > 0 ? (int) $result : false;
}
/**
* Get Country and State of this Address.
*
* @param int $id_address Address ID
*
* @return array
*/
public static function getCountryAndState($id_address)
{
if (isset(self::$_idCountries[$id_address])) {
return self::$_idCountries[$id_address];
}
if ($id_address) {
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
SELECT `id_country`, `id_state`, `vat_number`, `postcode` FROM `' . _DB_PREFIX_ . 'address`
WHERE `id_address` = ' . (int) $id_address);
} else {
$result = false;
}
self::$_idCountries[$id_address] = $result;
return $result;
}
/**
* Specify if an address is already in base.
*
* @param int $id_address Address id
*
* @return bool The address exists
*/
public static function addressExists($id_address)
{
$key = 'address_exists_' . (int) $id_address;
if (!Cache::isStored($key)) {
$id_address = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('SELECT `id_address` FROM ' . _DB_PREFIX_ . 'address a WHERE a.`id_address` = ' . (int) $id_address);
Cache::store($key, (bool) $id_address);
return (bool) $id_address;
}
return Cache::retrieve($key);
}
/**
* Check if the address is valid.
*
* @param int $id_address Address id
*
* @return bool The address is valid
*/
public static function isValid($id_address)
{
$id_address = (int) $id_address;
$isValid = Db::getInstance()->getValue('
SELECT `id_address` FROM ' . _DB_PREFIX_ . 'address a
WHERE a.`id_address` = ' . $id_address . ' AND a.`deleted` = 0 AND a.`active` = 1
');
return (bool) $isValid;
}
/**
* Get the first address id of the customer.
*
* @param int $id_customer Customer id
* @param bool $active Active addresses only
*
* @return bool|int|null
*/
public static function getFirstCustomerAddressId($id_customer, $active = true)
{
if (!$id_customer) {
return false;
}
$cache_id = 'Address::getFirstCustomerAddressId_' . (int) $id_customer . '-' . (bool) $active;
if (!Cache::isStored($cache_id)) {
$result = (int) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
'
SELECT `id_address`
FROM `' . _DB_PREFIX_ . 'address`
WHERE `id_customer` = ' . (int) $id_customer . ' AND `deleted` = 0' . ($active ? ' AND `active` = 1' : '')
);
Cache::store($cache_id, $result);
return $result;
}
return Cache::retrieve($cache_id);
}
/**
* Initialize an address corresponding to the specified id address or if empty to the
* default shop configuration.
*
* @param int $id_address
* @param bool $with_geoloc
*
* @return Address address
*
* @throws PrestaShopException
*/
public static function initialize($id_address = null, $with_geoloc = false)
{
$context = Context::getContext();
if ($id_address) {
$context_hash = (int) $id_address;
} elseif ($with_geoloc && isset($context->customer->geoloc_id_country)) {
$context_hash = md5((int) $context->customer->geoloc_id_country . '-' . (int) $context->customer->id_state . '-' .
$context->customer->postcode);
} else {
$context_hash = md5((int) $context->country->id);
}
$cache_id = 'Address::initialize_' . $context_hash;
if (!Cache::isStored($cache_id)) {
// if an id_address has been specified retrieve the address
if ($id_address) {
$address = new Address((int) $id_address);
if (!Validate::isLoadedObject($address)) {
throw new PrestaShopException('Invalid address #' . (int) $id_address);
}
} elseif ($with_geoloc && isset($context->customer->geoloc_id_country)) {
$address = new Address();
$address->id_country = (int) $context->customer->geoloc_id_country;
$address->id_state = (int) $context->customer->id_state;
$address->postcode = $context->customer->postcode;
} elseif ((int) $context->country->id && ((int) $context->country->id != Configuration::get('PS_SHOP_COUNTRY_ID'))) {
$address = new Address();
$address->id_country = (int) $context->country->id;
$address->id_state = 0;
$address->postcode = 0;
} elseif ((int) Configuration::get('PS_SHOP_COUNTRY_ID')) {
// set the default address
$address = new Address();
$address->id_country = Configuration::get('PS_SHOP_COUNTRY_ID');
$address->id_state = Configuration::get('PS_SHOP_STATE_ID');
$address->postcode = Configuration::get('PS_SHOP_CODE');
} else {
// set the default address
$address = new Address();
$address->id_country = Configuration::get('PS_COUNTRY_DEFAULT');
$address->id_state = 0;
$address->postcode = 0;
}
Cache::store($cache_id, $address);
return $address;
}
return Cache::retrieve($cache_id);
}
/**
* Returns Address ID for a given Supplier ID.
*
* @since 1.5.0
*
* @param int $id_supplier Supplier ID
*
* @return int $id_address Address ID
*/
public static function getAddressIdBySupplierId($id_supplier)
{
$query = new DbQuery();
$query->select('id_address');
$query->from('address');
$query->where('id_supplier = ' . (int) $id_supplier);
$query->where('deleted = 0');
$query->where('id_customer = 0');
$query->where('id_manufacturer = 0');
$query->where('id_warehouse = 0');
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query);
}
/**
* Check if the alias already exists.
*
* @param string $alias Alias of an address
* @param int $id_address Address id
* @param int $id_customer Customer id
*
* @return false|string|null Amount of aliases found
* @todo: Find out if we shouldn't be returning an int instead? (breaking change)
*/
public static function aliasExist($alias, $id_address, $id_customer)
{
$query = new DbQuery();
$query->select('count(*)');
$query->from('address');
$query->where('alias = \'' . pSQL($alias) . '\'');
$query->where('id_address != ' . (int) $id_address);
$query->where('id_customer = ' . (int) $id_customer);
$query->where('deleted = 0');
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query);
}
/**
* @see ObjectModel::getFieldsRequiredDB();
*/
public function getFieldsRequiredDB()
{
return parent::getCachedFieldsRequiredDatabase();
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class AddressChecksumCore.
*/
class AddressChecksumCore implements ChecksumInterface
{
const SEPARATOR = '_';
/**
* Generate a checksum.
*
* @param Address $address
*
* @return string SHA1 checksum for the Address
*/
public function generateChecksum($address)
{
if (!$address->id) {
return sha1('No address set');
}
$uniqId = '';
$fields = $address->getFields();
foreach ($fields as $name => $value) {
$uniqId .= $value . self::SEPARATOR;
}
$uniqId = rtrim($uniqId, self::SEPARATOR);
return sha1($uniqId);
}
}

648
classes/AddressFormat.php Normal file
View File

@@ -0,0 +1,648 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class AddressFormatCore.
*/
class AddressFormatCore extends ObjectModel
{
const FORMAT_NEW_LINE = "\n";
/** @var int $id_address_format Address format */
public $id_address_format;
/** @var int $id_country Country ID */
public $id_country;
/** @var string $format Format */
public $format;
protected $_errorFormatList = array();
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'address_format',
'primary' => 'id_country',
'fields' => array(
'format' => array('type' => self::TYPE_HTML, 'validate' => 'isGenericName', 'required' => true),
'id_country' => array('type' => self::TYPE_INT),
),
);
/** @var array Default required form fields list */
public static $requireFormFieldsList = array(
'firstname',
'lastname',
'address1',
'city',
'Country:name',
);
/** @var array Default forbidden property list */
public static $forbiddenPropertyList = array(
'deleted',
'date_add',
'alias',
'secure_key',
'note',
'newsletter',
'ip_registration_newsletter',
'newsletter_date_add',
'optin',
'passwd',
'last_passwd_gen',
'active',
'is_guest',
'date_upd',
'country',
'years',
'days',
'months',
'description',
'meta_description',
'short_description',
'link_rewrite',
'meta_title',
'meta_keywords',
'display_tax_label',
'need_zip_code',
'contains_states',
'call_prefixes',
'show_public_prices',
'max_payment',
'max_payment_days',
'geoloc_postcode',
'logged',
'account_number',
'groupBox',
'ape',
'max_payment',
'outstanding_allow_amount',
'call_prefix',
'definition',
'debug_list',
);
/** @var array Default formbidden class list */
public static $forbiddenClassList = array(
'Manufacturer',
'Supplier',
);
const _CLEANING_REGEX_ = '#([^\w:_]+)#i';
/**
* Check if the the association of the field name and a class name
* is valid.
*
* @param string $className The name class
* @param string $fieldName The property name
* @param bool $isIdField Do we have to allow a property name to be started with 'id_'
*
* @return bool Association of the field and class name is valid
*/
protected function _checkValidateClassField($className, $fieldName, $isIdField)
{
$isValid = false;
if (!class_exists($className)) {
$this->_errorFormatList[] = $this->trans('This class name does not exist.', array(), 'Admin.Notifications.Error') .
': ' . $className;
} else {
$obj = new $className();
$reflect = new ReflectionObject($obj);
// Check if the property is accessible
$publicProperties = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($publicProperties as $property) {
$propertyName = $property->getName();
if (($propertyName == $fieldName) && ($isIdField ||
(!preg_match('/\bid\b|id_\w+|\bid[A-Z]\w+/', $propertyName)))) {
$isValid = true;
}
}
if (!$isValid) {
$this->_errorFormatList[] = $this->trans('This property does not exist in the class or is forbidden.', array(), 'Admin.Notifications.Error') .
': ' . $className . ': ' . $fieldName;
}
unset(
$obj,
$reflect
);
}
return $isValid;
}
/**
* Verify the existence of a field name and check the availability
* of an association between a field name and a class (ClassName:fieldName)
* if the separator is overview.
*
* @param string $patternName The composition of the class and field name
* @param string $fieldsValidate The list of available field for the Address class
* @todo: Why is $fieldsValidate unused?
*/
protected function _checkLiableAssociation($patternName, $fieldsValidate)
{
$patternName = trim($patternName);
if ($associationName = explode(':', $patternName)) {
$totalNameUsed = count($associationName);
if ($totalNameUsed > 2) {
$this->_errorFormatList[] = $this->trans('This association has too many elements.', array(), 'Admin.Notifications.Error');
} elseif ($totalNameUsed == 1) {
$associationName[0] = strtolower($associationName[0]);
if (in_array($associationName[0], self::$forbiddenPropertyList) ||
!$this->_checkValidateClassField('Address', $associationName[0], false)) {
$this->_errorFormatList[] = $this->trans('This name is not allowed.', array(), 'Admin.Notifications.Error') . ': ' .
$associationName[0];
}
} elseif ($totalNameUsed == 2) {
if (empty($associationName[0]) || empty($associationName[1])) {
$this->_errorFormatList[] = $this->trans('Syntax error with this pattern.', array(), 'Admin.Notifications.Error') . ': ' . $patternName;
} else {
$associationName[0] = ucfirst($associationName[0]);
$associationName[1] = strtolower($associationName[1]);
if (in_array($associationName[0], self::$forbiddenClassList)) {
$this->_errorFormatList[] = $this->trans('This name is not allowed.', array(), 'Admin.Notifications.Error') . ': ' .
$associationName[0];
} else {
// Check if the id field name exist in the Address class
// Don't check this attribute on Address (no sense)
if ($associationName[0] != 'Address') {
$this->_checkValidateClassField('Address', 'id_' . strtolower($associationName[0]), true);
}
// Check if the field name exist in the class write by the user
$this->_checkValidateClassField($associationName[0], $associationName[1], false);
}
}
}
}
}
/**
* Check if the set fields are valid.
*/
public function checkFormatFields()
{
$this->_errorFormatList = array();
$fieldsValidate = Address::getFieldsValidate();
$usedKeyList = array();
$multipleLineFields = explode(self::FORMAT_NEW_LINE, $this->format);
if ($multipleLineFields && is_array($multipleLineFields)) {
foreach ($multipleLineFields as $lineField) {
if (($patternsName = preg_split(self::_CLEANING_REGEX_, $lineField, -1, PREG_SPLIT_NO_EMPTY))) {
if (is_array($patternsName)) {
foreach ($patternsName as $patternName) {
if (!in_array($patternName, $usedKeyList)) {
$this->_checkLiableAssociation($patternName, $fieldsValidate);
$usedKeyList[] = $patternName;
} else {
$this->_errorFormatList[] = $this->trans('This key has already been used.', array(), 'Admin.Notifications.Error') .
': ' . $patternName;
}
}
}
}
}
}
return (count($this->_errorFormatList)) ? false : true;
}
/**
* Returns the error list.
*/
public function getErrorList()
{
return $this->_errorFormatList;
}
/**
* Set the layout key with the liable value
* example : (firstname) => 'Presta' will result (Presta)
* : (firstname-lastname) => 'Presta' and 'Shop' result '(Presta-Shop)'.
*/
protected static function _setOriginalDisplayFormat(&$formattedValueList, $currentLine, $currentKeyList)
{
if ($currentKeyList && is_array($currentKeyList)) {
if ($originalFormattedPatternList = explode(' ', $currentLine)) {
// Foreach the available pattern
foreach ($originalFormattedPatternList as $patternNum => $pattern) {
// Var allows to modify the good formatted key value when multiple key exist into the same pattern
$mainFormattedKey = '';
// Multiple key can be found in the same pattern
foreach ($currentKeyList as $key) {
// Check if we need to use an older modified pattern if a key has already be matched before
$replacedValue = empty($mainFormattedKey) ? $pattern : $formattedValueList[$mainFormattedKey];
$chars = $start = $end = str_replace($key, '', $replacedValue);
if (preg_match(self::_CLEANING_REGEX_, $chars)) {
if (Tools::substr($replacedValue, 0, Tools::strlen($chars)) == $chars) {
$end = '';
} else {
$start = '';
}
if ($chars) {
$replacedValue = str_replace($chars, '', $replacedValue);
}
}
if ($formattedValue = preg_replace('/^' . $key . '$/', $formattedValueList[$key], $replacedValue, -1, $count)) {
if ($count) {
// Allow to check multiple key in the same pattern,
if (empty($mainFormattedKey)) {
$mainFormattedKey = $key;
}
// Set the pattern value to an empty string if an older key has already been matched before
if ($mainFormattedKey != $key) {
$formattedValueList[$key] = '';
}
// Store the new pattern value
$formattedValueList[$mainFormattedKey] = $start . $formattedValue . $end;
unset($originalFormattedPatternList[$patternNum]);
}
}
}
}
}
}
}
/**
* Cleaned the layout set by the user.
*/
public static function cleanOrderedAddress(&$orderedAddressField)
{
foreach ($orderedAddressField as &$line) {
$cleanedLine = '';
if (($keyList = preg_split(self::_CLEANING_REGEX_, $line, -1, PREG_SPLIT_NO_EMPTY))) {
foreach ($keyList as $key) {
$cleanedLine .= $key . ' ';
}
$cleanedLine = trim($cleanedLine);
$line = $cleanedLine;
}
}
}
/**
* Returns the formatted fields with associated values.
*
* @param Address $address Address object
* @param AddressFormat $addressFormat The format
*
* @return array
*/
public static function getFormattedAddressFieldsValues($address, $addressFormat, $id_lang = null)
{
if (!$id_lang) {
$id_lang = Context::getContext()->language->id;
}
$tab = array();
$temporyObject = array();
// Check if $address exist and it's an instanciate object of Address
if ($address && ($address instanceof Address)) {
foreach ($addressFormat as $line) {
if (($keyList = preg_split(self::_CLEANING_REGEX_, $line, -1, PREG_SPLIT_NO_EMPTY)) && is_array($keyList)) {
foreach ($keyList as $pattern) {
if ($associateName = explode(':', $pattern)) {
$totalName = count($associateName);
if ($totalName == 1 && isset($address->{$associateName[0]})) {
$tab[$associateName[0]] = $address->{$associateName[0]};
} else {
$tab[$pattern] = '';
// Check if the property exist in both classes
if (($totalName == 2) && class_exists($associateName[0]) &&
property_exists($associateName[0], $associateName[1]) &&
property_exists($address, 'id_' . strtolower($associateName[0]))) {
$idFieldName = 'id_' . strtolower($associateName[0]);
if (!isset($temporyObject[$associateName[0]])) {
$temporyObject[$associateName[0]] = new $associateName[0]($address->{$idFieldName});
}
if ($temporyObject[$associateName[0]]) {
$tab[$pattern] = (is_array($temporyObject[$associateName[0]]->{$associateName[1]})) ?
((isset($temporyObject[$associateName[0]]->{$associateName[1]}[$id_lang])) ?
$temporyObject[$associateName[0]]->{$associateName[1]}[$id_lang] : '') :
$temporyObject[$associateName[0]]->{$associateName[1]};
}
}
}
}
}
AddressFormat::_setOriginalDisplayFormat($tab, $line, $keyList);
}
}
}
AddressFormat::cleanOrderedAddress($addressFormat);
// Free the instanciate objects
foreach ($temporyObject as &$object) {
unset($object);
}
return $tab;
}
/**
* Generates the full address text.
*
* @param Address $address
* @param array $patternRules A defined rules array to avoid some pattern
* @param string $newLine A string containing the newLine format
* @param string $separator A string containing the separator format
* @param array $style
*
* @return string
*/
public static function generateAddress(Address $address, $patternRules = array(), $newLine = self::FORMAT_NEW_LINE, $separator = ' ', $style = array())
{
$addressFields = AddressFormat::getOrderedAddressFields($address->id_country);
$addressFormatedValues = AddressFormat::getFormattedAddressFieldsValues($address, $addressFields);
$addressText = '';
foreach ($addressFields as $line) {
if (($patternsList = preg_split(self::_CLEANING_REGEX_, $line, -1, PREG_SPLIT_NO_EMPTY))) {
$tmpText = '';
foreach ($patternsList as $pattern) {
if ((!array_key_exists('avoid', $patternRules)) ||
(is_array($patternRules) && array_key_exists('avoid', $patternRules) && !in_array($pattern, $patternRules['avoid']))) {
$tmpText .= (isset($addressFormatedValues[$pattern]) && !empty($addressFormatedValues[$pattern])) ?
(((isset($style[$pattern])) ?
(sprintf($style[$pattern], $addressFormatedValues[$pattern])) :
$addressFormatedValues[$pattern]) . $separator) : '';
}
}
$tmpText = trim($tmpText);
$addressText .= (!empty($tmpText)) ? $tmpText . $newLine : '';
}
}
$addressText = preg_replace('/' . preg_quote($newLine, '/') . '$/i', '', $addressText);
$addressText = rtrim($addressText, $separator);
return $addressText;
}
/**
* Generate formatted Address string for display on Smarty templates.
*
* @param array $params Address parameters
* @param Smarty $smarty Smarty instance
*
* @return string Formatted Address string
*/
public static function generateAddressSmarty($params, &$smarty)
{
return AddressFormat::generateAddress(
$params['address'],
(isset($params['patternRules']) ? $params['patternRules'] : array()),
(isset($params['newLine']) ? $params['newLine'] : self::FORMAT_NEW_LINE),
(isset($params['separator']) ? $params['separator'] : ' '),
(isset($params['style']) ? $params['style'] : array())
);
}
/**
* Returns selected fields required for an address in an array according to a selection hash.
*
* @return array String values
*/
public static function getValidateFields($className)
{
$propertyList = array();
if (class_exists($className)) {
$object = new $className();
$reflect = new ReflectionObject($object);
// Check if the property is accessible
$publicProperties = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($publicProperties as $property) {
$propertyName = $property->getName();
if ((!in_array($propertyName, AddressFormat::$forbiddenPropertyList)) &&
(!preg_match('#id|id_\w#', $propertyName))) {
$propertyList[] = $propertyName;
}
}
unset(
$object,
$reflect
);
}
return $propertyList;
}
/**
* Return a list of liable class of the className.
*
* @param string $className
*
* @return array
*/
public static function getLiableClass($className)
{
$objectList = array();
if (class_exists($className)) {
$object = new $className();
$reflect = new ReflectionObject($object);
// Get all the name object liable to the Address class
$publicProperties = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($publicProperties as $property) {
$propertyName = $property->getName();
if (preg_match('#id_\w#', $propertyName) && strlen($propertyName) > 3) {
$nameObject = ucfirst(substr($propertyName, 3));
if (!in_array($nameObject, self::$forbiddenClassList) &&
class_exists($nameObject)) {
$objectList[$nameObject] = new $nameObject();
}
}
}
unset(
$object,
$reflect
);
}
return $objectList;
}
/**
* Returns address format fields in array by country.
*
* @param int $idCountry If null using PS_COUNTRY_DEFAULT
* @param bool $splitAll
* @param bool $cleaned
*
* @return array String field address format
*/
public static function getOrderedAddressFields($idCountry = 0, $splitAll = false, $cleaned = false)
{
$out = array();
$fieldSet = explode(AddressFormat::FORMAT_NEW_LINE, AddressFormat::getAddressCountryFormat($idCountry));
foreach ($fieldSet as $fieldItem) {
if ($splitAll) {
if ($cleaned) {
$keyList = ($cleaned) ? preg_split(self::_CLEANING_REGEX_, $fieldItem, -1, PREG_SPLIT_NO_EMPTY) :
explode(' ', $fieldItem);
}
foreach ($keyList as $wordItem) {
$out[] = trim($wordItem);
}
} else {
$out[] = ($cleaned) ? implode(' ', preg_split(self::_CLEANING_REGEX_, trim($fieldItem), -1, PREG_SPLIT_NO_EMPTY))
: trim($fieldItem);
}
}
return $out;
}
/**
* Return a data array containing ordered, formatedValue and object fields.
*/
public static function getFormattedLayoutData($address)
{
$layoutData = array();
if ($address && $address instanceof Address) {
$layoutData['ordered'] = AddressFormat::getOrderedAddressFields((int) $address->id_country);
$layoutData['formated'] = AddressFormat::getFormattedAddressFieldsValues($address, $layoutData['ordered']);
$layoutData['object'] = array();
$reflect = new ReflectionObject($address);
$publicProperties = $reflect->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($publicProperties as $property) {
if (isset($address->{$property->getName()})) {
$layoutData['object'][$property->getName()] = $address->{$property->getName()};
}
}
}
return $layoutData;
}
/**
* Returns address format by country if not defined using default country.
*
* @param int $idCountry Country ID
*
* @return string field address format
*/
public static function getAddressCountryFormat($idCountry = 0)
{
$idCountry = (int) $idCountry;
$tmpObj = new AddressFormat();
$tmpObj->id_country = $idCountry;
$out = $tmpObj->getFormat($tmpObj->id_country);
unset($tmpObj);
return $out;
}
/**
* Returns address format by Country.
*
* @param int $idCountry Country ID
*
* @return string field Address format
*/
public function getFormat($idCountry)
{
$out = $this->getFormatDB($idCountry);
if (empty($out)) {
$out = $this->getFormatDB(Configuration::get('PS_COUNTRY_DEFAULT'));
}
if (Country::isNeedDniByCountryId($idCountry) && false === strpos($out, 'dni')) {
$out .= AddressFormat::FORMAT_NEW_LINE . 'dni';
}
return $out;
}
/**
* @param int $idCountry
*
* @return false|string|null
*
* @deprecated 1.7.0
*/
protected function _getFormatDB($idCountry)
{
return self::getFormatDB($idCountry);
}
/**
* Get Address format from DB.
*
* @param int $idCountry Country ID
*
* @return false|string|null Address format
*
* @since 1.7.0
*/
protected function getFormatDB($idCountry)
{
if (!Cache::isStored('AddressFormat::getFormatDB' . $idCountry)) {
$format = Db::getInstance()->getValue('
SELECT format
FROM `' . _DB_PREFIX_ . $this->def['table'] . '`
WHERE `id_country` = ' . (int) $idCountry);
$format = trim($format);
Cache::store('AddressFormat::getFormatDB' . $idCountry, $format);
return $format;
}
return Cache::retrieve('AddressFormat::getFormatDB' . $idCountry);
}
/**
* @see ObjectModel::getFieldsRequired()
*/
public static function getFieldsRequired()
{
$address = new CustomerAddress();
return array_unique(array_merge($address->getFieldsRequiredDB(), AddressFormat::$requireFormFieldsList));
}
}

171
classes/Alias.php Normal file
View File

@@ -0,0 +1,171 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class AliasCore.
*/
class AliasCore extends ObjectModel
{
public $alias;
public $search;
public $active = true;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'alias',
'primary' => 'id_alias',
'fields' => array(
'search' => array('type' => self::TYPE_STRING, 'validate' => 'isValidSearch', 'required' => true, 'size' => 255),
'alias' => array('type' => self::TYPE_STRING, 'validate' => 'isValidSearch', 'required' => true, 'size' => 255),
'active' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
),
);
/**
* AliasCore constructor.
*
* @param int|null $id Alias ID
* @param string|null $alias Alias
* @param string|null $search Search string
* @param int|null $idLang Language ID
*/
public function __construct($id = null, $alias = null, $search = null, $idLang = null)
{
$this->def = Alias::getDefinition($this);
$this->setDefinitionRetrocompatibility();
if ($id) {
parent::__construct($id);
} elseif ($alias && Validate::isValidSearch($alias)) {
if (!Alias::isFeatureActive()) {
$this->alias = trim($alias);
$this->search = trim($search);
} else {
$row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
SELECT a.id_alias, a.search, a.alias
FROM `' . _DB_PREFIX_ . 'alias` a
WHERE `alias` = \'' . pSQL($alias) . '\' AND `active` = 1');
if ($row) {
$this->id = (int) $row['id_alias'];
$this->search = $search ? trim($search) : $row['search'];
$this->alias = $row['alias'];
} else {
$this->alias = trim($alias);
$this->search = trim($search);
}
}
}
}
/**
* @see ObjectModel::add();
*/
public function add($autoDate = true, $nullValues = false)
{
$this->alias = Tools::replaceAccentedChars($this->alias);
$this->search = Tools::replaceAccentedChars($this->search);
if (parent::add($autoDate, $nullValues)) {
// Set cache of feature detachable to true
Configuration::updateGlobalValue('PS_ALIAS_FEATURE_ACTIVE', '1');
return true;
}
return false;
}
/**
* @see ObjectModel::delete();
*/
public function delete()
{
if (parent::delete()) {
// Refresh cache of feature detachable
Configuration::updateGlobalValue('PS_ALIAS_FEATURE_ACTIVE', Alias::isCurrentlyUsed($this->def['table'], true));
return true;
}
return false;
}
/**
* Get all found aliases from DB with search query.
*
* @return string Comma separated aliases
*/
public function getAliases()
{
if (!Alias::isFeatureActive()) {
return '';
}
$aliases = Db::getInstance()->executeS('
SELECT a.alias
FROM `' . _DB_PREFIX_ . 'alias` a
WHERE `search` = \'' . pSQL($this->search) . '\'');
$aliases = array_map('implode', $aliases);
return implode(', ', $aliases);
}
/**
* This method is allow to know if a feature is used or active.
*
* @since 1.5.0.1
*
* @return bool
*/
public static function isFeatureActive()
{
return Configuration::get('PS_ALIAS_FEATURE_ACTIVE');
}
/**
* This method is allow to know if a alias exist for AdminImportController.
*
* @param int $idAlias Alias ID
*
* @return bool
*
* @since 1.5.6.0
*/
public static function aliasExists($idAlias)
{
$sql = new DbQuery();
$sql->select('a.`id_alias`');
$sql->from('alias', 'a');
$sql->where('a.`id_alias` = ' . (int) $idAlias);
$row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql);
return isset($row['id_alias']);
}
}

252
classes/Attachment.php Normal file
View File

@@ -0,0 +1,252 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class AttachmentCore.
*/
class AttachmentCore extends ObjectModel
{
public $file;
public $file_name;
public $file_size;
public $name;
public $mime;
public $description;
/** @var int position Position */
public $position;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'attachment',
'primary' => 'id_attachment',
'multilang' => true,
'fields' => array(
'file' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 40),
'mime' => array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'required' => true, 'size' => 128),
'file_name' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 128),
'file_size' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 32),
'description' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isCleanHtml'),
),
);
/**
* @see ObjectModel::add()
*/
public function add($autoDate = true, $nullValues = false)
{
$this->file_size = filesize(_PS_DOWNLOAD_DIR_ . $this->file);
return parent::add($autoDate, $nullValues);
}
/**
* @see ObjectModel::update()
*/
public function update($nullValues = false)
{
$this->file_size = filesize(_PS_DOWNLOAD_DIR_ . $this->file);
return parent::update($nullValues);
}
/**
* @see ObjectModel::delete()
*/
public function delete()
{
@unlink(_PS_DOWNLOAD_DIR_ . $this->file);
$sql = new DbQuery();
$sql->select('pa.`id_product`');
$sql->from('product_attachment', 'pa');
$sql->where('pa.`id_attachment` = ' . (int) $this->id);
$products = Db::getInstance()->executeS($sql);
Db::getInstance()->delete(
'product_attachment',
'`id_attachment` = ' . (int) $this->id
);
foreach ($products as $product) {
Product::updateCacheAttachment((int) $product['id_product']);
}
return parent::delete();
}
/**
* Delete selection of attachments.
*
* @param array $attachments Attachments
*
* @return bool|int Whether the selection has been successfully deleted
* @todo: Find out if $return can be initialized with true. (breaking change)
*/
public function deleteSelection($attachments)
{
$return = 1;
foreach ($attachments as $idAttachment) {
$attachment = new Attachment((int) $idAttachment);
$return &= $attachment->delete();
}
return $return;
}
/**
* Get attachments.
*
* @param int $idLang Language ID
* @param int $idProduct Product ID
* @param bool $include Whether the attachments are included or excluded from the Product ID
*
* @return array|false|mysqli_result|PDOStatement|resource|null Database query result
*/
public static function getAttachments($idLang, $idProduct, $include = true)
{
return Db::getInstance()->executeS(
'
SELECT *
FROM ' . _DB_PREFIX_ . 'attachment a
LEFT JOIN ' . _DB_PREFIX_ . 'attachment_lang al
ON (a.id_attachment = al.id_attachment AND al.id_lang = ' . (int) $idLang . ')
WHERE a.id_attachment ' . ($include ? 'IN' : 'NOT IN') . ' (
SELECT pa.id_attachment
FROM ' . _DB_PREFIX_ . 'product_attachment pa
WHERE id_product = ' . (int) $idProduct . '
)'
);
}
/**
* Delete Product attachments for the given Product ID.
*
* @param int $idProduct Product ID
*
* @return bool
*/
public static function deleteProductAttachments($idProduct)
{
$res = Db::getInstance()->execute('
DELETE FROM ' . _DB_PREFIX_ . 'product_attachment
WHERE id_product = ' . (int) $idProduct);
Product::updateCacheAttachment((int) $idProduct);
return $res;
}
/**
* Associate $id_product to the current object.
*
* @param int $idProduct id of the product to associate
*
* @return bool true if success
*/
public function attachProduct($idProduct)
{
$res = Db::getInstance()->execute('
INSERT INTO ' . _DB_PREFIX_ . 'product_attachment
(id_attachment, id_product) VALUES
(' . (int) $this->id . ', ' . (int) $idProduct . ')');
Product::updateCacheAttachment((int) $idProduct);
return $res;
}
/**
* Associate an array of id_attachment $array to the product $id_product
* and remove eventual previous association.
*
* @param int $idProduct Product ID
* @param array $array Attachment IDs
*
* @return bool Whether the attachments have been successfully associated with the Product
*/
public static function attachToProduct($idProduct, $array)
{
$result1 = Attachment::deleteProductAttachments($idProduct);
if (is_array($array)) {
$ids = array();
foreach ($array as $idAttachment) {
if ((int) $idAttachment > 0) {
$ids[] = array('id_product' => (int) $idProduct, 'id_attachment' => (int) $idAttachment);
}
}
if (!empty($ids)) {
$result2 = Db::getInstance()->insert('product_attachment', $ids);
}
}
Product::updateCacheAttachment((int) $idProduct);
if (is_array($array)) {
return $result1 && (!isset($result2) || $result2);
}
return $result1;
}
/**
* Get Attachment IDs for the given Product within the given range of attachment IDs.
*
* @param int $idLang Language ID
* @param array $list List of attachment IDs in which to search
*
* @return array|bool List of attachment IDs found. False if nothing found.
*/
public static function getProductAttached($idLang, $list)
{
$idsAttachments = array();
if (is_array($list)) {
foreach ($list as $attachment) {
$idsAttachments[] = $attachment['id_attachment'];
}
$sql = 'SELECT * FROM `' . _DB_PREFIX_ . 'product_attachment` pa
LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl ON (pa.`id_product` = pl.`id_product`' . Shop::addSqlRestrictionOnLang('pl') . ')
WHERE `id_attachment` IN (' . implode(',', array_map('intval', $idsAttachments)) . ')
AND pl.`id_lang` = ' . (int) $idLang;
$tmp = Db::getInstance()->executeS($sql);
$productAttachments = array();
foreach ($tmp as $t) {
$productAttachments[$t['id_attachment']][] = $t['name'];
}
return $productAttachments;
} else {
return false;
}
}
}

401
classes/Attribute.php Normal file
View File

@@ -0,0 +1,401 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class AttributeCore.
*/
class AttributeCore extends ObjectModel
{
/** @var int Group id which attribute belongs */
public $id_attribute_group;
/** @var string Name */
public $name;
/** @var string $color */
public $color;
/** @var int $position */
public $position;
/** @todo Find type */
public $default;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'attribute',
'primary' => 'id_attribute',
'multilang' => true,
'fields' => array(
'id_attribute_group' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'color' => array('type' => self::TYPE_STRING, 'validate' => 'isColor'),
'position' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 128),
),
);
/** @var string $image_dir */
protected $image_dir = _PS_COL_IMG_DIR_;
/** @var array $webserviceParameters Web service parameters */
protected $webserviceParameters = array(
'objectsNodeName' => 'product_option_values',
'objectNodeName' => 'product_option_value',
'fields' => array(
'id_attribute_group' => array('xlink_resource' => 'product_options'),
),
);
/**
* AttributeCore constructor.
*
* @param int|null $id Attribute ID
* @param int|null $idLang Language ID
* @param int|null $idShop Shop ID
*/
public function __construct($id = null, $idLang = null, $idShop = null)
{
parent::__construct($id, $idLang, $idShop);
$this->image_dir = _PS_COL_IMG_DIR_;
}
/**
* @see ObjectModel::delete()
*/
public function delete()
{
if (!$this->hasMultishopEntries() || Shop::getContext() == Shop::CONTEXT_ALL) {
$result = Db::getInstance()->executeS('SELECT id_product_attribute FROM ' . _DB_PREFIX_ . 'product_attribute_combination WHERE id_attribute = ' . (int) $this->id);
$products = array();
foreach ($result as $row) {
$combination = new Combination($row['id_product_attribute']);
$newRequest = Db::getInstance()->executeS('SELECT id_product, default_on FROM ' . _DB_PREFIX_ . 'product_attribute WHERE id_product_attribute = ' . (int) $row['id_product_attribute']);
foreach ($newRequest as $value) {
if ($value['default_on'] == 1) {
$products[] = $value['id_product'];
}
}
$combination->delete();
}
foreach ($products as $product) {
$result = Db::getInstance()->executeS('SELECT id_product_attribute FROM ' . _DB_PREFIX_ . 'product_attribute WHERE id_product = ' . (int) $product . ' LIMIT 1');
foreach ($result as $row) {
if (Validate::isLoadedObject($product = new Product((int) $product))) {
$product->deleteDefaultAttributes();
$product->setDefaultAttribute($row['id_product_attribute']);
}
}
}
// Delete associated restrictions on cart rules
CartRule::cleanProductRuleIntegrity('attributes', $this->id);
/* Reinitializing position */
$this->cleanPositions((int) $this->id_attribute_group);
}
$return = parent::delete();
if ($return) {
Hook::exec('actionAttributeDelete', array('id_attribute' => $this->id));
}
return $return;
}
/**
* @see ObjectModel::update()
*/
public function update($nullValues = false)
{
$return = parent::update($nullValues);
if ($return) {
Hook::exec('actionAttributeSave', array('id_attribute' => $this->id));
}
return $return;
}
/**
* Adds current Attribute as a new Object to the database.
*
* @param bool $autoDate Automatically set `date_upd` and `date_add` column
* @param bool $nullValues Whether we want to use NULL values instead of empty quotes values
*
* @return bool Whether the Attribute has been successfully added
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function add($autoDate = true, $nullValues = false)
{
if ($this->position <= 0) {
$this->position = Attribute::getHigherPosition($this->id_attribute_group) + 1;
}
$return = parent::add($autoDate, $nullValues);
if ($return) {
Hook::exec('actionAttributeSave', array('id_attribute' => $this->id));
}
return $return;
}
/**
* Get all attributes for a given language.
*
* @param int $idLang Language ID
* @param bool $notNull Get only not null fields if true
*
* @return array Attributes
*/
public static function getAttributes($idLang, $notNull = false)
{
if (!Combination::isFeatureActive()) {
return array();
}
return Db::getInstance()->executeS('
SELECT DISTINCT ag.*, agl.*, a.`id_attribute`, al.`name`, agl.`name` AS `attribute_group`
FROM `' . _DB_PREFIX_ . 'attribute_group` ag
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_group_lang` agl
ON (ag.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = ' . (int) $idLang . ')
LEFT JOIN `' . _DB_PREFIX_ . 'attribute` a
ON a.`id_attribute_group` = ag.`id_attribute_group`
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_lang` al
ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = ' . (int) $idLang . ')
' . Shop::addSqlAssociation('attribute_group', 'ag') . '
' . Shop::addSqlAssociation('attribute', 'a') . '
' . ($notNull ? 'WHERE a.`id_attribute` IS NOT NULL AND al.`name` IS NOT NULL AND agl.`id_attribute_group` IS NOT NULL' : '') . '
ORDER BY agl.`name` ASC, a.`position` ASC
');
}
/**
* Check if the given name is an Attribute within the given AttributeGroup.
*
* @param int $idAttributeGroup AttributeGroup
* @param string $name Attribute name
* @param int $idLang Language ID
*
* @return array|bool
*/
public static function isAttribute($idAttributeGroup, $name, $idLang)
{
if (!Combination::isFeatureActive()) {
return array();
}
$result = Db::getInstance()->getValue('
SELECT COUNT(*)
FROM `' . _DB_PREFIX_ . 'attribute_group` ag
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_group_lang` agl
ON (ag.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = ' . (int) $idLang . ')
LEFT JOIN `' . _DB_PREFIX_ . 'attribute` a
ON a.`id_attribute_group` = ag.`id_attribute_group`
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_lang` al
ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = ' . (int) $idLang . ')
' . Shop::addSqlAssociation('attribute_group', 'ag') . '
' . Shop::addSqlAssociation('attribute', 'a') . '
WHERE al.`name` = \'' . pSQL($name) . '\' AND ag.`id_attribute_group` = ' . (int) $idAttributeGroup . '
ORDER BY agl.`name` ASC, a.`position` ASC
');
return (int) $result > 0;
}
/**
* Get quantity for a given attribute combination
* Check if quantity is enough to serve the customer.
*
* @param int $idProductAttribute Product attribute combination id
* @param int $qty Quantity needed
* @param Shop $shop Shop
*
* @return bool Quantity is available or not
*/
public static function checkAttributeQty($idProductAttribute, $qty, Shop $shop = null)
{
if (!$shop) {
$shop = Context::getContext()->shop;
}
$result = StockAvailable::getQuantityAvailableByProduct(null, (int) $idProductAttribute, $shop->id);
return $result && $qty <= $result;
}
/**
* Return true if the Attribute is a color.
*
* @return bool Color is the attribute type
*/
public function isColorAttribute()
{
if (!Db::getInstance()->getRow('
SELECT `group_type`
FROM `' . _DB_PREFIX_ . 'attribute_group`
WHERE `id_attribute_group` = (
SELECT `id_attribute_group`
FROM `' . _DB_PREFIX_ . 'attribute`
WHERE `id_attribute` = ' . (int) $this->id . ')
AND group_type = \'color\'')) {
return false;
}
return Db::getInstance()->numRows();
}
/**
* Get minimal quantity for product with attributes quantity.
*
* @param int $idProductAttribute Product Attribute ID
*
* @return mixed Minimal quantity or false if no result
*/
public static function getAttributeMinimalQty($idProductAttribute)
{
$minimalQuantity = Db::getInstance()->getValue(
'
SELECT `minimal_quantity`
FROM `' . _DB_PREFIX_ . 'product_attribute_shop` pas
WHERE `id_shop` = ' . (int) Context::getContext()->shop->id . '
AND `id_product_attribute` = ' . (int) $idProductAttribute
);
if ($minimalQuantity > 1) {
return (int) $minimalQuantity;
}
return false;
}
/**
* Move an attribute inside its group.
*
* @param bool $direction Up (1) or Down (0)
* @param int $position Current position of the attribute
*
* @return bool Update result
*/
public function updatePosition($direction, $position)
{
if (!$idAttributeGroup = (int) Tools::getValue('id_attribute_group')) {
$idAttributeGroup = (int) $this->id_attribute_group;
}
$sql = '
SELECT a.`id_attribute`, a.`position`, a.`id_attribute_group`
FROM `' . _DB_PREFIX_ . 'attribute` a
WHERE a.`id_attribute_group` = ' . (int) $idAttributeGroup . '
ORDER BY a.`position` ASC';
if (!$res = Db::getInstance()->executeS($sql)) {
return false;
}
foreach ($res as $attribute) {
if ((int) $attribute['id_attribute'] == (int) $this->id) {
$movedAttribute = $attribute;
}
}
if (!isset($movedAttribute) || !isset($position)) {
return false;
}
// < and > statements rather than BETWEEN operator
// since BETWEEN is treated differently according to databases
$res1 = Db::getInstance()->execute(
'
UPDATE `' . _DB_PREFIX_ . 'attribute`
SET `position`= `position` ' . ($direction ? '- 1' : '+ 1') . '
WHERE `position`
' . ($direction
? '> ' . (int) $movedAttribute['position'] . ' AND `position` <= ' . (int) $position
: '< ' . (int) $movedAttribute['position'] . ' AND `position` >= ' . (int) $position) . '
AND `id_attribute_group`=' . (int) $movedAttribute['id_attribute_group']
);
$res2 = Db::getInstance()->execute(
'
UPDATE `' . _DB_PREFIX_ . 'attribute`
SET `position` = ' . (int) $position . '
WHERE `id_attribute` = ' . (int) $movedAttribute['id_attribute'] . '
AND `id_attribute_group`=' . (int) $movedAttribute['id_attribute_group']
);
return $res1 && $res2;
}
/**
* Reorder the attribute position within the Attribute group.
* Call this method after deleting an attribute from a group.
*
* @param int $idAttributeGroup Attribute group ID
* @param bool $useLastAttribute
*
* @return bool Whether the result was successfully updated
*/
public function cleanPositions($idAttributeGroup, $useLastAttribute = true)
{
Db::getInstance()->execute('SET @i = -1', false);
$sql = 'UPDATE `' . _DB_PREFIX_ . 'attribute` SET `position` = @i:=@i+1 WHERE';
if ($useLastAttribute) {
$sql .= ' `id_attribute` != ' . (int) $this->id . ' AND';
}
$sql .= ' `id_attribute_group` = ' . (int) $idAttributeGroup . ' ORDER BY `position` ASC';
return Db::getInstance()->execute($sql);
}
/**
* get highest position.
*
* Get the highest attribute position from a group attribute
*
* @param int $idAttributeGroup AttributeGroup ID
*
* @return int $position Position
* @todo: Shouldn't this be called getHighestPosition instead?
*/
public static function getHigherPosition($idAttributeGroup)
{
$sql = 'SELECT MAX(`position`)
FROM `' . _DB_PREFIX_ . 'attribute`
WHERE id_attribute_group = ' . (int) $idAttributeGroup;
$position = Db::getInstance()->getValue($sql);
return (is_numeric($position)) ? $position : -1;
}
}

420
classes/AttributeGroup.php Normal file
View File

@@ -0,0 +1,420 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class AttributeGroupCore.
*/
class AttributeGroupCore extends ObjectModel
{
/** @var string Name */
public $name;
/** @var bool $is_color_group Whether the attribute group is a color group */
public $is_color_group;
/** @var int $position Position */
public $position;
/** @var string $group_type Group type */
public $group_type;
/** @var string Public Name */
public $public_name;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'attribute_group',
'primary' => 'id_attribute_group',
'multilang' => true,
'fields' => array(
'is_color_group' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'group_type' => array('type' => self::TYPE_STRING, 'required' => true),
'position' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 128),
'public_name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 64),
),
);
/** @var array $webserviceParameters Web service parameters */
protected $webserviceParameters = array(
'objectsNodeName' => 'product_options',
'objectNodeName' => 'product_option',
'fields' => array(),
'associations' => array(
'product_option_values' => array(
'resource' => 'product_option_value',
'fields' => array(
'id' => array(),
),
),
),
);
/**
* Adds current AttributeGroup as a new Object to the database.
*
* @param bool $autoDate Automatically set `date_upd` and `date_add` column
* @param bool $nullValues Whether we want to use NULL values instead of empty quotes values
*
* @return bool Whether the AttributeGroup has been successfully added
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function add($autoDate = true, $nullValues = false)
{
if ($this->group_type == 'color') {
$this->is_color_group = 1;
} else {
$this->is_color_group = 0;
}
if ($this->position <= 0) {
$this->position = AttributeGroup::getHigherPosition() + 1;
}
$return = parent::add($autoDate, true);
Hook::exec('actionAttributeGroupSave', array('id_attribute_group' => $this->id));
return $return;
}
/**
* Updates the current object in the database.
*
* @param bool $nullValues Whether we want to use NULL values instead of empty quotes values
*
* @return bool Whether the AttributeGroup has been succesfully updated
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function update($nullValues = false)
{
if ($this->group_type == 'color') {
$this->is_color_group = 1;
} else {
$this->is_color_group = 0;
}
$return = parent::update($nullValues);
Hook::exec('actionAttributeGroupSave', array('id_attribute_group' => $this->id));
return $return;
}
/**
* Clean dead combinations
* A combination is considered dead when its Attribute ID cannot be found.
*
* @return bool Whether the dead combinations have been successfully deleted
*/
public static function cleanDeadCombinations()
{
$attributeCombinations = Db::getInstance()->executeS('
SELECT pac.`id_attribute`, pa.`id_product_attribute`
FROM `' . _DB_PREFIX_ . 'product_attribute` pa
LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_combination` pac
ON (pa.`id_product_attribute` = pac.`id_product_attribute`)
');
$toRemove = array();
foreach ($attributeCombinations as $attributeCombination) {
if ((int) $attributeCombination['id_attribute'] == 0) {
$toRemove[] = (int) $attributeCombination['id_product_attribute'];
}
}
$return = true;
if (!empty($toRemove)) {
foreach ($toRemove as $remove) {
$combination = new Combination($remove);
$return &= $combination->delete();
}
}
return $return;
}
/**
* Deletes current AttributeGroup from database.
*
* @return bool True if delete was successful
*
* @throws PrestaShopException
*/
public function delete()
{
if (!$this->hasMultishopEntries() || Shop::getContext() == Shop::CONTEXT_ALL) {
/* Select children in order to find linked combinations */
$attributeIds = Db::getInstance()->executeS(
'
SELECT `id_attribute`
FROM `' . _DB_PREFIX_ . 'attribute`
WHERE `id_attribute_group` = ' . (int) $this->id
);
if ($attributeIds === false) {
return false;
}
/* Removing attributes to the found combinations */
$toRemove = array();
foreach ($attributeIds as $attribute) {
$toRemove[] = (int) $attribute['id_attribute'];
}
if (!empty($toRemove) && Db::getInstance()->execute('
DELETE FROM `' . _DB_PREFIX_ . 'product_attribute_combination`
WHERE `id_attribute`
IN (' . implode(', ', $toRemove) . ')') === false) {
return false;
}
/* Remove combinations if they do not possess attributes anymore */
if (!AttributeGroup::cleanDeadCombinations()) {
return false;
}
/* Also delete related attributes */
if (count($toRemove)) {
if (!Db::getInstance()->execute('
DELETE FROM `' . _DB_PREFIX_ . 'attribute_lang`
WHERE `id_attribute` IN (' . implode(',', $toRemove) . ')') ||
!Db::getInstance()->execute('
DELETE FROM `' . _DB_PREFIX_ . 'attribute_shop`
WHERE `id_attribute` IN (' . implode(',', $toRemove) . ')') ||
!Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'attribute` WHERE `id_attribute_group` = ' . (int) $this->id)) {
return false;
}
}
$this->cleanPositions();
}
$return = parent::delete();
if ($return) {
Hook::exec('actionAttributeGroupDelete', array('id_attribute_group' => $this->id));
}
return $return;
}
/**
* Get all attributes for a given language / group.
*
* @param int $idLang Language ID
* @param int $idAttributeGroup AttributeGroup ID
*
* @return array Attributes
*/
public static function getAttributes($idLang, $idAttributeGroup)
{
if (!Combination::isFeatureActive()) {
return array();
}
return Db::getInstance()->executeS('
SELECT *
FROM `' . _DB_PREFIX_ . 'attribute` a
' . Shop::addSqlAssociation('attribute', 'a') . '
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_lang` al
ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = ' . (int) $idLang . ')
WHERE a.`id_attribute_group` = ' . (int) $idAttributeGroup . '
ORDER BY `position` ASC
');
}
/**
* Get all attributes groups for a given language.
*
* @param int $idLang Language id
*
* @return array Attributes groups
*/
public static function getAttributesGroups($idLang)
{
if (!Combination::isFeatureActive()) {
return array();
}
return Db::getInstance()->executeS('
SELECT DISTINCT agl.`name`, ag.*, agl.*
FROM `' . _DB_PREFIX_ . 'attribute_group` ag
' . Shop::addSqlAssociation('attribute_group', 'ag') . '
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_group_lang` agl
ON (ag.`id_attribute_group` = agl.`id_attribute_group` AND `id_lang` = ' . (int) $idLang . ')
ORDER BY `name` ASC
');
}
/**
* Delete several objects from database.
*
* @param array $selection Array with AttributeGroup IDs
*
* @return bool Deletion result
*/
public function deleteSelection($selection)
{
/* Also delete Attributes */
foreach ($selection as $value) {
$obj = new AttributeGroup($value);
if (!$obj->delete()) {
return false;
}
}
return true;
}
/**
* Set the values of the current AttributeGroup for the webservice.
*
* @param array $values
*
* @return bool Whether the update was successful
*/
public function setWsProductOptionValues($values)
{
$ids = array();
foreach ($values as $value) {
$ids[] = (int) ($value['id']);
}
if (!empty($ids)) {
Db::getInstance()->execute(
'
DELETE FROM `' . _DB_PREFIX_ . 'attribute`
WHERE `id_attribute_group` = ' . (int) $this->id . '
AND `id_attribute` NOT IN (' . implode(',', $ids) . ')'
);
}
$ok = true;
foreach ($values as $value) {
$result = Db::getInstance()->execute(
'
UPDATE `' . _DB_PREFIX_ . 'attribute`
SET `id_attribute_group` = ' . (int) $this->id . '
WHERE `id_attribute` = ' . (int) $value['id']
);
if ($result === false) {
$ok = false;
}
}
return $ok;
}
/**
* Get values of current AttributeGroup instance for the webservice.
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public function getWsProductOptionValues()
{
$result = Db::getInstance()->executeS(
'
SELECT a.id_attribute AS id
FROM `' . _DB_PREFIX_ . 'attribute` a
' . Shop::addSqlAssociation('attribute', 'a') . '
WHERE a.id_attribute_group = ' . (int) $this->id
);
return $result;
}
/**
* Move a group attribute.
*
* @param bool $direction Up (1) or Down (0)
* @param int $position
*
* @return bool Update result
*/
public function updatePosition($direction, $position)
{
if (!$res = Db::getInstance()->executeS(
'
SELECT ag.`position`, ag.`id_attribute_group`
FROM `' . _DB_PREFIX_ . 'attribute_group` ag
WHERE ag.`id_attribute_group` = ' . (int) Tools::getValue('id_attribute_group', 1) . '
ORDER BY ag.`position` ASC'
)) {
return false;
}
foreach ($res as $groupAttribute) {
if ((int) $groupAttribute['id_attribute_group'] == (int) $this->id) {
$movedGroupAttribute = $groupAttribute;
}
}
if (!isset($movedGroupAttribute) || !isset($position)) {
return false;
}
// < and > statements rather than BETWEEN operator
// since BETWEEN is treated differently according to databases
return Db::getInstance()->execute(
'
UPDATE `' . _DB_PREFIX_ . 'attribute_group`
SET `position`= `position` ' . ($direction ? '- 1' : '+ 1') . '
WHERE `position`
' . ($direction
? '> ' . (int) $movedGroupAttribute['position'] . ' AND `position` <= ' . (int) $position
: '< ' . (int) $movedGroupAttribute['position'] . ' AND `position` >= ' . (int) $position)
) && Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'attribute_group`
SET `position` = ' . (int) $position . '
WHERE `id_attribute_group`=' . (int) $movedGroupAttribute['id_attribute_group']);
}
/**
* Reorder group attribute position
* Call it after deleting a group attribute.
*
* @return bool $return
*/
public static function cleanPositions()
{
$return = true;
Db::getInstance()->execute('SET @i = -1', false);
$return = Db::getInstance()->execute(
'
UPDATE `' . _DB_PREFIX_ . 'attribute_group`
SET `position` = @i:=@i+1
ORDER BY `position`'
);
return $return;
}
/**
* Get the highest AttributeGroup position.
*
* @return int $position Position
*/
public static function getHigherPosition()
{
$sql = 'SELECT MAX(`position`)
FROM `' . _DB_PREFIX_ . 'attribute_group`';
$position = Db::getInstance()->getValue($sql);
return (is_numeric($position)) ? $position : -1;
}
}

365
classes/CMS.php Normal file
View File

@@ -0,0 +1,365 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class CMSCore.
*/
class CMSCore extends ObjectModel
{
/** @var string Name */
public $id;
public $id_cms;
public $head_seo_title;
public $meta_title;
public $meta_description;
public $meta_keywords;
public $content;
public $link_rewrite;
public $id_cms_category;
public $position;
public $indexation;
public $active;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'cms',
'primary' => 'id_cms',
'multilang' => true,
'multilang_shop' => true,
'fields' => array(
'id_cms_category' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
'position' => array('type' => self::TYPE_INT),
'indexation' => array('type' => self::TYPE_BOOL),
'active' => array('type' => self::TYPE_BOOL),
/* Lang fields */
'meta_description' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 512),
'meta_keywords' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255),
'meta_title' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 255),
'head_seo_title' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255),
'link_rewrite' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isLinkRewrite', 'required' => true, 'size' => 128),
'content' => array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml', 'size' => 3999999999999),
),
);
protected $webserviceParameters = array(
'objectNodeName' => 'content',
'objectsNodeName' => 'content_management_system',
);
/**
* Adds current CMS 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 CMS has been successfully added
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function add($autoDate = true, $nullValues = false)
{
$this->position = CMS::getLastPosition((int) $this->id_cms_category);
return parent::add($autoDate, true);
}
/**
* Updates the current CMS in the database.
*
* @param bool $nullValues Whether we want to use NULL values instead of empty quotes values
*
* @return bool Indicates whether the CMS has been successfully updated
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function update($nullValues = false)
{
if (parent::update($nullValues)) {
return $this->cleanPositions($this->id_cms_category);
}
return false;
}
/**
* Deletes current CMS from the database.
*
* @return bool True if delete was successful
*
* @throws PrestaShopException
*/
public function delete()
{
if (parent::delete()) {
return $this->cleanPositions($this->id_cms_category);
}
return false;
}
/**
* Get links.
*
* @param int $idLang Language ID
* @param null $selection
* @param bool $active
* @param Link|null $link
*
* @return array
*/
public static function getLinks($idLang, $selection = null, $active = true, Link $link = null)
{
if (!$link) {
$link = Context::getContext()->link;
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT c.id_cms, cl.link_rewrite, cl.meta_title
FROM ' . _DB_PREFIX_ . 'cms c
LEFT JOIN ' . _DB_PREFIX_ . 'cms_lang cl ON (c.id_cms = cl.id_cms AND cl.id_lang = ' . (int) $idLang . ')
' . Shop::addSqlAssociation('cms', 'c') . '
WHERE 1
' . (($selection !== null) ? ' AND c.id_cms IN (' . implode(',', array_map('intval', $selection)) . ')' : '') .
($active ? ' AND c.`active` = 1 ' : '') .
'GROUP BY c.id_cms
ORDER BY c.`position`');
$links = array();
if ($result) {
foreach ($result as $row) {
$row['link'] = $link->getCMSLink((int) $row['id_cms'], $row['link_rewrite']);
$links[] = $row;
}
}
return $links;
}
/**
* @param null $idLang
* @param bool $idBlock
* @param bool $active
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public static function listCms($idLang = null, $idBlock = false, $active = true)
{
if (empty($idLang)) {
$idLang = (int) Configuration::get('PS_LANG_DEFAULT');
}
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT c.id_cms, l.meta_title
FROM ' . _DB_PREFIX_ . 'cms c
JOIN ' . _DB_PREFIX_ . 'cms_lang l ON (c.id_cms = l.id_cms)
' . Shop::addSqlAssociation('cms', 'c') . '
' . (($idBlock) ? 'JOIN ' . _DB_PREFIX_ . 'block_cms b ON (c.id_cms = b.id_cms)' : '') . '
WHERE l.id_lang = ' . (int) $idLang . (($idBlock) ? ' AND b.id_block = ' . (int) $idBlock : '') . ($active ? ' AND c.`active` = 1 ' : '') . '
GROUP BY c.id_cms
ORDER BY c.`position`');
}
/**
* @param $way
* @param $position
*
* @return bool
*/
public function updatePosition($way, $position)
{
if (!$res = Db::getInstance()->executeS(
'
SELECT cp.`id_cms`, cp.`position`, cp.`id_cms_category`
FROM `' . _DB_PREFIX_ . 'cms` cp
WHERE cp.`id_cms_category` = ' . (int) $this->id_cms_category . '
ORDER BY cp.`position` ASC'
)) {
return false;
}
foreach ($res as $cms) {
if ((int) $cms['id_cms'] == (int) $this->id) {
$movedCms = $cms;
}
}
if (!isset($movedCms) || !isset($position)) {
return false;
}
// < and > statements rather than BETWEEN operator
// since BETWEEN is treated differently according to databases
return Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'cms`
SET `position`= `position` ' . ($way ? '- 1' : '+ 1') . '
WHERE `position`
' . ($way
? '> ' . (int) $movedCms['position'] . ' AND `position` <= ' . (int) $position
: '< ' . (int) $movedCms['position'] . ' AND `position` >= ' . (int) $position) . '
AND `id_cms_category`=' . (int) $movedCms['id_cms_category'])
&& Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'cms`
SET `position` = ' . (int) $position . '
WHERE `id_cms` = ' . (int) $movedCms['id_cms'] . '
AND `id_cms_category`=' . (int) $movedCms['id_cms_category']);
}
/**
* @param $idCategory
*
* @return bool
*/
public static function cleanPositions($idCategory)
{
$sql = '
SELECT `id_cms`
FROM `' . _DB_PREFIX_ . 'cms`
WHERE `id_cms_category` = ' . (int) $idCategory . '
ORDER BY `position`';
$result = Db::getInstance()->executeS($sql);
for ($i = 0, $total = count($result); $i < $total; ++$i) {
$sql = 'UPDATE `' . _DB_PREFIX_ . 'cms`
SET `position` = ' . (int) $i . '
WHERE `id_cms_category` = ' . (int) $idCategory . '
AND `id_cms` = ' . (int) $result[$i]['id_cms'];
Db::getInstance()->execute($sql);
}
return true;
}
/**
* @param $idCategory
*
* @return false|string|null
*/
public static function getLastPosition($idCategory)
{
$sql = '
SELECT MAX(position) + 1
FROM `' . _DB_PREFIX_ . 'cms`
WHERE `id_cms_category` = ' . (int) $idCategory;
return Db::getInstance()->getValue($sql);
}
/**
* @param null $idLang
* @param null $idCmsCategory
* @param bool $active
* @param null $idShop
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public static function getCMSPages($idLang = null, $idCmsCategory = null, $active = true, $idShop = null)
{
$sql = new DbQuery();
$sql->select('*');
$sql->from('cms', 'c');
if ($idLang) {
if ($idShop) {
$sql->innerJoin('cms_lang', 'l', 'c.id_cms = l.id_cms AND l.id_lang = ' . (int) $idLang . ' AND l.id_shop = ' . (int) $idShop);
} else {
$sql->innerJoin('cms_lang', 'l', 'c.id_cms = l.id_cms AND l.id_lang = ' . (int) $idLang);
}
}
if ($idShop) {
$sql->innerJoin('cms_shop', 'cs', 'c.id_cms = cs.id_cms AND cs.id_shop = ' . (int) $idShop);
}
if ($active) {
$sql->where('c.active = 1');
}
if ($idCmsCategory) {
$sql->where('c.id_cms_category = ' . (int) $idCmsCategory);
}
$sql->orderBy('position');
return Db::getInstance()->executeS($sql);
}
/**
* @param $idCms
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public static function getUrlRewriteInformations($idCms)
{
$sql = 'SELECT l.`id_lang`, c.`link_rewrite`
FROM `' . _DB_PREFIX_ . 'cms_lang` AS c
LEFT JOIN `' . _DB_PREFIX_ . 'lang` AS l ON c.`id_lang` = l.`id_lang`
WHERE c.`id_cms` = ' . (int) $idCms . '
AND l.`active` = 1';
return Db::getInstance()->executeS($sql);
}
/**
* @param int $idCms
* @param int|null $idLang
* @param int|null $idShop
*
* @return array|bool|object|null
*/
public static function getCMSContent($idCms, $idLang = null, $idShop = null)
{
if (null === $idLang) {
$idLang = (int) Configuration::get('PS_LANG_DEFAULT');
}
if (null === $idShop) {
$idShop = (int) Configuration::get('PS_SHOP_DEFAULT');
}
$sql = '
SELECT `content`
FROM `' . _DB_PREFIX_ . 'cms_lang`
WHERE `id_cms` = ' . (int) $idCms . ' AND `id_lang` = ' . (int) $idLang . ' AND `id_shop` = ' . (int) $idShop;
return Db::getInstance()->getRow($sql);
}
/**
* Method required for new PrestaShop Core.
*
* @return string
*
* @since 1.7.0
*/
public static function getRepositoryClassName()
{
return '\\PrestaShop\\PrestaShop\\Core\\CMS\\CMSRepository';
}
}

711
classes/CMSCategory.php Normal file
View File

@@ -0,0 +1,711 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class CMSCategoryCore extends ObjectModel
{
public $id;
/** @var int CMSCategory ID */
public $id_cms_category;
/** @var string Name */
public $name;
/** @var bool Status for display */
public $active = 1;
/** @var string Description */
public $description;
/** @var int Parent CMSCategory ID */
public $id_parent;
/** @var int category position */
public $position;
/** @var int Parents number */
public $level_depth;
/** @var string string used in rewrited URL */
public $link_rewrite;
/** @var string Meta title */
public $meta_title;
/** @var string Meta keywords */
public $meta_keywords;
/** @var string Meta description */
public $meta_description;
/** @var string Object creation date */
public $date_add;
/** @var string Object last modification date */
public $date_upd;
protected static $_links = array();
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'cms_category',
'primary' => 'id_cms_category',
'multilang' => true,
'multilang_shop' => true,
'fields' => array(
'active' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true),
'id_parent' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'required' => true),
'position' => array('type' => self::TYPE_INT),
'level_depth' => array('type' => self::TYPE_INT),
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isCatalogName', 'required' => true, 'size' => 64),
'link_rewrite' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isLinkRewrite', 'required' => true, 'size' => 64),
'description' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isCleanHtml'),
'meta_title' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255),
'meta_description' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 512),
'meta_keywords' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255),
),
);
public function add($autodate = true, $null_values = false)
{
$this->position = CMSCategory::getLastPosition((int) $this->id_parent);
$this->level_depth = $this->calcLevelDepth();
foreach ($this->name as $k => $value) {
if (preg_match('/^[1-9]\./', $value)) {
$this->name[$k] = '0' . $value;
}
}
$ret = parent::add($autodate, $null_values);
$this->cleanPositions($this->id_parent);
return $ret;
}
public function update($null_values = false)
{
$this->level_depth = $this->calcLevelDepth();
foreach ($this->name as $k => $value) {
if (preg_match('/^[1-9]\./', $value)) {
$this->name[$k] = '0' . $value;
}
}
return parent::update($null_values);
}
/**
* Recursive scan of subcategories.
*
* @param int $max_depth Maximum depth of the tree (i.e. 2 => 3 levels depth)
* @param int $currentDepth specify the current depth in the tree (don't use it, only for rucursivity!)
* @param array $excluded_ids_array specify a list of ids to exclude of results
* @param int $idLang Specify the id of the language used
*
* @return array Subcategories lite tree
*/
public function recurseLiteCategTree($max_depth = 3, $currentDepth = 0, $id_lang = null, $excluded_ids_array = null, Link $link = null)
{
if (!$link) {
$link = Context::getContext()->link;
}
if (null === $id_lang) {
$id_lang = Context::getContext()->language->id;
}
// recursivity for subcategories
$children = array();
$subcats = $this->getSubCategories($id_lang, true);
if (($max_depth == 0 || $currentDepth < $max_depth) && $subcats && count($subcats)) {
foreach ($subcats as &$subcat) {
if (!$subcat['id_cms_category']) {
break;
} elseif (!is_array($excluded_ids_array) || !in_array($subcat['id_cms_category'], $excluded_ids_array)) {
$categ = new CMSCategory($subcat['id_cms_category'], $id_lang);
$categ->name = CMSCategory::hideCMSCategoryPosition($categ->name);
$children[] = $categ->recurseLiteCategTree($max_depth, $currentDepth + 1, $id_lang, $excluded_ids_array);
}
}
}
return array(
'id' => $this->id_cms_category,
'link' => $link->getCMSCategoryLink($this->id, $this->link_rewrite),
'name' => $this->name,
'desc' => $this->description,
'children' => $children,
);
}
public static function getRecurseCategory($id_lang = null, $current = 1, $active = 1, $links = 0, Link $link = null)
{
if (!$link) {
$link = Context::getContext()->link;
}
if (null === $id_lang) {
$id_lang = Context::getContext()->language->id;
}
$sql = 'SELECT c.`id_cms_category`, c.`id_parent`, c.`level_depth`, cl.`name`, cl.`link_rewrite`
FROM `' . _DB_PREFIX_ . 'cms_category` c
JOIN `' . _DB_PREFIX_ . 'cms_category_lang` cl ON c.`id_cms_category` = cl.`id_cms_category`
WHERE c.`id_cms_category` = ' . (int) $current . '
AND `id_lang` = ' . (int) $id_lang;
$category = Db::getInstance()->getRow($sql);
$sql = 'SELECT c.`id_cms_category`
FROM `' . _DB_PREFIX_ . 'cms_category` c
' . Shop::addSqlAssociation('cms_category', 'c') . '
WHERE c.`id_parent` = ' . (int) $current .
($active ? ' AND c.`active` = 1' : '');
$result = Db::getInstance()->executeS($sql);
foreach ($result as $row) {
$category['children'][] = CMSCategory::getRecurseCategory($id_lang, $row['id_cms_category'], $active, $links);
}
$sql = 'SELECT c.`id_cms`, cl.`meta_title`, cl.`link_rewrite`
FROM `' . _DB_PREFIX_ . 'cms` c
' . Shop::addSqlAssociation('cms', 'c') . '
JOIN `' . _DB_PREFIX_ . 'cms_lang` cl ON c.`id_cms` = cl.`id_cms`
WHERE `id_cms_category` = ' . (int) $current . '
AND cl.`id_lang` = ' . (int) $id_lang . ($active ? ' AND c.`active` = 1' : '') . '
GROUP BY c.id_cms
ORDER BY c.`position`';
$category['cms'] = Db::getInstance()->executeS($sql);
if ($links == 1) {
$category['link'] = $link->getCMSCategoryLink($current, $category['link_rewrite']);
foreach ($category['cms'] as $key => $cms) {
$category['cms'][$key]['link'] = $link->getCMSLink($cms['id_cms'], $cms['link_rewrite']);
}
}
return $category;
}
public static function recurseCMSCategory($categories, $current, $id_cms_category = 1, $id_selected = 1, $is_html = 0)
{
$html = '<option value="' . $id_cms_category . '"' . (($id_selected == $id_cms_category) ? ' selected="selected"' : '') . '>'
. str_repeat('&nbsp;', $current['infos']['level_depth'] * 5)
. CMSCategory::hideCMSCategoryPosition(stripslashes($current['infos']['name'])) . '</option>';
if ($is_html == 0) {
echo $html;
}
if (isset($categories[$id_cms_category])) {
foreach (array_keys($categories[$id_cms_category]) as $key) {
$html .= CMSCategory::recurseCMSCategory($categories, $categories[$id_cms_category][$key], $key, $id_selected, $is_html);
}
}
return $html;
}
/**
* Recursively add specified CMSCategory childs to $toDelete array.
*
* @param array &$toDelete Array reference where categories ID will be saved
* @param array|int $id_cms_category Parent CMSCategory ID
*/
protected function recursiveDelete(&$to_delete, $id_cms_category)
{
if (!is_array($to_delete) || !$id_cms_category) {
die(Tools::displayError());
}
$result = Db::getInstance()->executeS('
SELECT `id_cms_category`
FROM `' . _DB_PREFIX_ . 'cms_category`
WHERE `id_parent` = ' . (int) $id_cms_category);
foreach ($result as $row) {
$to_delete[] = (int) $row['id_cms_category'];
$this->recursiveDelete($to_delete, (int) $row['id_cms_category']);
}
}
/**
* Directly call the parent of delete, in order to avoid recursion.
*
* @return bool Deletion result
*/
private function deleteLite()
{
return parent::delete();
}
public function delete()
{
if ((int) $this->id === 1) {
return false;
}
$this->clearCache();
$cmsCategories = $this->getAllChildren();
$cmsCategories[] = $this;
foreach ($cmsCategories as $cmsCategory) {
/* @var CMSCategory */
$cmsCategory->deleteCMS();
$cmsCategory->deleteLite();
CMSCategory::cleanPositions($cmsCategory->id_parent);
}
return true;
}
/**
* Delete pages which are in CMSCategories to delete.
*
* @return bool Deletion result
*/
private function deleteCMS()
{
$result = true;
$cms = new PrestaShopCollection('CMS');
$cms->where('id_cms_category', '=', $this->id);
foreach ($cms as $c) {
$result &= $c->delete();
}
return $result;
}
/**
* Delete several categories from database.
*
* return boolean Deletion result
*/
public function deleteSelection($categories)
{
$return = 1;
foreach ($categories as $id_category_cms) {
$category_cms = new CMSCategory($id_category_cms);
$return &= $category_cms->delete();
}
return $return;
}
/**
* Get the number of parent categories.
*
* @return int Level depth
*/
public function calcLevelDepth()
{
$parentCMSCategory = new CMSCategory($this->id_parent);
if (!$parentCMSCategory) {
die(Tools::displayError('parent CMS Category does not exist'));
}
return $parentCMSCategory->level_depth + 1;
}
/**
* Return available categories.
*
* @param int $id_lang Language ID
* @param bool $active return only active categories
*
* @return array Categories
*/
public static function getCategories($id_lang, $active = true, $order = true)
{
if (!Validate::isBool($active)) {
die(Tools::displayError());
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT *
FROM `' . _DB_PREFIX_ . 'cms_category` c
LEFT JOIN `' . _DB_PREFIX_ . 'cms_category_lang` cl ON c.`id_cms_category` = cl.`id_cms_category`
WHERE `id_lang` = ' . (int) $id_lang . '
' . ($active ? 'AND `active` = 1' : '') . '
ORDER BY `name` ASC');
if (!$order) {
return $result;
}
$categories = array();
foreach ($result as $row) {
$categories[$row['id_parent']][$row['id_cms_category']]['infos'] = $row;
}
return $categories;
}
public static function getSimpleCategories($id_lang)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT c.`id_cms_category`, cl.`name`
FROM `' . _DB_PREFIX_ . 'cms_category` c
LEFT JOIN `' . _DB_PREFIX_ . 'cms_category_lang` cl ON (c.`id_cms_category` = cl.`id_cms_category`)
WHERE cl.`id_lang` = ' . (int) $id_lang . '
ORDER BY cl.`name`');
}
/**
* Return current CMSCategory childs.
*
* @param int $id_lang Language ID
* @param bool $active return only active categories
*
* @return array Categories
*/
public function getSubCategories($id_lang, $active = true)
{
if (!Validate::isBool($active)) {
die(Tools::displayError());
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT c.*, cl.id_lang, cl.name, cl.description, cl.link_rewrite, cl.meta_title, cl.meta_keywords, cl.meta_description
FROM `' . _DB_PREFIX_ . 'cms_category` c
LEFT JOIN `' . _DB_PREFIX_ . 'cms_category_lang` cl ON (c.`id_cms_category` = cl.`id_cms_category` AND `id_lang` = ' . (int) $id_lang . ')
WHERE `id_parent` = ' . (int) $this->id . '
' . ($active ? 'AND `active` = 1' : '') . '
GROUP BY c.`id_cms_category`
ORDER BY `name` ASC');
// Modify SQL result
foreach ($result as &$row) {
$row['name'] = CMSCategory::hideCMSCategoryPosition($row['name']);
}
return $result;
}
/**
* Hide CMSCategory prefix used for position.
*
* @param string $name CMSCategory name
*
* @return string Name without position
*/
public static function hideCMSCategoryPosition($name)
{
return preg_replace('/^[0-9]+\./', '', $name);
}
/**
* Return main categories.
*
* @param int $id_lang Language ID
* @param bool $active return only active categories
*
* @return array categories
*/
public static function getHomeCategories($id_lang, $active = true)
{
return CMSCategory::getChildren(1, $id_lang, $active);
}
public static function getChildren($id_parent, $id_lang, $active = true)
{
if (!Validate::isBool($active)) {
die(Tools::displayError());
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT c.`id_cms_category`, cl.`name`, cl.`link_rewrite`
FROM `' . _DB_PREFIX_ . 'cms_category` c
LEFT JOIN `' . _DB_PREFIX_ . 'cms_category_lang` cl ON c.`id_cms_category` = cl.`id_cms_category`
WHERE `id_lang` = ' . (int) $id_lang . '
AND c.`id_parent` = ' . (int) $id_parent . '
' . ($active ? 'AND `active` = 1' : '') . '
ORDER BY `name` ASC');
// Modify SQL result
$results_array = array();
foreach ($result as $row) {
$row['name'] = CMSCategory::hideCMSCategoryPosition($row['name']);
$results_array[] = $row;
}
return $results_array;
}
/**
* Return an array of all children of the current CMSCategory.
*
* @return PrestaShopCollection Collection of CMSCategory
*/
private function getAllChildren()
{
// Get children
$toDelete = array((int) $this->id);
$this->recursiveDelete($toDelete, (int) $this->id);
$toDelete = array_unique($toDelete);
// remove id of current CMSCategory because we want only ids of children
unset($toDelete[0]);
if (count($toDelete)) {
$children = new PrestaShopCollection('CMSCategory');
$children->where('id_cms_category', 'in', $toDelete);
return $children;
}
return $toDelete;
}
/**
* Check if CMSCategory can be moved in another one.
*
* @param int $id_parent Parent candidate
*
* @return bool Parent validity
*/
public static function checkBeforeMove($id_cms_category, $id_parent)
{
if ($id_cms_category == $id_parent) {
return false;
}
if ($id_parent == 1) {
return true;
}
$i = (int) $id_parent;
while (42) {
$result = Db::getInstance()->getRow('SELECT `id_parent` FROM `' . _DB_PREFIX_ . 'cms_category` WHERE `id_cms_category` = ' . (int) $i);
if (!isset($result['id_parent'])) {
return false;
}
if ($result['id_parent'] == $id_cms_category) {
return false;
}
if ($result['id_parent'] == 1) {
return true;
}
$i = $result['id_parent'];
}
}
public static function getLinkRewrite($id_cms_category, $id_lang)
{
if (!Validate::isUnsignedId($id_cms_category) || !Validate::isUnsignedId($id_lang)) {
return false;
}
if (isset(self::$_links[$id_cms_category . '-' . $id_lang])) {
return self::$_links[$id_cms_category . '-' . $id_lang];
}
$result = Db::getInstance()->getRow('
SELECT cl.`link_rewrite`
FROM `' . _DB_PREFIX_ . 'cms_category` c
LEFT JOIN `' . _DB_PREFIX_ . 'cms_category_lang` cl ON c.`id_cms_category` = cl.`id_cms_category`
WHERE `id_lang` = ' . (int) $id_lang . '
AND c.`id_cms_category` = ' . (int) $id_cms_category);
self::$_links[$id_cms_category . '-' . $id_lang] = $result['link_rewrite'];
return $result['link_rewrite'];
}
public function getLink(Link $link = null)
{
if (!$link) {
$link = Context::getContext()->link;
}
return $link->getCMSCategoryLink($this->id, $this->link_rewrite);
}
public function getName($id_lang = null)
{
$context = Context::getContext();
if (!$id_lang) {
if (isset($this->name[$context->language->id])) {
$id_lang = $context->language->id;
} else {
$id_lang = (int) Configuration::get('PS_LANG_DEFAULT');
}
}
return isset($this->name[$id_lang]) ? $this->name[$id_lang] : '';
}
/**
* Light back office search for categories.
*
* @param int $id_lang Language ID
* @param string $query Searched string
* @param bool $unrestricted allows search without lang and includes first CMSCategory and exact match
*
* @return array Corresponding categories
*/
public static function searchByName($id_lang, $query, $unrestricted = false)
{
if ($unrestricted === true) {
return Db::getInstance()->getRow('
SELECT c.*, cl.*
FROM `' . _DB_PREFIX_ . 'cms_category` c
LEFT JOIN `' . _DB_PREFIX_ . 'cms_category_lang` cl ON (c.`id_cms_category` = cl.`id_cms_category`)
WHERE `name` = \'' . pSQL($query) . '\'');
} else {
return Db::getInstance()->executeS('
SELECT c.*, cl.*
FROM `' . _DB_PREFIX_ . 'cms_category` c
LEFT JOIN `' . _DB_PREFIX_ . 'cms_category_lang` cl ON (c.`id_cms_category` = cl.`id_cms_category` AND `id_lang` = ' . (int) $id_lang . ')
WHERE `name` LIKE \'%' . pSQL($query) . '%\' AND c.`id_cms_category` != 1');
}
}
/**
* Retrieve CMSCategory by name and parent CMSCategory id.
*
* @param int $id_lang Language ID
* @param string $CMSCategory_name Searched CMSCategory name
* @param int $id_parent_CMSCategory parent CMSCategory ID
*
* @return array Corresponding CMSCategory
*
* @deprecated 1.5.3.0
*/
public static function searchByNameAndParentCMSCategoryId($id_lang, $CMSCategory_name, $id_parent_CMSCategory)
{
Tools::displayAsDeprecated();
return Db::getInstance()->getRow('
SELECT c.*, cl.*
FROM `' . _DB_PREFIX_ . 'cms_category` c
LEFT JOIN `' . _DB_PREFIX_ . 'cms_category_lang` cl ON (c.`id_cms_category` = cl.`id_cms_category` AND `id_lang` = ' . (int) $id_lang . ')
WHERE `name` = \'' . pSQL($CMSCategory_name) . '\'
AND c.`id_cms_category` != 1
AND c.`id_parent` = ' . (int) $id_parent_CMSCategory);
}
/**
* Get Each parent CMSCategory of this CMSCategory until the root CMSCategory.
*
* @param int $id_lang Language ID
*
* @return array Corresponding categories
*/
public function getParentsCategories($id_lang = null)
{
if (null === $id_lang) {
$id_lang = Context::getContext()->language->id;
}
$categories = null;
$id_current = $this->id;
while (true) {
$query = '
SELECT c.*, cl.*
FROM `' . _DB_PREFIX_ . 'cms_category` c
LEFT JOIN `' . _DB_PREFIX_ . 'cms_category_lang` cl ON (c.`id_cms_category` = cl.`id_cms_category` AND `id_lang` = ' . (int) $id_lang . ')
WHERE c.`id_cms_category` = ' . (int) $id_current . ' AND c.`id_parent` != 0
';
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($query);
$categories[] = $result[0];
if (!$result || $result[0]['id_parent'] == 1) {
return $categories;
}
$id_current = $result[0]['id_parent'];
}
}
public function updatePosition($way, $position)
{
if (!$res = Db::getInstance()->executeS(
'
SELECT cp.`id_cms_category`, cp.`position`, cp.`id_parent`
FROM `' . _DB_PREFIX_ . 'cms_category` cp
WHERE cp.`id_parent` = ' . (int) $this->id_parent . '
ORDER BY cp.`position` ASC'
)) {
return false;
}
foreach ($res as $category) {
if ((int) $category['id_cms_category'] == (int) $this->id) {
$moved_category = $category;
}
}
if (!isset($moved_category) || !isset($position)) {
return false;
}
// < and > statements rather than BETWEEN operator
// since BETWEEN is treated differently according to databases
return Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'cms_category`
SET `position`= `position` ' . ($way ? '- 1' : '+ 1') . '
WHERE `position`
' . ($way
? '> ' . (int) $moved_category['position'] . ' AND `position` <= ' . (int) $position
: '< ' . (int) $moved_category['position'] . ' AND `position` >= ' . (int) $position) . '
AND `id_parent`=' . (int) $moved_category['id_parent'])
&& Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'cms_category`
SET `position` = ' . (int) $position . '
WHERE `id_parent` = ' . (int) $moved_category['id_parent'] . '
AND `id_cms_category`=' . (int) $moved_category['id_cms_category']);
}
public static function cleanPositions($id_category_parent)
{
$result = Db::getInstance()->executeS('
SELECT `id_cms_category`
FROM `' . _DB_PREFIX_ . 'cms_category`
WHERE `id_parent` = ' . (int) $id_category_parent . '
ORDER BY `position`');
$sizeof = count($result);
for ($i = 0; $i < $sizeof; ++$i) {
$sql = '
UPDATE `' . _DB_PREFIX_ . 'cms_category`
SET `position` = ' . (int) $i . '
WHERE `id_parent` = ' . (int) $id_category_parent . '
AND `id_cms_category` = ' . (int) $result[$i]['id_cms_category'];
Db::getInstance()->execute($sql);
}
return true;
}
public static function getLastPosition($id_category_parent)
{
return Db::getInstance()->getValue('SELECT MAX(position)+1 FROM `' . _DB_PREFIX_ . 'cms_category` WHERE `id_parent` = ' . (int) $id_category_parent);
}
public static function getUrlRewriteInformations($id_category)
{
$sql = '
SELECT l.`id_lang`, c.`link_rewrite`
FROM `' . _DB_PREFIX_ . 'cms_category_lang` AS c
LEFT JOIN `' . _DB_PREFIX_ . 'lang` AS l ON c.`id_lang` = l.`id_lang`
WHERE c.`id_cms_category` = ' . (int) $id_category . '
AND l.`active` = 1';
$arr_return = Db::getInstance()->executeS($sql);
return $arr_return;
}
}

58
classes/CMSRole.php Normal file
View File

@@ -0,0 +1,58 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class CMSRoleCore.
*/
class CMSRoleCore extends ObjectModel
{
/** @var string name */
public $name;
/** @var int id_cms */
public $id_cms;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'cms_role',
'primary' => 'id_cms_role',
'fields' => array(
'name' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 50),
'id_cms' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
),
);
/**
* @return string
*
* @since 1.7.0
*/
public static function getRepositoryClassName()
{
return '\\PrestaShop\\PrestaShop\\Core\\CMS\\CMSRoleRepository';
}
}

113
classes/CSV.php Normal file
View File

@@ -0,0 +1,113 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Simple class to output CSV data
* Uses CollectionCore.
*
* @since 1.5
*/
class CSVCore
{
public $filename;
public $collection;
public $delimiter;
/**
* Loads objects, filename and optionnaly a delimiter.
*
* @param array|Iterator $collection Collection of objects / arrays (of non-objects)
* @param string $filename used later to save the file
* @param string $delimiter delimiter used
*/
public function __construct($collection, $filename, $delimiter = ';')
{
$this->filename = $filename;
$this->delimiter = $delimiter;
$this->collection = $collection;
}
/**
* Main function
* Adds headers
* Outputs.
*/
public function export()
{
$this->headers();
$headerLine = false;
foreach ($this->collection as $object) {
$vars = get_object_vars($object);
if (!$headerLine) {
$this->output(array_keys($vars));
$headerLine = true;
}
// outputs values
$this->output($vars);
unset($vars);
}
}
/**
* Wraps data and echoes
* Uses defined delimiter.
*
* @param array $data
*/
public function output($data)
{
$wrappedData = array_map(array('CSVCore', 'wrap'), $data);
echo sprintf("%s\n", implode($this->delimiter, $wrappedData));
}
/**
* Escapes data.
*
* @param string $data
*
* @return string $data
*/
public static function wrap($data)
{
$data = str_replace(array('"', ';'), '', $data);
return sprintf('"%s"', $data);
}
/**
* Adds headers.
*/
public function headers()
{
header('Content-type: text/csv');
header('Content-Type: application/force-download; charset=UTF-8');
header('Cache-Control: no-store, no-cache');
header('Content-disposition: attachment; filename="' . $this->filename . '.csv"');
}
}

1703
classes/Carrier.php Normal file

File diff suppressed because it is too large Load Diff

5000
classes/Cart.php Executable file

File diff suppressed because it is too large Load Diff

1744
classes/CartRule.php Executable file

File diff suppressed because it is too large Load Diff

2364
classes/Category.php Normal file

File diff suppressed because it is too large Load Diff

143
classes/Chart.php Normal file
View File

@@ -0,0 +1,143 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class ChartCore
{
protected static $poolId = 0;
protected $width = 600;
protected $height = 300;
/* Time mode */
protected $timeMode = false;
protected $from;
protected $to;
protected $format;
protected $granularity;
protected $curves = array();
/** @prototype void public static function init(void) */
public static function init()
{
if (!self::$poolId) {
++self::$poolId;
return true;
}
}
/** @prototype void public function __construct() */
public function __construct()
{
++self::$poolId;
}
/** @prototype void public function setSize(int $width, int $height) */
public function setSize($width, $height)
{
$this->width = (int) $width;
$this->height = (int) $height;
}
/** @prototype void public function setTimeMode($from, $to, $granularity) */
public function setTimeMode($from, $to, $granularity)
{
$this->granularity = $granularity;
if (Validate::isDate($from)) {
$from = strtotime($from);
}
$this->from = $from;
if (Validate::isDate($to)) {
$to = strtotime($to);
}
$this->to = $to;
if ($granularity == 'd') {
$this->format = '%d/%m/%y';
}
if ($granularity == 'w') {
$this->format = '%d/%m/%y';
}
if ($granularity == 'm') {
$this->format = '%m/%y';
}
if ($granularity == 'y') {
$this->format = '%y';
}
$this->timeMode = true;
}
public function getCurve($i)
{
if (!array_key_exists($i, $this->curves)) {
$this->curves[$i] = new Curve();
}
return $this->curves[$i];
}
/** @prototype void public function display() */
public function display()
{
echo $this->fetch();
}
public function fetch()
{
if ($this->timeMode) {
$options = 'xaxis:{mode:"time",timeformat:\'' . addslashes($this->format) . '\',min:' . $this->from . '000,max:' . $this->to . '000}';
if ($this->granularity == 'd') {
foreach ($this->curves as $curve) {
/* @var Curve $curve */
for ($i = $this->from; $i <= $this->to; $i = strtotime('+1 day', $i)) {
if (!$curve->getPoint($i)) {
$curve->setPoint($i, 0);
}
}
}
}
}
$jsCurves = array();
foreach ($this->curves as $curve) {
$jsCurves[] = $curve->getValues($this->timeMode);
}
if (count($jsCurves)) {
return '
<div id="flot' . self::$poolId . '" style="width:' . $this->width . 'px;height:' . $this->height . 'px"></div>
<script type="text/javascript">
$(function () {
$.plot($(\'#flot' . self::$poolId . '\'), [' . implode(',', $jsCurves) . '], {' . $options . '});
});
</script>';
} else {
return ErrorFacade::Display(PS_ERROR_UNDEFINED, 'No values for this chart.');
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
interface ChecksumInterface
{
public function generateChecksum($object);
}

459
classes/Combination.php Normal file
View File

@@ -0,0 +1,459 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class CombinationCore.
*/
class CombinationCore extends ObjectModel
{
/** @var int $id_product Product ID */
public $id_product;
public $reference;
/** @var string $supplier_reference */
public $supplier_reference;
public $location;
public $ean13;
public $isbn;
public $upc;
public $wholesale_price;
public $price;
public $unit_price_impact;
public $ecotax;
public $minimal_quantity = 1;
/** @var int|null Low stock for mail alert */
public $low_stock_threshold = null;
/** @var bool Low stock mail alert activated */
public $low_stock_alert = false;
public $quantity;
public $weight;
public $default_on;
public $available_date = '0000-00-00';
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'product_attribute',
'primary' => 'id_product_attribute',
'fields' => array(
'id_product' => array('type' => self::TYPE_INT, 'shop' => 'both', 'validate' => 'isUnsignedId', 'required' => true),
'location' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 64),
'ean13' => array('type' => self::TYPE_STRING, 'validate' => 'isEan13', 'size' => 13),
'isbn' => array('type' => self::TYPE_STRING, 'validate' => 'isIsbn', 'size' => 32),
'upc' => array('type' => self::TYPE_STRING, 'validate' => 'isUpc', 'size' => 12),
'quantity' => array('type' => self::TYPE_INT, 'validate' => 'isInt', 'size' => 10),
'reference' => array('type' => self::TYPE_STRING, 'size' => 64),
'supplier_reference' => array('type' => self::TYPE_STRING, 'size' => 64),
/* Shop fields */
'wholesale_price' => array('type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isPrice', 'size' => 27),
'price' => array('type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isNegativePrice', 'size' => 20),
'ecotax' => array('type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isPrice', 'size' => 20),
'weight' => array('type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isFloat'),
'unit_price_impact' => array('type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isNegativePrice', 'size' => 20),
'minimal_quantity' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedId', 'required' => true),
'low_stock_threshold' => array('type' => self::TYPE_INT, 'shop' => true, 'allow_null' => true, 'validate' => 'isInt'),
'low_stock_alert' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'),
'default_on' => array('type' => self::TYPE_BOOL, 'allow_null' => true, 'shop' => true, 'validate' => 'isBool'),
'available_date' => array('type' => self::TYPE_DATE, 'shop' => true, 'validate' => 'isDateFormat'),
),
);
protected $webserviceParameters = array(
'objectNodeName' => 'combination',
'objectsNodeName' => 'combinations',
'fields' => array(
'id_product' => array('required' => true, 'xlink_resource' => 'products'),
),
'associations' => array(
'product_option_values' => array('resource' => 'product_option_value'),
'images' => array('resource' => 'image', 'api' => 'images/products'),
),
);
/**
* Deletes current Combination from the database.
*
* @return bool True if delete was successful
*
* @throws PrestaShopException
*/
public function delete()
{
if (!parent::delete()) {
return false;
}
// Removes the product from StockAvailable, for the current shop
StockAvailable::removeProductFromStockAvailable((int) $this->id_product, (int) $this->id);
if ($specificPrices = SpecificPrice::getByProductId((int) $this->id_product, (int) $this->id)) {
foreach ($specificPrices as $specificPrice) {
$price = new SpecificPrice((int) $specificPrice['id_specific_price']);
$price->delete();
}
}
if (!$this->hasMultishopEntries() && !$this->deleteAssociations()) {
return false;
}
$this->deleteFromSupplier($this->id_product);
Product::updateDefaultAttribute($this->id_product);
Tools::clearColorListCache((int) $this->id_product);
return true;
}
/**
* Delete from Supplier.
*
* @param int $idProduct Product ID
*
* @return bool
*/
public function deleteFromSupplier($idProduct)
{
return Db::getInstance()->delete('product_supplier', 'id_product = ' . (int) $idProduct
. ' AND id_product_attribute = ' . (int) $this->id);
}
/**
* Adds current Combination 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 Combination has been successfully added
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function add($autoDate = true, $nullValues = false)
{
if ($this->default_on) {
$this->default_on = 1;
} else {
$this->default_on = null;
}
if (!parent::add($autoDate, $nullValues)) {
return false;
}
$product = new Product((int) $this->id_product);
if ($product->getType() == Product::PTYPE_VIRTUAL) {
StockAvailable::setProductOutOfStock((int) $this->id_product, 1, null, (int) $this->id);
} else {
StockAvailable::setProductOutOfStock((int) $this->id_product, StockAvailable::outOfStock((int) $this->id_product), null, $this->id);
}
SpecificPriceRule::applyAllRules(array((int) $this->id_product));
Product::updateDefaultAttribute($this->id_product);
return true;
}
/**
* Updates the current Combination in the database.
*
* @param bool $nullValues Whether we want to use NULL values instead of empty quotes values
*
* @return bool Indicates whether the Combination has been successfully updated
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function update($nullValues = false)
{
if ($this->default_on) {
$this->default_on = 1;
} else {
$this->default_on = null;
}
$return = parent::update($nullValues);
Product::updateDefaultAttribute($this->id_product);
return $return;
}
/**
* Delete associations.
*
* @return bool Indicates whether associations have been successfully deleted
*/
public function deleteAssociations()
{
$result = Db::getInstance()->delete('product_attribute_combination', '`id_product_attribute` = ' . (int) $this->id);
$result &= Db::getInstance()->delete('cart_product', '`id_product_attribute` = ' . (int) $this->id);
$result &= Db::getInstance()->delete('product_attribute_image', '`id_product_attribute` = ' . (int) $this->id);
if ($result) {
Hook::exec('actionAttributeCombinationDelete', array('id_product_attribute' => (int) $this->id));
}
return $result;
}
/**
* @param array $idsAttribute
*
* @return bool
*/
public function setAttributes($idsAttribute)
{
$result = $this->deleteAssociations();
if ($result && !empty($idsAttribute)) {
$sqlValues = array();
foreach ($idsAttribute as $value) {
$sqlValues[] = '(' . (int) $value . ', ' . (int) $this->id . ')';
}
$result = Db::getInstance()->execute(
'
INSERT INTO `' . _DB_PREFIX_ . 'product_attribute_combination` (`id_attribute`, `id_product_attribute`)
VALUES ' . implode(',', $sqlValues)
);
if ($result) {
Hook::exec('actionAttributeCombinationSave', array('id_product_attribute' => (int) $this->id, 'id_attributes' => $idsAttribute));
}
}
return $result;
}
/**
* @param array $values
*
* @return bool
*/
public function setWsProductOptionValues($values)
{
$idsAttributes = array();
foreach ($values as $value) {
$idsAttributes[] = $value['id'];
}
return $this->setAttributes($idsAttributes);
}
/**
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public function getWsProductOptionValues()
{
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT a.id_attribute AS id
FROM `' . _DB_PREFIX_ . 'product_attribute_combination` a
' . Shop::addSqlAssociation('attribute', 'a') . '
WHERE a.id_product_attribute = ' . (int) $this->id);
return $result;
}
/**
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public function getWsImages()
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT a.`id_image` as id
FROM `' . _DB_PREFIX_ . 'product_attribute_image` a
' . Shop::addSqlAssociation('product_attribute', 'a') . '
WHERE a.`id_product_attribute` = ' . (int) $this->id . '
');
}
/**
* @param $idsImage
*
* @return bool
*/
public function setImages($idsImage)
{
if (Db::getInstance()->execute('
DELETE FROM `' . _DB_PREFIX_ . 'product_attribute_image`
WHERE `id_product_attribute` = ' . (int) $this->id) === false) {
return false;
}
if (is_array($idsImage) && count($idsImage)) {
$sqlValues = array();
foreach ($idsImage as $value) {
$sqlValues[] = '(' . (int) $this->id . ', ' . (int) $value . ')';
}
if (is_array($sqlValues) && count($sqlValues)) {
Db::getInstance()->execute(
'
INSERT INTO `' . _DB_PREFIX_ . 'product_attribute_image` (`id_product_attribute`, `id_image`)
VALUES ' . implode(',', $sqlValues)
);
}
}
return true;
}
/**
* @param $values
*
* @return bool
*/
public function setWsImages($values)
{
$idsImages = array();
foreach ($values as $value) {
$idsImages[] = (int) $value['id'];
}
return $this->setImages($idsImages);
}
/**
* @param $idLang
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public function getAttributesName($idLang)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT al.*
FROM ' . _DB_PREFIX_ . 'product_attribute_combination pac
JOIN ' . _DB_PREFIX_ . 'attribute_lang al ON (pac.id_attribute = al.id_attribute AND al.id_lang=' . (int) $idLang . ')
WHERE pac.id_product_attribute=' . (int) $this->id);
}
/**
* This method is allow to know if a feature is active.
*
* @since 1.5.0.1
*
* @return bool
*/
public static function isFeatureActive()
{
static $feature_active = null;
if ($feature_active === null) {
$feature_active = (bool) Configuration::get('PS_COMBINATION_FEATURE_ACTIVE');
}
return $feature_active;
}
/**
* This method is allow to know if a Combination entity is currently used.
*
* @since 1.5.0.1
*
* @param $table
* @param $hasActiveColumn
*
* @return bool
*/
public static function isCurrentlyUsed($table = null, $hasActiveColumn = false)
{
return parent::isCurrentlyUsed('product_attribute');
}
/**
* For a given product_attribute reference, returns the corresponding id.
*
* @param int $idProduct
* @param string $reference
*
* @return int id
*/
public static function getIdByReference($idProduct, $reference)
{
if (empty($reference)) {
return 0;
}
$query = new DbQuery();
$query->select('pa.id_product_attribute');
$query->from('product_attribute', 'pa');
$query->where('pa.reference LIKE \'%' . pSQL($reference) . '%\'');
$query->where('pa.id_product = ' . (int) $idProduct);
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query);
}
/**
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public function getColorsAttributes()
{
return Db::getInstance()->executeS('
SELECT a.id_attribute
FROM ' . _DB_PREFIX_ . 'product_attribute_combination pac
JOIN ' . _DB_PREFIX_ . 'attribute a ON (pac.id_attribute = a.id_attribute)
JOIN ' . _DB_PREFIX_ . 'attribute_group ag ON (ag.id_attribute_group = a.id_attribute_group)
WHERE pac.id_product_attribute=' . (int) $this->id . ' AND ag.is_color_group = 1
');
}
/**
* Retrive the price of combination.
*
* @param int $idProductAttribute
*
* @return float mixed
*
* @since 1.5.0
*/
public static function getPrice($idProductAttribute)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
'
SELECT product_attribute_shop.`price`
FROM `' . _DB_PREFIX_ . 'product_attribute` pa
' . Shop::addSqlAssociation('product_attribute', 'pa') . '
WHERE pa.`id_product_attribute` = ' . (int) $idProductAttribute
);
}
}

747
classes/Configuration.php Normal file
View File

@@ -0,0 +1,747 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ConfigurationCore.
*/
class ConfigurationCore extends ObjectModel
{
public $id;
/** @var string Key */
public $name;
public $id_shop_group;
public $id_shop;
/** @var string Value */
public $value;
/** @var string Object creation date */
public $date_add;
/** @var string Object last modification date */
public $date_upd;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'configuration',
'primary' => 'id_configuration',
'multilang' => true,
'fields' => array(
'name' => array('type' => self::TYPE_STRING, 'validate' => 'isConfigName', 'required' => true, 'size' => 254),
'id_shop_group' => array('type' => self::TYPE_NOTHING, 'validate' => 'isUnsignedId'),
'id_shop' => array('type' => self::TYPE_NOTHING, 'validate' => 'isUnsignedId'),
'value' => array('type' => self::TYPE_STRING),
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
),
);
/** @var array Configuration cache (kept for backward compat) */
protected static $_cache = null;
/** @var array Configuration cache with optimised key order */
protected static $_new_cache_shop = null;
protected static $_new_cache_group = null;
protected static $_new_cache_global = null;
protected static $_initialized = false;
/** @var array Vars types */
protected static $types = array();
protected $webserviceParameters = array(
'fields' => array(
'value' => array(),
),
);
/**
* @see ObjectModel::getFieldsLang()
*
* @return bool|array Multilingual fields
*/
public function getFieldsLang()
{
if (!is_array($this->value)) {
return true;
}
return parent::getFieldsLang();
}
/**
* Return ID a configuration key.
*
* @param string $key
* @param int $idShopGroup
* @param int $idShop
*
* @return int Configuration key ID
*/
public static function getIdByName($key, $idShopGroup = null, $idShop = null)
{
if ($idShop === null) {
$idShop = Shop::getContextShopID(true);
}
if ($idShopGroup === null) {
$idShopGroup = Shop::getContextShopGroupID(true);
}
$sql = 'SELECT `' . bqSQL(self::$definition['primary']) . '`
FROM `' . _DB_PREFIX_ . bqSQL(self::$definition['table']) . '`
WHERE name = \'' . pSQL($key) . '\'
' . Configuration::sqlRestriction($idShopGroup, $idShop);
return (int) Db::getInstance()->getValue($sql);
}
/**
* Is the configuration loaded.
*
* @return bool `true` if configuration is loaded
*/
public static function configurationIsLoaded()
{
return self::$_initialized;
}
/**
* WARNING: For testing only. Do NOT rely on this method, it may be removed at any time.
*
* @todo Delegate static calls from Configuration to an instance
* of a class to be created.
*/
public static function clearConfigurationCacheForTesting()
{
self::$_cache = null;
self::$_new_cache_shop = null;
self::$_new_cache_group = null;
self::$_new_cache_global = null;
self::$_initialized = false;
}
/**
* Load all configuration data.
*/
public static function loadConfiguration()
{
$sql = 'SELECT c.`name`, cl.`id_lang`, IF(cl.`id_lang` IS NULL, c.`value`, cl.`value`) AS value, c.id_shop_group, c.id_shop
FROM `' . _DB_PREFIX_ . bqSQL(self::$definition['table']) . '` c
LEFT JOIN `' . _DB_PREFIX_ . bqSQL(self::$definition['table']) . '_lang` cl ON (c.`' . bqSQL(
self::$definition['primary']
) . '` = cl.`' . bqSQL(self::$definition['primary']) . '`)';
$db = Db::getInstance();
$results = $db->executeS($sql);
if ($results) {
foreach ($results as $row) {
$lang = ($row['id_lang']) ? $row['id_lang'] : 0;
self::$types[$row['name']] = (bool) $lang;
if (!isset(self::$_cache[self::$definition['table']][$lang])) {
self::$_cache[self::$definition['table']][$lang] = array(
'global' => array(),
'group' => array(),
'shop' => array(),
);
}
if ($row['value'] === null) {
$row['value'] = '';
}
if ($row['id_shop']) {
self::$_cache[self::$definition['table']][$lang]['shop'][$row['id_shop']][$row['name']] = $row['value'];
self::$_new_cache_shop[$row['name']][$lang][$row['id_shop']] = $row['value'];
} elseif ($row['id_shop_group']) {
self::$_cache[self::$definition['table']][$lang]['group'][$row['id_shop_group']][$row['name']] = $row['value'];
self::$_new_cache_group[$row['name']][$lang][$row['id_shop_group']] = $row['value'];
} else {
self::$_cache[self::$definition['table']][$lang]['global'][$row['name']] = $row['value'];
self::$_new_cache_global[$row['name']][$lang] = $row['value'];
}
}
self::$_initialized = true;
}
}
/**
* Get a single configuration value (in one language only).
*
* @param string $key Key wanted
* @param int $idLang Language ID
*
* @return string Value
*/
public static function get($key, $idLang = null, $idShopGroup = null, $idShop = null, $default = false)
{
if (defined('_PS_DO_NOT_LOAD_CONFIGURATION_') && _PS_DO_NOT_LOAD_CONFIGURATION_) {
return false;
}
// Init the cache on demand
if (!self::$_initialized) {
Configuration::loadConfiguration();
}
$idLang = (int) $idLang;
if (!self::isLangKey($key)) {
$idLang = 0;
}
if (self::$_new_cache_shop === null) {
$idShop = 0;
} else {
if ($idShop === null || !Shop::isFeatureActive()) {
$idShop = Shop::getContextShopID(true);
}
}
if (self::$_new_cache_group === null) {
$idShopGroup = 0;
} else {
if ($idShopGroup === null || !Shop::isFeatureActive()) {
$idShopGroup = Shop::getContextShopGroupID(true);
}
}
if ($idShop && Configuration::hasKey($key, $idLang, null, $idShop)) {
return self::$_new_cache_shop[$key][$idLang][$idShop];
} elseif ($idShopGroup && Configuration::hasKey($key, $idLang, $idShopGroup)) {
return self::$_new_cache_group[$key][$idLang][$idShopGroup];
} elseif (Configuration::hasKey($key, $idLang)) {
return self::$_new_cache_global[$key][$idLang];
}
return $default;
}
/**
* Get global value.
*
* @param string $key Configuration key
* @param int|null $idLang Language ID
*
* @return string
*/
public static function getGlobalValue($key, $idLang = null)
{
return Configuration::get($key, $idLang, 0, 0);
}
/**
* Get a single configuration value (in multiple languages).
*
* @param string $key Configuration Key
* @param int $idShopGroup Shop Group ID
* @param int $idShop Shop ID
*
* @return array Values in multiple languages
*/
public static function getInt($key, $idShopGroup = null, $idShop = null)
{
$resultsArray = array();
foreach (Language::getIDs() as $idLang) {
$resultsArray[$idLang] = Configuration::get($key, $idLang, $idShopGroup, $idShop);
}
return $resultsArray;
}
/**
* Get a single configuration value for all shops.
*
* @param string $key Key wanted
* @param int $idLang
*
* @return array Values for all shops
*/
public static function getMultiShopValues($key, $idLang = null)
{
$shops = Shop::getShops(false, null, true);
$resultsArray = array();
foreach ($shops as $idShop) {
$resultsArray[$idShop] = Configuration::get($key, $idLang, null, $idShop);
}
return $resultsArray;
}
/**
* Get several configuration values (in one language only).
*
* @throws PrestaShopException
*
* @param array $keys Keys wanted
* @param int $idLang Language ID
* @param int $idShopGroup
* @param int $idShop
*
* @return array Values
*/
public static function getMultiple($keys, $idLang = null, $idShopGroup = null, $idShop = null)
{
if (!is_array($keys)) {
throw new PrestaShopException('keys var is not an array');
}
$idLang = (int) $idLang;
if ($idShop === null) {
$idShop = Shop::getContextShopID(true);
}
if ($idShopGroup === null) {
$idShopGroup = Shop::getContextShopGroupID(true);
}
$results = array();
foreach ($keys as $key) {
$results[$key] = Configuration::get($key, $idLang, $idShopGroup, $idShop);
}
return $results;
}
/**
* Check if key exists in configuration.
*
* @param string $key
* @param int $idLang
* @param int $idShopGroup
* @param int $idShop
*
* @return bool
*/
public static function hasKey($key, $idLang = null, $idShopGroup = null, $idShop = null)
{
if (!is_int($key) && !is_string($key)) {
return false;
}
$idLang = (int) $idLang;
if ($idShop) {
return isset(self::$_new_cache_shop[$key][$idLang][$idShop]);
} elseif ($idShopGroup) {
return isset(self::$_new_cache_group[$key][$idLang][$idShopGroup]);
}
return isset(self::$_new_cache_global[$key][$idLang]);
}
/**
* Set TEMPORARY a single configuration value (in one language only).
*
* @param string $key Configuration key
* @param mixed $values `$values` is an array if the configuration is multilingual, a single string else
* @param int $idShopGroup
* @param int $idShop
*/
public static function set($key, $values, $idShopGroup = null, $idShop = null)
{
if (!Validate::isConfigName($key)) {
die(Tools::displayError(Context::getContext()->getTranslator()->trans('[%s] is not a valid configuration key', array(Tools::htmlentitiesUTF8($key)), 'Admin.Notifications.Error')));
}
if ($idShop === null) {
$idShop = (int) Shop::getContextShopID(true);
}
if ($idShopGroup === null) {
$idShopGroup = (int) Shop::getContextShopGroupID(true);
}
if (!is_array($values)) {
$values = array($values);
}
foreach ($values as $lang => $value) {
if ($idShop) {
self::$_new_cache_shop[$key][$lang][$idShop] = $value;
self::$_cache[self::$definition['table']][$lang]['shop'][$idShop][$key] = $value;
} elseif ($idShopGroup) {
self::$_new_cache_group[$key][$lang][$idShopGroup] = $value;
self::$_cache[self::$definition['table']][$lang]['group'][$idShopGroup][$key] = $value;
} else {
self::$_new_cache_global[$key][$lang] = $value;
self::$_cache[self::$definition['table']][$lang]['global'][$key] = $value;
}
}
}
/**
* Update configuration key for global context only.
*
* @param string $key
* @param mixed $values
* @param bool $html
*
* @return bool
*/
public static function updateGlobalValue($key, $values, $html = false)
{
return Configuration::updateValue($key, $values, $html, 0, 0);
}
/**
* Update configuration key and value into database (automatically insert if key does not exist).
*
* Values are inserted/updated directly using SQL, because using (Configuration) ObjectModel
* may not insert values correctly (for example, HTML is escaped, when it should not be).
*
* @TODO Fix saving HTML values in Configuration model
*
* @param string $key Configuration key
* @param mixed $values $values is an array if the configuration is multilingual, a single string else
* @param bool $html Specify if html is authorized in value
* @param int $idShopGroup
* @param int $idShop
*
* @return bool Update result
*/
public static function updateValue($key, $values, $html = false, $idShopGroup = null, $idShop = null)
{
if (!Validate::isConfigName($key)) {
die(Tools::displayError(Context::getContext()->getTranslator()->trans('[%s] is not a valid configuration key', array(Tools::htmlentitiesUTF8($key)), 'Admin.Notifications.Error')));
}
if ($idShop === null || !Shop::isFeatureActive()) {
$idShop = Shop::getContextShopID(true);
}
if ($idShopGroup === null || !Shop::isFeatureActive()) {
$idShopGroup = Shop::getContextShopGroupID(true);
}
if (!is_array($values)) {
$values = array($values);
}
if ($html) {
foreach ($values as &$value) {
$value = Tools::purifyHTML($value);
}
unset($value);
}
$result = true;
foreach ($values as $lang => $value) {
$storedValue = Configuration::get($key, $lang, $idShopGroup, $idShop);
// if there isn't a $stored_value, we must insert $value
if ((!is_numeric($value) && $value === $storedValue) || (is_numeric($value) && $value == $storedValue && Configuration::hasKey($key, $lang))) {
continue;
}
// If key already exists, update value
if (Configuration::hasKey($key, $lang, $idShopGroup, $idShop)) {
if (!$lang) {
// Update config not linked to lang
$result &= Db::getInstance()->update(self::$definition['table'], array(
'value' => pSQL($value, $html),
'date_upd' => date('Y-m-d H:i:s'),
), '`name` = \'' . pSQL($key) . '\'' . Configuration::sqlRestriction($idShopGroup, $idShop), 1, true);
} else {
// Update multi lang
$sql = 'UPDATE `' . _DB_PREFIX_ . bqSQL(self::$definition['table']) . '_lang` cl
SET cl.value = \'' . pSQL($value, $html) . '\',
cl.date_upd = NOW()
WHERE cl.id_lang = ' . (int) $lang . '
AND cl.`' . bqSQL(self::$definition['primary']) . '` = (
SELECT c.`' . bqSQL(self::$definition['primary']) . '`
FROM `' . _DB_PREFIX_ . bqSQL(self::$definition['table']) . '` c
WHERE c.name = \'' . pSQL($key) . '\''
. Configuration::sqlRestriction($idShopGroup, $idShop)
. ')';
$result &= Db::getInstance()->execute($sql);
}
} else {
// If key does not exists, create it
if (!$configID = Configuration::getIdByName($key, $idShopGroup, $idShop)) {
$now = date('Y-m-d H:i:s');
$data = array(
'id_shop_group' => $idShopGroup ? (int) $idShopGroup : null,
'id_shop' => $idShop ? (int) $idShop : null,
'name' => pSQL($key),
'value' => $lang ? null : pSQL($value, $html),
'date_add' => $now,
'date_upd' => $now,
);
$result &= Db::getInstance()->insert(self::$definition['table'], $data, true);
$configID = Db::getInstance()->Insert_ID();
}
if ($lang) {
$table = self::$definition['table'] . '_lang';
$selectConfiguration = strtr(
'SELECT 1 FROM {{ table }} WHERE id_lang = {{ lang }} ' .
'AND `{{ primary_key_column }}` = {{ config_id }}',
array(
'{{ table }}' => _DB_PREFIX_ . $table,
'{{ lang }}' => (int) $lang,
'{{ primary_key_column }}' => self::$definition['primary'],
'{{ config_id }}' => $configID,
)
);
$results = Db::getInstance()->getRow($selectConfiguration);
$configurationExists = is_array($results) && count($results) > 0;
$now = date('Y-m-d H:i:s');
$sanitizedValue = pSQL($value, $html);
if ($configurationExists) {
$condition = strtr(
'`{{ primary_key_column }}` = {{ config_id }} AND ' .
'date_upd = "{{ update_date }}" AND ' .
'value = "{{ value }}"',
array(
'{{ primary_key_column }}' => self::$definition['primary'],
'{{ config_id }}' => $configID,
'{{ update_date }}' => $now,
'{{ value }}' => $sanitizedValue,
)
);
$result &= Db::getInstance()->update($table, array(
'value' => $sanitizedValue,
'date_upd' => date('Y-m-d H:i:s'),
), $condition, 1, true);
} else {
$result &= Db::getInstance()->insert($table, array(
self::$definition['primary'] => $configID,
'id_lang' => (int) $lang,
'value' => $sanitizedValue,
'date_upd' => $now,
));
}
}
}
}
Configuration::set($key, $values, $idShopGroup, $idShop);
return $result;
}
/**
* Delete a configuration key in database (with or without language management).
*
* @param string $key Key to delete
*
* @return bool Deletion result
*/
public static function deleteByName($key)
{
if (!Validate::isConfigName($key)) {
return false;
}
$result = Db::getInstance()->execute('
DELETE FROM `' . _DB_PREFIX_ . bqSQL(self::$definition['table']) . '_lang`
WHERE `' . bqSQL(self::$definition['primary']) . '` IN (
SELECT `' . bqSQL(self::$definition['primary']) . '`
FROM `' . _DB_PREFIX_ . bqSQL(self::$definition['table']) . '`
WHERE `name` = "' . pSQL($key) . '"
)');
$result2 = Db::getInstance()->execute('
DELETE FROM `' . _DB_PREFIX_ . bqSQL(self::$definition['table']) . '`
WHERE `name` = "' . pSQL($key) . '"');
self::$_cache = null;
self::$_new_cache_shop = null;
self::$_new_cache_group = null;
self::$_new_cache_global = null;
self::$_initialized = false;
return $result && $result2;
}
/**
* Delete configuration key from current context.
*
* @param string $key
*/
public static function deleteFromContext($key)
{
if (Shop::getContext() == Shop::CONTEXT_ALL) {
return;
}
$idShop = null;
$idShopGroup = Shop::getContextShopGroupID(true);
if (Shop::getContext() == Shop::CONTEXT_SHOP) {
$idShop = Shop::getContextShopID(true);
}
$id = Configuration::getIdByName($key, $idShopGroup, $idShop);
Db::getInstance()->execute('
DELETE FROM `' . _DB_PREFIX_ . bqSQL(self::$definition['table']) . '`
WHERE `' . bqSQL(self::$definition['primary']) . '` = ' . (int) $id);
Db::getInstance()->execute('
DELETE FROM `' . _DB_PREFIX_ . bqSQL(self::$definition['table']) . '_lang`
WHERE `' . bqSQL(self::$definition['primary']) . '` = ' . (int) $id);
self::$_cache = null;
self::$_new_cache_shop = null;
self::$_new_cache_group = null;
self::$_new_cache_global = null;
self::$_initialized = false;
}
/**
* Check if configuration var is defined in given context.
*
* @param string $key
* @param int $idLang
* @param int $context
*/
public static function hasContext($key, $idLang, $context)
{
if (Shop::getContext() == Shop::CONTEXT_ALL) {
$idShop = $idShopGroup = null;
} elseif (Shop::getContext() == Shop::CONTEXT_GROUP) {
$idShopGroup = Shop::getContextShopGroupID(true);
$idShop = null;
} else {
$idShopGroup = Shop::getContextShopGroupID(true);
$idShop = Shop::getContextShopID(true);
}
if ($context == Shop::CONTEXT_SHOP && Configuration::hasKey($key, $idLang, null, $idShop)) {
return true;
} elseif ($context == Shop::CONTEXT_GROUP && Configuration::hasKey($key, $idLang, $idShopGroup)) {
return true;
} elseif ($context == Shop::CONTEXT_ALL && Configuration::hasKey($key, $idLang)) {
return true;
}
return false;
}
/**
* @param string $key
*
* @return bool
*/
public static function isOverridenByCurrentContext($key)
{
if (Configuration::isLangKey($key)) {
$testContext = false;
foreach (Language::getIDs(false) as $idLang) {
if ((Shop::getContext() == Shop::CONTEXT_SHOP && Configuration::hasContext($key, $idLang, Shop::CONTEXT_SHOP))
|| (Shop::getContext() == Shop::CONTEXT_GROUP && Configuration::hasContext($key, $idLang, Shop::CONTEXT_GROUP))) {
$testContext = true;
}
}
} else {
$testContext = ((Shop::getContext() == Shop::CONTEXT_SHOP && Configuration::hasContext($key, null, Shop::CONTEXT_SHOP))
|| (Shop::getContext() == Shop::CONTEXT_GROUP && Configuration::hasContext($key, null, Shop::CONTEXT_GROUP))) ? true : false;
}
return Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_ALL && $testContext;
}
/**
* Check if a key was loaded as multi lang.
*
* @param string $key
*
* @return bool
*/
public static function isLangKey($key)
{
return isset(self::$types[$key]) && self::$types[$key];
}
/**
* @return bool
*/
public static function isCatalogMode()
{
if (is_a(Context::getContext()->controller, 'FrontController')) {
$isCatalogMode =
Configuration::get('PS_CATALOG_MODE') ||
!Configuration::showPrices() ||
(Context::getContext()->controller->getRestrictedCountry() == Country::GEOLOC_CATALOG_MODE);
} else {
$isCatalogMode =
Configuration::get('PS_CATALOG_MODE') ||
!Configuration::showPrices();
}
return $isCatalogMode;
}
/**
* @return bool
*/
public static function showPrices()
{
return Group::isFeatureActive() ? (bool) Group::getCurrent()->show_prices : true;
}
/**
* Add SQL restriction on shops for configuration table.
*
* @param int $idShopGroup
* @param int $idShop
*
* @return string
*/
protected static function sqlRestriction($idShopGroup, $idShop)
{
if ($idShop) {
return ' AND id_shop = ' . (int) $idShop;
} elseif ($idShopGroup) {
return ' AND id_shop_group = ' . (int) $idShopGroup . ' AND (id_shop IS NULL OR id_shop = 0)';
} else {
return ' AND (id_shop_group IS NULL OR id_shop_group = 0) AND (id_shop IS NULL OR id_shop = 0)';
}
}
/**
* This method is override to allow TranslatedConfiguration entity.
*
* @param string $sqlJoin
* @param string $sqlFilter
* @param string $sqlSort
* @param string $sqlLimit
*
* @return array
*/
public function getWebserviceObjectList($sqlJoin, $sqlFilter, $sqlSort, $sqlLimit)
{
$query = '
SELECT DISTINCT main.`' . bqSQL($this->def['primary']) . '`
FROM `' . _DB_PREFIX_ . bqSQL($this->def['table']) . '` main
' . $sqlJoin . '
WHERE id_configuration NOT IN (
SELECT id_configuration
FROM `' . _DB_PREFIX_ . bqSQL($this->def['table']) . '_lang`
) ' . $sqlFilter . '
' . ($sqlSort != '' ? $sqlSort : '') . '
' . ($sqlLimit != '' ? $sqlLimit : '');
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($query);
}
}

View File

@@ -0,0 +1,312 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ConfigurationKPICore.
*/
class ConfigurationKPICore extends Configuration
{
public static $definition_backup;
/**
* Set KPI definition.
*/
public static function setKpiDefinition()
{
ConfigurationKPI::$definition_backup = Configuration::$definition;
Configuration::$definition['table'] = 'configuration_kpi';
Configuration::$definition['primary'] = 'id_configuration_kpi';
}
/**
* Unset KPI definition.
*/
public static function unsetKpiDefinition()
{
Configuration::$definition = ConfigurationKPI::$definition_backup;
}
/**
* Get ID by name.
*
* @param string $key Configuration key
* @param int|null $idShopGroup ShopGroup ID
* @param int|null $idShop Shop ID
*
* @return int ConfigurationKPI ID
*/
public static function getIdByName($key, $idShopGroup = null, $idShop = null)
{
ConfigurationKPI::setKpiDefinition();
$configurationKpi = parent::getIdByName($key, $idShopGroup, $idShop);
ConfigurationKPI::unsetKpiDefinition();
return $configurationKpi;
}
/**
* Load configuration.
*/
public static function loadConfiguration()
{
ConfigurationKPI::setKpiDefinition();
parent::loadConfiguration();
ConfigurationKPI::unsetKpiDefinition();
}
/**
* Get value.
*
* @param string $key Configuration key
* @param null $idLang Language ID
* @param null $idShopGroup ShopGroup ID
* @param null $idShop Shop ID
* @param bool $default Default value
*
* @return string
*/
public static function get($key, $idLang = null, $idShopGroup = null, $idShop = null, $default = false)
{
ConfigurationKPI::setKpiDefinition();
$value = parent::get($key, $idLang, $idShopGroup, $idShop, $default);
ConfigurationKPI::unsetKpiDefinition();
return $value;
}
/**
* Get global vlaue.
*
* @param string $key Configuration key
* @param int|null $idLang Language ID
*
* @return string Global value
*/
public static function getGlobalValue($key, $idLang = null)
{
ConfigurationKPI::setKpiDefinition();
$globalValue = parent::getGlobalValue($key, $idLang);
ConfigurationKPI::unsetKpiDefinition();
return $globalValue;
}
/**
* Get value independent from language.
*
* @param string $key Configuration key
* @param null $idShopGroup ShopGroup ID
* @param null $idShop Shop ID
*
* @return array Values for key for all available languages
*/
public static function getInt($key, $idShopGroup = null, $idShop = null)
{
ConfigurationKPI::setKpiDefinition();
$values = parent::getInt($key, $idShopGroup, $idShop);
ConfigurationKPI::unsetKpiDefinition();
return $values;
}
/**
* Get multiple keys.
*
* @param array $keys Configuation keys
* @param int|null $idLang Language ID
* @param int|null $idShopGroup ShopGroup ID
* @param int|null $idShop Shop ID
*
* @return array Configuration values
*/
public static function getMultiple($keys, $idLang = null, $idShopGroup = null, $idShop = null)
{
ConfigurationKPI::setKpiDefinition();
$configurationValues = parent::getMultiple($keys, $idLang, $idShopGroup, $idShop);
ConfigurationKPI::unsetKpiDefinition();
return $configurationValues;
}
/**
* Has key.
*
* @param string $key
* @param int|null $idLang Language ID
* @param int|null $idShopGroup ShopGroup ID
* @param int|null $idShop Shop ID
*
* @return bool
*/
public static function hasKey($key, $idLang = null, $idShopGroup = null, $idShop = null)
{
ConfigurationKPI::setKpiDefinition();
$hasKey = parent::hasKey($key, $idLang, $idShopGroup, $idShop);
ConfigurationKPI::unsetKpiDefinition();
return $hasKey;
}
/**
* Set key.
*
* @param string $key Configuration key
* @param mixed $values Values
* @param null $idShopGroup ShopGroup ID
* @param null $idShop Shop ID
*/
public static function set($key, $values, $idShopGroup = null, $idShop = null)
{
ConfigurationKPI::setKpiDefinition();
parent::set($key, $values, $idShopGroup, $idShop);
ConfigurationKPI::unsetKpiDefinition();
}
/**
* Update global value.
*
* @param string $key Configuration key
* @param mixed $values Values
* @param bool $html Do the values contain HTML?
*
* @return bool Indicates whether the key was successfully updated
*/
public static function updateGlobalValue($key, $values, $html = false)
{
ConfigurationKPI::setKpiDefinition();
$updateSuccess = parent::updateGlobalValue($key, $values, $html);
ConfigurationKPI::unsetKpiDefinition();
return $updateSuccess;
}
/**
* Update value.
*
* @param string $key Configuration key
* @param mixed $values Values
* @param bool $html Do the values contain HTML?
* @param null $idShopGroup ShopGroup ID
* @param null $idShop Shop ID
*
* @return bool Indicates whether the key was successfully updated
*/
public static function updateValue($key, $values, $html = false, $idShopGroup = null, $idShop = null)
{
ConfigurationKPI::setKpiDefinition();
$updateSuccess = parent::updateValue($key, $values, $html, $idShopGroup, $idShop);
ConfigurationKPI::unsetKpiDefinition();
return $updateSuccess;
}
/**
* @param string $key
*
* @return bool
*/
public static function deleteByName($key)
{
ConfigurationKPI::setKpiDefinition();
$deleteSuccess = parent::deleteByName($key);
ConfigurationKPI::unsetKpiDefinition();
return $deleteSuccess;
}
/**
* @param string $key
*
* @return bool
*/
public static function deleteFromContext($key)
{
ConfigurationKPI::setKpiDefinition();
$deleteSuccess = parent::deleteFromContext($key);
ConfigurationKPI::unsetKpiDefinition();
return $deleteSuccess;
}
/**
* @param string $key
* @param int $idLang
* @param int $context
*
* @return bool
*/
public static function hasContext($key, $idLang, $context)
{
ConfigurationKPI::setKpiDefinition();
$hasContext = parent::hasContext($key, $idLang, $context);
ConfigurationKPI::unsetKpiDefinition();
return $hasContext;
}
/**
* @param string $key
*
* @return bool
*/
public static function isOverridenByCurrentContext($key)
{
ConfigurationKPI::setKpiDefinition();
$isOverriden = parent::isOverridenByCurrentContext($key);
ConfigurationKPI::unsetKpiDefinition();
return $isOverriden;
}
/**
* @param string $key
*
* @return bool
*/
public static function isLangKey($key)
{
ConfigurationKPI::setKpiDefinition();
$isLangKey = parent::isLangKey($key);
ConfigurationKPI::unsetKpiDefinition();
return $isLangKey;
}
/**
* @param int $idShopGroup
* @param int $idShop
*
* @return string
*/
protected static function sqlRestriction($idShopGroup, $idShop)
{
ConfigurationKPI::setKpiDefinition();
$sqlRestriction = parent::sqlRestriction($idShopGroup, $idShop);
ConfigurationKPI::unsetKpiDefinition();
return $sqlRestriction;
}
}

View File

@@ -0,0 +1,434 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class ConfigurationTestCore
{
public static $test_files = array(
'/classes/log/index.php',
'/classes/cache/index.php',
'/config/index.php',
'/controllers/admin/AdminLoginController.php',
'/download/index.php',
'/js/tools.js',
'/js/jquery/plugins/fancybox/jquery.fancybox.js',
'/localization/fr.xml',
'/mails/index.php',
'/modules/index.php',
'/override/controllers/front/index.php',
'/pdf/order-return.tpl',
'/translations/export/index.php',
'/webservice/dispatcher.php',
'/index.php',
'/vendor/autoload.php',
);
/**
* getDefaultTests return an array of tests to executes.
* key are method name, value are parameters (false for no parameter)
* all path are _PS_ROOT_DIR_ related.
*
* @return array
*/
public static function getDefaultTests()
{
$tests = array(
'upload' => false,
'cache_dir' => 'var/cache',
'log_dir' => 'var/logs',
'img_dir' => 'img',
'module_dir' => 'modules',
'theme_lang_dir' => 'themes/' . _THEME_NAME_ . '/lang/',
'theme_pdf_lang_dir' => 'themes/' . _THEME_NAME_ . '/pdf/lang/',
'theme_cache_dir' => 'themes/' . _THEME_NAME_ . '/cache/',
'translations_dir' => 'translations',
'customizable_products_dir' => 'upload',
'virtual_products_dir' => 'download',
'config_sf2_dir' => 'app/config',
'translations_sf2' => 'app/Resources/translations',
);
if (!defined('_PS_HOST_MODE_')) {
$tests = array_merge($tests, array(
'system' => array(
'fopen', 'fclose', 'fread', 'fwrite',
'rename', 'file_exists', 'unlink', 'rmdir', 'mkdir',
'getcwd', 'chdir', 'chmod',
),
'phpversion' => false,
'apache_mod_rewrite' => false,
'curl' => false,
'gd' => false,
'json' => false,
'pdo_mysql' => false,
'config_dir' => 'config',
'files' => false,
'mails_dir' => 'mails',
'openssl' => 'false',
'simplexml' => false,
'zip' => false,
'fileinfo' => false,
'intl' => false,
));
}
return $tests;
}
/**
* getDefaultTestsOp return an array of tests to executes.
* key are method name, value are parameters (false for no parameter).
*
* @return array
*/
public static function getDefaultTestsOp()
{
return array(
'new_phpversion' => false,
'gz' => false,
'mbstring' => false,
'dom' => false,
'pdo_mysql' => false,
'fopen' => false,
'intl' => false,
);
}
/**
* run all test defined in $tests.
*
* @param array $tests
*
* @return array results of tests
*/
public static function check($tests)
{
$res = array();
foreach ($tests as $key => $test) {
$res[$key] = ConfigurationTest::run($key, $test);
}
return $res;
}
public static function run($ptr, $arg = 0)
{
if (call_user_func(array('ConfigurationTest', 'test_' . $ptr), $arg)) {
return 'ok';
}
return 'fail';
}
public static function test_phpversion()
{
return version_compare(substr(PHP_VERSION, 0, 5), '5.6.0', '>=');
}
public static function test_apache_mod_rewrite()
{
if (isset($_SERVER['SERVER_SOFTWARE'])
&& strpos(strtolower($_SERVER['SERVER_SOFTWARE']), 'apache') === false || !function_exists('apache_get_modules')) {
return true;
}
return in_array('mod_rewrite', apache_get_modules());
}
public static function test_new_phpversion()
{
return version_compare(substr(PHP_VERSION, 0, 5), '5.6.0', '>=');
}
public static function test_mysql_support()
{
return extension_loaded('mysql') || extension_loaded('mysqli') || extension_loaded('pdo_mysql');
}
public static function test_intl()
{
return extension_loaded('intl');
}
public static function test_pdo_mysql()
{
return extension_loaded('pdo_mysql');
}
public static function test_upload()
{
return ini_get('file_uploads');
}
public static function test_fopen()
{
return in_array(ini_get('allow_url_fopen'), array('On', 'on', '1'));
}
public static function test_system($funcs)
{
foreach ($funcs as $func) {
if (!function_exists($func)) {
return false;
}
}
return true;
}
public static function test_curl()
{
return extension_loaded('curl');
}
public static function test_gd()
{
return function_exists('imagecreatetruecolor');
}
public static function test_json()
{
return extension_loaded('json');
}
public static function test_gz()
{
if (function_exists('gzencode')) {
return @gzencode('dd') !== false;
}
return false;
}
public static function test_simplexml()
{
return extension_loaded('SimpleXML');
}
public static function test_zip()
{
return extension_loaded('zip');
}
public static function test_fileinfo()
{
return extension_loaded('fileinfo');
}
public static function test_dir($relative_dir, $recursive = false, &$full_report = null)
{
$dir = rtrim(_PS_ROOT_DIR_, '\\/') . DIRECTORY_SEPARATOR . trim($relative_dir, '\\/');
if (!file_exists($dir) || !$dh = @opendir($dir)) {
$full_report = sprintf('Directory %s does not exist or is not writable', $dir); // sprintf for future translation
return false;
}
closedir($dh);
$dummy = rtrim($dir, '\\/') . DIRECTORY_SEPARATOR . uniqid();
if (@file_put_contents($dummy, 'test')) {
@unlink($dummy);
if (!$recursive) {
return true;
}
} elseif (!is_writable($dir)) {
$full_report = sprintf('Directory %s is not writable', $dir); // sprintf for future translation
return false;
}
if ($recursive) {
foreach (Tools::getDirectories($dir) as $file) {
if (!ConfigurationTest::test_dir($relative_dir . DIRECTORY_SEPARATOR . $file, $recursive, $full_report)) {
return false;
}
}
}
return true;
}
public static function test_file($file_relative)
{
$file = _PS_ROOT_DIR_ . DIRECTORY_SEPARATOR . $file_relative;
return file_exists($file) && is_writable($file);
}
public static function test_config_dir($dir)
{
return ConfigurationTest::test_dir($dir);
}
public static function test_sitemap($dir)
{
return ConfigurationTest::test_file($dir);
}
public static function test_root_dir($dir)
{
return ConfigurationTest::test_dir($dir);
}
public static function test_log_dir($dir)
{
return ConfigurationTest::test_dir($dir);
}
public static function test_admin_dir($dir)
{
return ConfigurationTest::test_dir($dir);
}
public static function test_img_dir($dir)
{
return ConfigurationTest::test_dir($dir, true);
}
public static function test_module_dir($dir)
{
return ConfigurationTest::test_dir($dir, true);
}
public static function test_cache_dir($dir)
{
return ConfigurationTest::test_dir($dir, true);
}
public static function test_tools_v2_dir($dir)
{
return ConfigurationTest::test_dir($dir);
}
public static function test_cache_v2_dir($dir)
{
return ConfigurationTest::test_dir($dir);
}
public static function test_download_dir($dir)
{
return ConfigurationTest::test_dir($dir);
}
public static function test_mails_dir($dir)
{
return ConfigurationTest::test_dir($dir, true);
}
public static function test_translations_dir($dir)
{
return ConfigurationTest::test_dir($dir, true);
}
public static function test_config_sf2_dir($dir)
{
return ConfigurationTest::test_dir($dir, true);
}
public static function test_theme_lang_dir($dir)
{
$absoluteDir = rtrim(_PS_ROOT_DIR_, '\\/') . DIRECTORY_SEPARATOR . trim($dir, '\\/');
if (!file_exists($absoluteDir)) {
return true;
}
return ConfigurationTest::test_dir($dir, true);
}
public static function test_theme_pdf_lang_dir($dir)
{
$absoluteDir = rtrim(_PS_ROOT_DIR_, '\\/') . DIRECTORY_SEPARATOR . trim($dir, '\\/');
if (!file_exists($absoluteDir)) {
return true;
}
return ConfigurationTest::test_dir($dir, true);
}
public static function test_theme_cache_dir($dir)
{
$absoluteDir = rtrim(_PS_ROOT_DIR_, '\\/') . DIRECTORY_SEPARATOR . trim($dir, '\\/');
if (!file_exists($absoluteDir)) {
return true;
}
return ConfigurationTest::test_dir($dir, true);
}
public static function test_customizable_products_dir($dir)
{
return ConfigurationTest::test_dir($dir);
}
public static function test_virtual_products_dir($dir)
{
return ConfigurationTest::test_dir($dir);
}
public static function test_mbstring()
{
return function_exists('mb_strtolower');
}
public static function test_openssl()
{
return function_exists('openssl_encrypt');
}
public static function test_sessions()
{
if (!$path = @ini_get('session.save_path')) {
return true;
}
return is_writable($path);
}
public static function test_dom()
{
return extension_loaded('Dom');
}
public static function test_files($full = false)
{
$return = array();
foreach (ConfigurationTest::$test_files as $file) {
if (!file_exists(rtrim(_PS_ROOT_DIR_, DIRECTORY_SEPARATOR) . str_replace('/', DIRECTORY_SEPARATOR, $file))) {
if ($full) {
$return[] = $file;
} else {
return false;
}
}
}
if ($full) {
return $return;
}
return true;
}
public static function test_translations_sf2($dir)
{
return ConfigurationTest::test_dir($dir);
}
}

242
classes/Connection.php Normal file
View File

@@ -0,0 +1,242 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ConnectionCore.
*/
class ConnectionCore extends ObjectModel
{
/** @var int $id_guest */
public $id_guest;
/** @var int $id_page */
public $id_page;
/** @var string $ip_address */
public $ip_address;
/** @var string $http_referer */
public $http_referer;
/** @var int $id_shop */
public $id_shop;
/** @var int $id_shop_group */
public $id_shop_group;
/** @var string $date_add */
public $date_add;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'connections',
'primary' => 'id_connections',
'fields' => array(
'id_guest' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_page' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'ip_address' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
'http_referer' => array('type' => self::TYPE_STRING, 'validate' => 'isAbsoluteUrl'),
'id_shop' => array('type' => self::TYPE_INT, 'required' => true),
'id_shop_group' => array('type' => self::TYPE_INT, 'required' => true),
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
),
);
/**
* @see ObjectModel::getFields()
*
* @return array
*/
public function getFields()
{
if (!$this->id_shop_group) {
$this->id_shop_group = Context::getContext()->shop->id_shop_group;
}
$fields = parent::getFields();
return $fields;
}
/**
* @param Cookie $cookie
* @param bool $full
*
* @return array
*/
public static function setPageConnection($cookie, $full = true)
{
$idPage = false;
// The connection is created if it does not exist yet and we get the current page id
if (!isset($cookie->id_connections) || !strstr(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '', Tools::getHttpHost(false, false))) {
$idPage = Connection::setNewConnection($cookie);
}
// If we do not track the pages, no need to get the page id
if (!Configuration::get('PS_STATSDATA_PAGESVIEWS') && !Configuration::get('PS_STATSDATA_CUSTOMER_PAGESVIEWS')) {
return array();
}
if (!$idPage) {
$idPage = Page::getCurrentId();
}
// If we do not track the page views by customer, the id_page is the only information needed
if (!Configuration::get('PS_STATSDATA_CUSTOMER_PAGESVIEWS')) {
return array('id_page' => $idPage);
}
// The ending time will be updated by an ajax request when the guest will close the page
$timeStart = date('Y-m-d H:i:s');
Db::getInstance()->insert(
'connections_page',
array(
'id_connections' => (int) $cookie->id_connections,
'id_page' => (int) $idPage,
'time_start' => $timeStart,
),
false,
true,
Db::INSERT_IGNORE
);
// This array is serialized and used by the ajax request to identify the page
return array(
'id_connections' => (int) $cookie->id_connections,
'id_page' => (int) $idPage,
'time_start' => $timeStart,
);
}
/**
* @param Cookie $cookie
*
* @return int|bool Connection ID
* `false` if failure
*/
public static function setNewConnection($cookie)
{
if (isset($_SERVER['HTTP_USER_AGENT'])
&& preg_match('/BotLink|ahoy|AlkalineBOT|anthill|appie|arale|araneo|AraybOt|ariadne|arks|ATN_Worldwide|Atomz|bbot|Bjaaland|Ukonline|borg\-bot\/0\.9|boxseabot|bspider|calif|christcrawler|CMC\/0\.01|combine|confuzzledbot|CoolBot|cosmos|Internet Cruiser Robot|cusco|cyberspyder|cydralspider|desertrealm, desert realm|digger|DIIbot|grabber|downloadexpress|DragonBot|dwcp|ecollector|ebiness|elfinbot|esculapio|esther|fastcrawler|FDSE|FELIX IDE|ESI|fido|H<>m<EFBFBD>h<EFBFBD>kki|KIT\-Fireball|fouineur|Freecrawl|gammaSpider|gazz|gcreep|golem|googlebot|griffon|Gromit|gulliver|gulper|hambot|havIndex|hotwired|htdig|iajabot|INGRID\/0\.1|Informant|InfoSpiders|inspectorwww|irobot|Iron33|JBot|jcrawler|Teoma|Jeeves|jobo|image\.kapsi\.net|KDD\-Explorer|ko_yappo_robot|label\-grabber|larbin|legs|Linkidator|linkwalker|Lockon|logo_gif_crawler|marvin|mattie|mediafox|MerzScope|NEC\-MeshExplorer|MindCrawler|udmsearch|moget|Motor|msnbot|muncher|muninn|MuscatFerret|MwdSearch|sharp\-info\-agent|WebMechanic|NetScoop|newscan\-online|ObjectsSearch|Occam|Orbsearch\/1\.0|packrat|pageboy|ParaSite|patric|pegasus|perlcrawler|phpdig|piltdownman|Pimptrain|pjspider|PlumtreeWebAccessor|PortalBSpider|psbot|Getterrobo\-Plus|Raven|RHCS|RixBot|roadrunner|Robbie|robi|RoboCrawl|robofox|Scooter|Search\-AU|searchprocess|Senrigan|Shagseeker|sift|SimBot|Site Valet|skymob|SLCrawler\/2\.0|slurp|ESI|snooper|solbot|speedy|spider_monkey|SpiderBot\/1\.0|spiderline|nil|suke|http:\/\/www\.sygol\.com|tach_bw|TechBOT|templeton|titin|topiclink|UdmSearch|urlck|Valkyrie libwww\-perl|verticrawl|Victoria|void\-bot|Voyager|VWbot_K|crawlpaper|wapspider|WebBandit\/1\.0|webcatcher|T\-H\-U\-N\-D\-E\-R\-S\-T\-O\-N\-E|WebMoose|webquest|webreaper|webs|webspider|WebWalker|wget|winona|whowhere|wlm|WOLP|WWWC|none|XGET|Nederland\.zoek|AISearchBot|woriobot|NetSeer|Nutch|YandexBot/i', $_SERVER['HTTP_USER_AGENT'])) {
// This is a bot and we have to retrieve its connection ID
$sql = 'SELECT SQL_NO_CACHE `id_connections` FROM `' . _DB_PREFIX_ . 'connections`
WHERE ip_address = ' . (int) ip2long(Tools::getRemoteAddr()) . '
AND `date_add` > \'' . pSQL(date('Y-m-d H:i:00', time() - 1800)) . '\'
' . Shop::addSqlRestriction(Shop::SHARE_CUSTOMER) . '
ORDER BY `date_add` DESC';
if ($idConnections = Db::getInstance()->getValue($sql, false)) {
$cookie->id_connections = (int) $idConnections;
return Page::getCurrentId();
}
}
// A new connection is created if the guest made no actions during 30 minutes
$sql = 'SELECT SQL_NO_CACHE `id_guest`
FROM `' . _DB_PREFIX_ . 'connections`
WHERE `id_guest` = ' . (int) $cookie->id_guest . '
AND `date_add` > \'' . pSQL(date('Y-m-d H:i:00', time() - 1800)) . '\'
' . Shop::addSqlRestriction(Shop::SHARE_CUSTOMER) . '
ORDER BY `date_add` DESC';
$result = Db::getInstance()->getRow($sql, false);
if (!$result['id_guest'] && (int) $cookie->id_guest) {
// The old connections details are removed from the database in order to spare some memory
Connection::cleanConnectionsPages();
$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
$arrayUrl = parse_url($referer);
if (!isset($arrayUrl['host']) || preg_replace('/^www./', '', $arrayUrl['host']) == preg_replace('/^www./', '', Tools::getHttpHost(false, false))) {
$referer = '';
}
$connection = new Connection();
$connection->id_guest = (int) $cookie->id_guest;
$connection->id_page = Page::getCurrentId();
$connection->ip_address = Tools::getRemoteAddr() ? (int) ip2long(Tools::getRemoteAddr()) : '';
$connection->id_shop = Context::getContext()->shop->id;
$connection->id_shop_group = Context::getContext()->shop->id_shop_group;
$connection->date_add = $cookie->date_add;
if (Validate::isAbsoluteUrl($referer)) {
$connection->http_referer = substr($referer, 0, 254);
}
$connection->add();
$cookie->id_connections = $connection->id;
return $connection->id_page;
}
return false;
}
/**
* @param int $idConnections
* @param int $idPage
* @param string $timeStart
* @param int $time
*/
public static function setPageTime($idConnections, $idPage, $timeStart, $time)
{
if (!Validate::isUnsignedId($idConnections)
|| !Validate::isUnsignedId($idPage)
|| !Validate::isDate($timeStart)) {
return;
}
// Limited to 5 minutes because more than 5 minutes is considered as an error
if ($time > 300000) {
$time = 300000;
}
Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'connections_page`
SET `time_end` = `time_start` + INTERVAL ' . (int) ($time / 1000) . ' SECOND
WHERE `id_connections` = ' . (int) $idConnections . '
AND `id_page` = ' . (int) $idPage . '
AND `time_start` = \'' . pSQL($timeStart) . '\'');
}
/**
* Clean connections page.
*/
public static function cleanConnectionsPages()
{
$period = Configuration::get('PS_STATS_OLD_CONNECT_AUTO_CLEAN');
if ($period === 'week') {
$interval = '1 WEEK';
} elseif ($period === 'month') {
$interval = '1 MONTH';
} elseif ($period === 'year') {
$interval = '1 YEAR';
} else {
return;
}
if ($interval != null) {
// Records of connections details older than the beginning of the specified interval are deleted
Db::getInstance()->execute('
DELETE FROM `' . _DB_PREFIX_ . 'connections_page`
WHERE time_start < LAST_DAY(DATE_SUB(NOW(), INTERVAL ' . $interval . '))');
}
}
}

View File

@@ -0,0 +1,147 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ConnectionsSourceCore.
*/
class ConnectionsSourceCore extends ObjectModel
{
public $id_connections;
public $http_referer;
public $request_uri;
public $keywords;
public $date_add;
public static $uri_max_size = 255;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'connections_source',
'primary' => 'id_connections_source',
'fields' => array(
'id_connections' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'http_referer' => array('type' => self::TYPE_STRING, 'validate' => 'isAbsoluteUrl'),
'request_uri' => array('type' => self::TYPE_STRING, 'validate' => 'isUrl'),
'keywords' => array('type' => self::TYPE_STRING, 'validate' => 'isMessage'),
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate', 'required' => true),
),
);
/**
* Adds current ConnectionsSource 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 ConnectionsSource has been successfully added
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function add($autoDate = true, $nullValues = false)
{
if ($result = parent::add($autoDate, $nullValues)) {
Referrer::cacheNewSource($this->id);
}
return $result;
}
public static function logHttpReferer(Cookie $cookie = null)
{
if (!$cookie) {
$cookie = Context::getContext()->cookie;
}
if (!isset($cookie->id_connections) || !Validate::isUnsignedId($cookie->id_connections)) {
return false;
}
// If the referrer is not correct, we drop the connection
if (isset($_SERVER['HTTP_REFERER']) && !Validate::isAbsoluteUrl($_SERVER['HTTP_REFERER'])) {
return false;
}
// If there is no referrer and we do not want to save direct traffic (as opposed to referral traffic), we drop the connection
if (!isset($_SERVER['HTTP_REFERER']) && !Configuration::get('TRACKING_DIRECT_TRAFFIC')) {
return false;
}
$source = new ConnectionsSource();
// There are a few more operations if there is a referrer
if (isset($_SERVER['HTTP_REFERER'])) {
// If the referrer is internal (i.e. from your own website), then we drop the connection
$parsed = parse_url($_SERVER['HTTP_REFERER']);
$parsedHost = parse_url(Tools::getProtocol() . Tools::getHttpHost(false, false) . __PS_BASE_URI__);
if (!isset($parsed['host']) || (!isset($parsed['path']) || !isset($parsedHost['path']))) {
return false;
}
if ((preg_replace('/^www./', '', $parsed['host']) == preg_replace('/^www./', '', Tools::getHttpHost(false, false))) && !strncmp($parsed['path'], $parsedHost['path'], strlen(__PS_BASE_URI__))) {
return false;
}
$source->http_referer = substr($_SERVER['HTTP_REFERER'], 0, ConnectionsSource::$uri_max_size);
$source->keywords = substr(trim(SearchEngine::getKeywords($_SERVER['HTTP_REFERER'])), 0, ConnectionsSource::$uri_max_size);
}
$source->id_connections = (int) $cookie->id_connections;
$source->request_uri = Tools::getHttpHost(false, false);
if (isset($_SERVER['REQUEST_URI'])) {
$source->request_uri .= $_SERVER['REQUEST_URI'];
} elseif (isset($_SERVER['REDIRECT_URL'])) {
$source->request_uri .= $_SERVER['REDIRECT_URL'];
}
if (!Validate::isUrl($source->request_uri)) {
$source->request_uri = '';
}
$source->request_uri = substr($source->request_uri, 0, ConnectionsSource::$uri_max_size);
return $source->add();
}
/**
* Get Order sources.
*
* @param int $idOrder Order ID
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public static function getOrderSources($idOrder)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT cos.http_referer, cos.request_uri, cos.keywords, cos.date_add
FROM ' . _DB_PREFIX_ . 'orders o
INNER JOIN ' . _DB_PREFIX_ . 'guest g ON g.id_customer = o.id_customer
INNER JOIN ' . _DB_PREFIX_ . 'connections co ON co.id_guest = g.id_guest
INNER JOIN ' . _DB_PREFIX_ . 'connections_source cos ON cos.id_connections = co.id_connections
WHERE id_order = ' . (int) ($idOrder) . '
ORDER BY cos.date_add DESC');
}
}

121
classes/Contact.php Normal file
View File

@@ -0,0 +1,121 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ContactCore.
*/
class ContactCore extends ObjectModel
{
public $id;
/** @var string Name */
public $name;
/** @var string e-mail */
public $email;
/** @var string Detailed description */
public $description;
public $customer_service;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'contact',
'primary' => 'id_contact',
'multilang' => true,
'fields' => array(
'email' => array(
'type' => self::TYPE_STRING,
'validate' => 'isEmail',
'size' => 255,
),
'customer_service' => array(
'type' => self::TYPE_BOOL,
'validate' => 'isBool',
),
/* Lang fields */
'name' => array(
'type' => self::TYPE_STRING,
'lang' => true,
'validate' => 'isGenericName',
'required' => true,
'size' => 255,
),
'description' => array(
'type' => self::TYPE_STRING,
'lang' => true,
'validate' => 'isCleanHtml',
),
),
);
/**
* Return available contacts.
*
* @param int $idLang Language ID
*
* @return array Contacts
*/
public static function getContacts($idLang)
{
$shopIds = Shop::getContextListShopID();
$sql = 'SELECT *
FROM `' . _DB_PREFIX_ . 'contact` c
' . Shop::addSqlAssociation('contact', 'c', false) . '
LEFT JOIN `' . _DB_PREFIX_ . 'contact_lang` cl ON (c.`id_contact` = cl.`id_contact`)
WHERE cl.`id_lang` = ' . (int) $idLang . '
AND contact_shop.`id_shop` IN (' . implode(', ', array_map('intval', $shopIds)) . ')
GROUP BY c.`id_contact`
ORDER BY `name` ASC';
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
}
/**
* Return available categories contacts.
*
* @return array Contacts
*/
public static function getCategoriesContacts()
{
$shopIds = Shop::getContextListShopID();
return Db::getInstance()->executeS('
SELECT cl.*
FROM ' . _DB_PREFIX_ . 'contact ct
' . Shop::addSqlAssociation('contact', 'ct', false) . '
LEFT JOIN ' . _DB_PREFIX_ . 'contact_lang cl
ON (cl.id_contact = ct.id_contact AND cl.id_lang = ' . (int) Context::getContext()->language->id . ')
WHERE ct.customer_service = 1
AND contact_shop.`id_shop` IN (' . implode(', ', array_map('intval', $shopIds)) . ')
GROUP BY ct.`id_contact`
');
}
}

435
classes/Context.php Normal file
View File

@@ -0,0 +1,435 @@
<?php
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
use PrestaShop\PrestaShop\Core\Localization\Locale;
use PrestaShopBundle\Translation\TranslatorComponent as Translator;
use PrestaShopBundle\Translation\TranslatorLanguageLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
/**
* Class ContextCore.
*
* @since 1.5.0.1
*/
class ContextCore
{
/** @var Context */
protected static $instance;
/** @var Cart */
public $cart;
/** @var Customer */
public $customer;
/** @var Cookie */
public $cookie;
/** @var Link */
public $link;
/** @var Country */
public $country;
/** @var Employee */
public $employee;
/** @var AdminController|FrontController */
public $controller;
/** @var string $override_controller_name_for_translations */
public $override_controller_name_for_translations;
/** @var Language */
public $language;
/** @var Currency */
public $currency;
/**
* Current locale instance.
*
* @var Locale
*/
public $currentLocale;
/** @var Tab */
public $tab;
/** @var Shop */
public $shop;
/** @var Smarty */
public $smarty;
/** @var \Mobile_Detect */
public $mobile_detect;
/** @var int */
public $mode;
/** @var ContainerBuilder */
public $container;
/** @var Translator */
protected $translator = null;
/**
* Mobile device of the customer.
*
* @var bool|null
*/
protected $mobile_device = null;
/** @var bool|null */
protected $is_mobile = null;
/** @var bool|null */
protected $is_tablet = null;
/** @var int */
const DEVICE_COMPUTER = 1;
/** @var int */
const DEVICE_TABLET = 2;
/** @var int */
const DEVICE_MOBILE = 4;
/** @var int */
const MODE_STD = 1;
/** @var int */
const MODE_STD_CONTRIB = 2;
/** @var int */
const MODE_HOST_CONTRIB = 4;
/** @var int */
const MODE_HOST = 8;
/**
* Sets Mobile_Detect tool object.
*
* @return Mobile_Detect
*/
public function getMobileDetect()
{
if ($this->mobile_detect === null) {
$this->mobile_detect = new Mobile_Detect();
}
return $this->mobile_detect;
}
/**
* Checks if visitor's device is a mobile device.
*
* @return bool
*/
public function isMobile()
{
if ($this->is_mobile === null) {
$mobileDetect = $this->getMobileDetect();
$this->is_mobile = $mobileDetect->isMobile();
}
return $this->is_mobile;
}
/**
* Checks if visitor's device is a tablet device.
*
* @return bool
*/
public function isTablet()
{
if ($this->is_tablet === null) {
$mobileDetect = $this->getMobileDetect();
$this->is_tablet = $mobileDetect->isTablet();
}
return $this->is_tablet;
}
/**
* Sets mobile_device context variable.
*
* @return bool
*/
public function getMobileDevice()
{
if ($this->mobile_device === null) {
$this->mobile_device = false;
if ($this->checkMobileContext()) {
if (isset(Context::getContext()->cookie->no_mobile) && Context::getContext()->cookie->no_mobile == false && (int) Configuration::get('PS_ALLOW_MOBILE_DEVICE') != 0) {
$this->mobile_device = true;
} else {
switch ((int) Configuration::get('PS_ALLOW_MOBILE_DEVICE')) {
case 1: // Only for mobile device
if ($this->isMobile() && !$this->isTablet()) {
$this->mobile_device = true;
}
break;
case 2: // Only for touchpads
if ($this->isTablet() && !$this->isMobile()) {
$this->mobile_device = true;
}
break;
case 3: // For touchpad or mobile devices
if ($this->isMobile() || $this->isTablet()) {
$this->mobile_device = true;
}
break;
}
}
}
}
return $this->mobile_device;
}
/**
* Returns mobile device type.
*
* @return int
*/
public function getDevice()
{
static $device = null;
if ($device === null) {
if ($this->isTablet()) {
$device = Context::DEVICE_TABLET;
} elseif ($this->isMobile()) {
$device = Context::DEVICE_MOBILE;
} else {
$device = Context::DEVICE_COMPUTER;
}
}
return $device;
}
/**
* @return Locale
*/
public function getCurrentLocale()
{
return $this->currentLocale;
}
/**
* Checks if mobile context is possible.
*
* @return bool
*
* @throws PrestaShopException
*/
protected function checkMobileContext()
{
// Check mobile context
if (Tools::isSubmit('no_mobile_theme')) {
Context::getContext()->cookie->no_mobile = true;
if (Context::getContext()->cookie->id_guest) {
$guest = new Guest(Context::getContext()->cookie->id_guest);
$guest->mobile_theme = false;
$guest->update();
}
} elseif (Tools::isSubmit('mobile_theme_ok')) {
Context::getContext()->cookie->no_mobile = false;
if (Context::getContext()->cookie->id_guest) {
$guest = new Guest(Context::getContext()->cookie->id_guest);
$guest->mobile_theme = true;
$guest->update();
}
}
return isset($_SERVER['HTTP_USER_AGENT'], Context::getContext()->cookie)
&& (bool) Configuration::get('PS_ALLOW_MOBILE_DEVICE')
&& @filemtime(_PS_THEME_MOBILE_DIR_)
&& !Context::getContext()->cookie->no_mobile;
}
/**
* Get a singleton instance of Context object.
*
* @return Context
*/
public static function getContext()
{
if (!isset(self::$instance)) {
self::$instance = new Context();
}
return self::$instance;
}
/**
* @param $testInstance Context
* Unit testing purpose only
*/
public static function setInstanceForTesting($testInstance)
{
self::$instance = $testInstance;
}
/**
* Unit testing purpose only.
*/
public static function deleteTestingInstance()
{
self::$instance = null;
}
/**
* Clone current context object.
*
* @return Context
*/
public function cloneContext()
{
return clone $this;
}
/**
* Update context after customer login.
*
* @param Customer $customer Created customer
*/
public function updateCustomer(Customer $customer)
{
$this->customer = $customer;
$this->cookie->id_customer = (int) $customer->id;
$this->cookie->customer_lastname = $customer->lastname;
$this->cookie->customer_firstname = $customer->firstname;
$this->cookie->passwd = $customer->passwd;
$this->cookie->logged = 1;
$customer->logged = 1;
$this->cookie->email = $customer->email;
$this->cookie->is_guest = $customer->isGuest();
$this->cart->secure_key = $customer->secure_key;
if (Configuration::get('PS_CART_FOLLOWING') && (empty($this->cookie->id_cart) || Cart::getNbProducts($this->cookie->id_cart) == 0) && $idCart = (int) Cart::lastNoneOrderedCart($this->customer->id)) {
$this->cart = new Cart($idCart);
} else {
$idCarrier = (int) $this->cart->id_carrier;
$this->cart->id_carrier = 0;
$this->cart->setDeliveryOption(null);
$this->cart->updateAddressId($this->cart->id_address_delivery, (int) Address::getFirstCustomerAddressId((int) ($customer->id)));
$this->cart->id_address_delivery = (int) Address::getFirstCustomerAddressId((int) ($customer->id));
$this->cart->id_address_invoice = (int) Address::getFirstCustomerAddressId((int) ($customer->id));
}
$this->cart->id_customer = (int) $customer->id;
if (isset($idCarrier) && $idCarrier) {
$deliveryOption = [$this->cart->id_address_delivery => $idCarrier . ','];
$this->cart->setDeliveryOption($deliveryOption);
}
$this->cart->save();
$this->cookie->id_cart = (int) $this->cart->id;
$this->cookie->write();
$this->cart->autosetProductAddress();
$this->cookie->registerSession(new CustomerSession());
}
/**
* Returns a translator depending on service container availability and if the method
* is called by the installer or not.
*
* @param bool $isInstaller Set to true if the method is called by the installer
*
* @return Translator
*/
public function getTranslator($isInstaller = false)
{
if (null !== $this->translator) {
return $this->translator;
}
$sfContainer = SymfonyContainer::getInstance();
if ($isInstaller || null === $sfContainer) {
// symfony's container isn't available in front office, so we load and configure the translator component
$this->translator = $this->getTranslatorFromLocale($this->language->locale);
} else {
$this->translator = $sfContainer->get('translator');
// We need to set the locale here because in legacy BO pages, the translator is used
// before the TranslatorListener does its job of setting the locale according to the Request object
$this->translator->setLocale($this->language->locale);
}
return $this->translator;
}
/**
* Returns a new instance of Translator for the provided locale code.
*
* @param string $locale IETF language tag (eg. "en-US")
*
* @return Translator
*/
public function getTranslatorFromLocale($locale)
{
$cacheDir = _PS_CACHE_DIR_ . 'translations';
$translator = new Translator($locale, null, $cacheDir, false);
// In case we have at least 1 translated message, we return the current translator.
// If some translations are missing, clear cache
if ($locale === '' || null === $locale || count($translator->getCatalogue($locale)->all())) {
return $translator;
}
// However, in some case, even empty catalog were stored in the cache and then used as-is.
// For this one, we drop the cache and try to regenerate it.
if (is_dir($cacheDir)) {
$cache_file = Finder::create()
->files()
->in($cacheDir)
->depth('==0')
->name('*.' . $locale . '.*');
(new Filesystem())->remove($cache_file);
}
$translator->clearLanguage($locale);
$adminContext = defined('_PS_ADMIN_DIR_');
// Do not load DB translations when $this->language is PrestashopBundle\Install\Language
// because it means that we're looking for the installer translations, so we're not yet connected to the DB
$withDB = !$this->language instanceof PrestashopBundle\Install\Language;
$theme = $this->shop !== null ? $this->shop->theme : null;
(new TranslatorLanguageLoader($adminContext))->loadLanguage($translator, $locale, $withDB, $theme);
return $translator;
}
}

546
classes/Cookie.php Normal file
View File

@@ -0,0 +1,546 @@
<?php
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use Defuse\Crypto\Key;
use PrestaShop\PrestaShop\Core\Exception\CoreException;
use PrestaShop\PrestaShop\Core\Session\SessionInterface;
class CookieCore
{
/** @var array Contain cookie content in a key => value format */
protected $_content = array();
/** @var array Crypted cookie name for setcookie() */
protected $_name;
/** @var array expiration date for setcookie() */
protected $_expire;
/** @var array Website domain for setcookie() */
protected $_domain;
/** @var array Path for setcookie() */
protected $_path;
/** @var array cipher tool instance */
protected $cipherTool;
protected $_modified = false;
protected $_allow_writing;
protected $_salt;
protected $_standalone;
protected $_secure = false;
/**
* Get data if the cookie exists and else initialize an new one.
*
* @param $name string Cookie name before encrypting
* @param $path string
*/
public function __construct($name, $path = '', $expire = null, $shared_urls = null, $standalone = false, $secure = false)
{
$this->_content = array();
$this->_standalone = $standalone;
$this->_expire = null === $expire ? time() + 1728000 : (int) $expire;
$this->_path = trim(($this->_standalone ? '' : Context::getContext()->shop->physical_uri) . $path, '/\\') . '/';
if ($this->_path[0] != '/') {
$this->_path = '/' . $this->_path;
}
$this->_path = rawurlencode($this->_path);
$this->_path = str_replace('%2F', '/', $this->_path);
$this->_path = str_replace('%7E', '~', $this->_path);
$this->_domain = $this->getDomain($shared_urls);
$this->_name = 'PrestaShop-' . md5(($this->_standalone ? '' : _PS_VERSION_) . $name . $this->_domain);
$this->_allow_writing = true;
$this->_salt = $this->_standalone ? str_pad('', 8, md5('ps' . __FILE__)) : _COOKIE_IV_;
if ($this->_standalone) {
$asciiSafeString = \Defuse\Crypto\Encoding::saveBytesToChecksummedAsciiSafeString(Key::KEY_CURRENT_VERSION, str_pad($name, Key::KEY_BYTE_SIZE, __FILE__));
$this->cipherTool = new PhpEncryption($asciiSafeString);
} else {
$this->cipherTool = new PhpEncryption(_NEW_COOKIE_KEY_);
}
$this->_secure = (bool) $secure;
$this->update();
}
public function disallowWriting()
{
$this->_allow_writing = false;
}
protected function getDomain($shared_urls = null)
{
$r = '!(?:(\w+)://)?(?:(\w+)\:(\w+)@)?([^/:]+)?(?:\:(\d*))?([^#?]+)?(?:\?([^#]+))?(?:#(.+$))?!i';
if (!preg_match($r, Tools::getHttpHost(false, false), $out) || !isset($out[4])) {
return false;
}
if (preg_match('/^(((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]{1}[0-9]|[1-9]).)' .
'{1}((25[0-5]|2[0-4][0-9]|[1]{1}[0-9]{2}|[1-9]{1}[0-9]|[0-9]).)' .
'{2}((25[0-5]|2[0-4][0-9]|[1]{1}[0-9]{2}|[1-9]{1}[0-9]|[0-9]){1}))$/', $out[4])) {
return false;
}
if (!strstr(Tools::getHttpHost(false, false), '.')) {
return false;
}
$domain = false;
if ($shared_urls !== null) {
foreach ($shared_urls as $shared_url) {
if ($shared_url != $out[4]) {
continue;
}
if (preg_match('/^(?:.*\.)?([^.]*(?:.{2,4})?\..{2,3})$/Ui', $shared_url, $res)) {
$domain = '.' . $res[1];
break;
}
}
}
if (!$domain) {
$domain = $out[4];
}
return $domain;
}
/**
* Set expiration date.
*
* @param int $expire Expiration time from now
*/
public function setExpire($expire)
{
$this->_expire = (int) ($expire);
}
/**
* Magic method wich return cookie data from _content array.
*
* @param string $key key wanted
*
* @return string value corresponding to the key
*/
public function __get($key)
{
return isset($this->_content[$key]) ? $this->_content[$key] : false;
}
/**
* Magic method which check if key exists in the cookie.
*
* @param string $key key wanted
*
* @return bool key existence
*/
public function __isset($key)
{
return isset($this->_content[$key]);
}
/**
* Magic method which adds data into _content array.
*
* @param string $key Access key for the value
* @param mixed $value Value corresponding to the key
*
* @throws Exception
*/
public function __set($key, $value)
{
if (is_array($value)) {
die(Tools::displayError());
}
if (preg_match('/¤|\|/', $key . $value)) {
throw new Exception('Forbidden chars in cookie');
}
if (!$this->_modified && (!array_key_exists($key, $this->_content) || $this->_content[$key] != $value)) {
$this->_modified = true;
}
$this->_content[$key] = $value;
}
/**
* Magic method which delete data into _content array.
*
* @param string $key key wanted
*/
public function __unset($key)
{
if (isset($this->_content[$key])) {
$this->_modified = true;
}
unset($this->_content[$key]);
}
/**
* Check customer informations saved into cookie and return customer validity.
*
* @deprecated as of version 1.5 use Customer::isLogged() instead
*
* @return bool customer validity
*/
public function isLogged($withGuest = false)
{
Tools::displayAsDeprecated('Use Customer::isLogged() instead');
if (!$withGuest && $this->is_guest == 1) {
return false;
}
/* Customer is valid only if it can be load and if cookie password is the same as database one */
if ($this->logged == 1 && $this->id_customer && Validate::isUnsignedId($this->id_customer) && Customer::checkPassword((int) ($this->id_customer), $this->passwd)) {
return true;
}
return false;
}
/**
* Check employee informations saved into cookie and return employee validity.
*
* @deprecated as of version 1.5 use Employee::isLoggedBack() instead
*
* @return bool employee validity
*/
public function isLoggedBack()
{
Tools::displayAsDeprecated('Use Employee::isLoggedBack() instead');
/* Employee is valid only if it can be load and if cookie password is the same as database one */
return $this->id_employee
&& Validate::isUnsignedId($this->id_employee)
&& Employee::checkPassword((int) $this->id_employee, $this->passwd)
&& (!isset($this->_content['remote_addr']) || $this->_content['remote_addr'] == ip2long(Tools::getRemoteAddr()) || !Configuration::get('PS_COOKIE_CHECKIP'));
}
/**
* Delete cookie
* As of version 1.5 don't call this function, use Customer::logout() or Employee::logout() instead;.
*/
public function logout()
{
$this->deleteSession();
$this->_content = [];
$this->encryptAndSetCookie();
unset($_COOKIE[$this->_name]);
$this->_modified = true;
}
/**
* Soft logout, delete everything links to the customer
* but leave there affiliate's informations.
* As of version 1.5 don't call this function, use Customer::mylogout() instead;.
*/
public function mylogout()
{
unset(
$this->_content['id_customer'],
$this->_content['id_guest'],
$this->_content['is_guest'],
$this->_content['id_connections'],
$this->_content['customer_lastname'],
$this->_content['customer_firstname'],
$this->_content['passwd'],
$this->_content['logged'],
$this->_content['email'],
$this->_content['id_cart'],
$this->_content['id_address_invoice'],
$this->_content['id_address_delivery']
);
$this->_modified = true;
}
public function makeNewLog()
{
unset(
$this->_content['id_customer'],
$this->_content['id_guest']
);
Guest::setNewGuest($this);
$this->_modified = true;
}
/**
* Get cookie content.
*/
public function update($nullValues = false)
{
if (isset($_COOKIE[$this->_name])) {
/* Decrypt cookie content */
$content = $this->cipherTool->decrypt($_COOKIE[$this->_name]);
//printf("\$content = %s<br />", $content);
/* Get cookie checksum */
$tmpTab = explode('¤', $content);
array_pop($tmpTab);
$content_for_checksum = implode('¤', $tmpTab) . '¤';
$checksum = crc32($this->_salt . $content_for_checksum);
//printf("\$checksum = %s<br />", $checksum);
/* Unserialize cookie content */
$tmpTab = explode('¤', $content);
foreach ($tmpTab as $keyAndValue) {
$tmpTab2 = explode('|', $keyAndValue);
if (count($tmpTab2) == 2) {
$this->_content[$tmpTab2[0]] = $tmpTab2[1];
}
}
/* Check if cookie has not been modified */
if (!isset($this->_content['checksum']) || $this->_content['checksum'] != $checksum) {
$this->logout();
}
if (!isset($this->_content['date_add'])) {
$this->_content['date_add'] = date('Y-m-d H:i:s');
}
} else {
$this->_content['date_add'] = date('Y-m-d H:i:s');
}
//checks if the language exists, if not choose the default language
if (!$this->_standalone && !Language::getLanguage((int) $this->id_lang)) {
$this->id_lang = Configuration::get('PS_LANG_DEFAULT');
// set detect_language to force going through Tools::setCookieLanguage to figure out browser lang
$this->detect_language = true;
}
}
/**
* Encrypt and set the Cookie.
*
* @param string|null $cookie Cookie content
*
* @return bool Indicates whether the Cookie was successfully set
*
* @deprecated 1.7.0
*/
protected function _setcookie($cookie = null)
{
return $this->encryptAndSetCookie($cookie);
}
/**
* Encrypt and set the Cookie.
*
* @param string|null $cookie Cookie content
*
* @return bool Indicates whether the Cookie was successfully set
*
* @since 1.7.0
*/
protected function encryptAndSetCookie($cookie = null)
{
// Check if the content fits in the Cookie
$length = (ini_get('mbstring.func_overload') & 2) ? mb_strlen($cookie, ini_get('default_charset')) : strlen($cookie);
if ($length >= 1048576) {
return false;
}
if ($cookie) {
$content = $this->cipherTool->encrypt($cookie);
$time = $this->_expire;
} else {
$content = 0;
$time = 1;
}
return setcookie($this->_name, $content, $time, $this->_path, $this->_domain, $this->_secure, true);
}
public function __destruct()
{
$this->write();
}
/**
* Save cookie with setcookie().
*/
public function write()
{
if (!$this->_modified || headers_sent() || !$this->_allow_writing) {
return;
}
$cookie = '';
/* Serialize cookie content */
if (isset($this->_content['checksum'])) {
unset($this->_content['checksum']);
}
foreach ($this->_content as $key => $value) {
$cookie .= $key . '|' . $value . '¤';
}
/* Add checksum to cookie */
$cookie .= 'checksum|' . crc32($this->_salt . $cookie);
$this->_modified = false;
/* Cookies are encrypted for evident security reasons */
return $this->encryptAndSetCookie($cookie);
}
/**
* Get a family of variables (e.g. "filter_").
*/
public function getFamily($origin)
{
$result = array();
if (count($this->_content) == 0) {
return $result;
}
foreach ($this->_content as $key => $value) {
if (strncmp($key, $origin, strlen($origin)) == 0) {
$result[$key] = $value;
}
}
return $result;
}
public function unsetFamily($origin)
{
$family = $this->getFamily($origin);
foreach (array_keys($family) as $member) {
unset($this->$member);
}
}
public function getAll()
{
return $this->_content;
}
/**
* @return string name of cookie
*/
public function getName()
{
return $this->_name;
}
/**
* Check if the cookie exists.
*
* @since 1.5.0
*
* @return bool
*/
public function exists()
{
return isset($_COOKIE[$this->_name]);
}
/**
* Register a new session
*
* @param SessionInterface $session
*/
public function registerSession(SessionInterface $session)
{
if (isset($this->id_employee)) {
$session->setUserId((int) $this->id_employee);
} elseif (isset($this->id_customer)) {
$session->setUserId((int) $this->id_customer);
} else {
throw new CoreException('Invalid user id');
}
$session->setToken(sha1(time() . uniqid()));
$session->add();
$this->session_id = $session->getId();
$this->session_token = $session->getToken();
}
/**
* Delete session
*
* @return bool
*/
public function deleteSession()
{
if (!isset($this->session_id)) {
return false;
}
$session = $this->getSession($this->session_id);
if ($session !== null) {
$session->delete();
return true;
}
return false;
}
/**
* Check if this session is still alive
*
* @return bool
*/
public function isSessionAlive()
{
if (!isset($this->session_id, $this->session_token)) {
return false;
}
$session = $this->getSession($this->session_id);
return
$session !== null
&& $session->getToken() === $this->session_token
&& (
(int) $this->id_employee === $session->getUserId()
|| (int) $this->id_customer === $session->getUserId()
)
;
}
/**
* Retrieve session based on a session id and the employee or
* customer id
*
* @return SessionInterface|null
*/
public function getSession($sessionId)
{
if (isset($this->id_employee)) {
$session = new EmployeeSession($sessionId);
} elseif (isset($this->id_customer)) {
$session = new CustomerSession($sessionId);
}
if (!empty($session->getId())) {
return $session;
}
return null;
}
}

521
classes/Country.php Normal file
View File

@@ -0,0 +1,521 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class CountryCore.
*/
class CountryCore extends ObjectModel
{
public $id;
/** @var int Zone id which country belongs */
public $id_zone;
/** @var int Currency id which country belongs */
public $id_currency;
/** @var string 2 letters iso code */
public $iso_code;
/** @var int international call prefix */
public $call_prefix;
/** @var string Name */
public $name;
/** @var bool Contain states */
public $contains_states;
/** @var bool Need identification number dni/nif/nie */
public $need_identification_number;
/** @var bool Need Zip Code */
public $need_zip_code;
/** @var string Zip Code Format */
public $zip_code_format;
/** @var bool Display or not the tax incl./tax excl. mention in the front office */
public $display_tax_label = true;
/** @var bool Status for delivery */
public $active = true;
protected static $_idZones = array();
const GEOLOC_ALLOWED = 0;
const GEOLOC_CATALOG_MODE = 1;
const GEOLOC_FORBIDDEN = 2;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'country',
'primary' => 'id_country',
'multilang' => true,
'fields' => array(
'id_zone' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_currency' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'call_prefix' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
'iso_code' => array('type' => self::TYPE_STRING, 'validate' => 'isLanguageIsoCode', 'required' => true, 'size' => 3),
'active' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'contains_states' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true),
'need_identification_number' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true),
'need_zip_code' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'zip_code_format' => array('type' => self::TYPE_STRING, 'validate' => 'isZipCodeFormat'),
'display_tax_label' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true),
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 64),
),
'associations' => array(
'zone' => array('type' => self::HAS_ONE),
'currency' => array('type' => self::HAS_ONE),
),
);
protected static $cache_iso_by_id = array();
protected $webserviceParameters = array(
'objectsNodeName' => 'countries',
'fields' => array(
'id_zone' => array('xlink_resource' => 'zones'),
'id_currency' => array('xlink_resource' => 'currencies'),
),
);
/**
* Deletes current Country from the database.
*
* @return bool True if delete was successful
*
* @throws PrestaShopException
*/
public function delete()
{
if (!parent::delete()) {
return false;
}
return Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'cart_rule_country WHERE id_country = ' . (int) $this->id);
}
/**
* @brief Return available countries
*
* @param int $idLang Language ID
* @param bool $active return only active coutries
* @param bool $containStates return only country with states
* @param bool $listStates Include the states list with the returned list
*
* @return array Countries and corresponding zones
*/
public static function getCountries($idLang, $active = false, $containStates = false, $listStates = true)
{
$countries = array();
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT cl.*,c.*, cl.`name` country, z.`name` zone
FROM `' . _DB_PREFIX_ . 'country` c ' . Shop::addSqlAssociation('country', 'c') . '
LEFT JOIN `' . _DB_PREFIX_ . 'country_lang` cl ON (c.`id_country` = cl.`id_country` AND cl.`id_lang` = ' . (int) $idLang . ')
LEFT JOIN `' . _DB_PREFIX_ . 'zone` z ON (z.`id_zone` = c.`id_zone`)
WHERE 1' . ($active ? ' AND c.active = 1' : '') . ($containStates ? ' AND c.`contains_states` = ' . (int) $containStates : '') . '
ORDER BY cl.name ASC');
foreach ($result as $row) {
$countries[$row['id_country']] = $row;
}
if ($listStates) {
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'state` ORDER BY `name` ASC');
foreach ($result as $row) {
if (isset($countries[$row['id_country']]) && $row['active'] == 1) { /* Does not keep the state if its country has been disabled and not selected */
$countries[$row['id_country']]['states'][] = $row;
}
}
}
return $countries;
}
public static function getCountriesByIdShop($idShop, $idLang)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT *
FROM `' . _DB_PREFIX_ . 'country` c
LEFT JOIN `' . _DB_PREFIX_ . 'country_shop` cs ON (cs.`id_country`= c.`id_country`)
LEFT JOIN `' . _DB_PREFIX_ . 'country_lang` cl ON (c.`id_country` = cl.`id_country` AND cl.`id_lang` = ' . (int) $idLang . ')
WHERE `id_shop` = ' . (int) $idShop);
}
/**
* Get a country ID with its iso code.
*
* @param string $isoCode Country iso code
* @param bool $active return only active coutries
*
* @return int Country ID
*/
public static function getByIso($isoCode, $active = false)
{
if (!Validate::isLanguageIsoCode($isoCode)) {
die(Tools::displayError());
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(
'
SELECT `id_country`
FROM `' . _DB_PREFIX_ . 'country`
WHERE `iso_code` = \'' . pSQL(strtoupper($isoCode)) . '\''
. ($active ? ' AND active = 1' : '')
);
if (isset($result['id_country'])) {
return (int) $result['id_country'];
}
return false;
}
/**
* Get Zone ID by Country.
*
* @param int $idCountry Country ID
*
* @return bool|int
*/
public static function getIdZone($idCountry)
{
if (!Validate::isUnsignedId($idCountry)) {
die(Tools::displayError());
}
if (isset(self::$_idZones[$idCountry])) {
return (int) self::$_idZones[$idCountry];
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
SELECT `id_zone`
FROM `' . _DB_PREFIX_ . 'country`
WHERE `id_country` = ' . (int) $idCountry);
if (isset($result['id_zone'])) {
self::$_idZones[$idCountry] = (int) $result['id_zone'];
return (int) $result['id_zone'];
}
return false;
}
/**
* Get a country name with its ID.
*
* @param int $idLang Language ID
* @param int $idCountry Country ID
*
* @return string Country name
*/
public static function getNameById($idLang, $idCountry)
{
$key = 'country_getNameById_' . $idCountry . '_' . $idLang;
if (!Cache::isStored($key)) {
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
'
SELECT `name`
FROM `' . _DB_PREFIX_ . 'country_lang`
WHERE `id_lang` = ' . (int) $idLang . '
AND `id_country` = ' . (int) $idCountry
);
Cache::store($key, $result);
return $result;
}
return Cache::retrieve($key);
}
/**
* Get a country iso with its ID.
*
* @param int $idCountry Country ID
*
* @return string Country iso
*/
public static function getIsoById($idCountry)
{
if (!isset(Country::$cache_iso_by_id[$idCountry])) {
Country::$cache_iso_by_id[$idCountry] = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
SELECT `iso_code`
FROM `' . _DB_PREFIX_ . 'country`
WHERE `id_country` = ' . (int) $idCountry);
}
if (isset(Country::$cache_iso_by_id[$idCountry])) {
return Country::$cache_iso_by_id[$idCountry];
}
return false;
}
/**
* Get a country id with its name.
*
* @param int|null $idLang Language ID
* @param string $country Country Name
*
* @return int Country ID
*/
public static function getIdByName($idLang, $country)
{
$sql = '
SELECT `id_country`
FROM `' . _DB_PREFIX_ . 'country_lang`
WHERE `name` = \'' . pSQL($country) . '\'';
if ($idLang) {
$sql .= ' AND `id_lang` = ' . (int) $idLang;
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql);
if (isset($result['id_country'])) {
return (int) $result['id_country'];
}
return false;
}
/**
* Does the Country need a zip code?
*
* @param int $idCountry Country ID
*
* @return bool Indicates whether the Country needs a zip code
*/
public static function getNeedZipCode($idCountry)
{
if (!(int) $idCountry) {
return false;
}
return (bool) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
SELECT `need_zip_code`
FROM `' . _DB_PREFIX_ . 'country`
WHERE `id_country` = ' . (int) $idCountry);
}
/**
* Get zip code format for Country.
*
* @param int $idCountry Country ID
*
* @return bool|false|string|null
*/
public static function getZipCodeFormat($idCountry)
{
if (!(int) $idCountry) {
return false;
}
$zipCodeFormat = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
SELECT `zip_code_format`
FROM `' . _DB_PREFIX_ . 'country`
WHERE `id_country` = ' . (int) $idCountry);
if (isset($zipCodeFormat) && $zipCodeFormat) {
return $zipCodeFormat;
}
return false;
}
/**
* Get Countries by Zone ID.
*
* @param int $idZone Zone ID
* @param int $idLang Language ID
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public static function getCountriesByZoneId($idZone, $idLang)
{
if (empty($idZone) || empty($idLang)) {
die(Tools::displayError());
}
$sql = ' SELECT DISTINCT c.*, cl.*
FROM `' . _DB_PREFIX_ . 'country` c
' . Shop::addSqlAssociation('country', 'c', false) . '
LEFT JOIN `' . _DB_PREFIX_ . 'state` s ON (s.`id_country` = c.`id_country`)
LEFT JOIN `' . _DB_PREFIX_ . 'country_lang` cl ON (c.`id_country` = cl.`id_country`)
WHERE (c.`id_zone` = ' . (int) $idZone . ' OR s.`id_zone` = ' . (int) $idZone . ')
AND `id_lang` = ' . (int) $idLang;
return Db::getInstance()->executeS($sql);
}
/**
* Does the Country need a DNI.
*
* @return bool Indicates whether the Country needs a DNI
*/
public function isNeedDni()
{
return Country::isNeedDniByCountryId($this->id);
}
/**
* Does the given Country need a DNI?
*
* @param int $idCountry Country ID
*
* @return bool Indicates whether the Country needs a DNI
*/
public static function isNeedDniByCountryId($idCountry)
{
return (bool) Db::getInstance()->getValue('
SELECT `need_identification_number`
FROM `' . _DB_PREFIX_ . 'country`
WHERE `id_country` = ' . (int) $idCountry);
}
/**
* Does the given Country contain States?
*
* @param int $idCountry Country ID
*
* @return bool Indicates whether the Country contains States
*/
public static function containsStates($idCountry)
{
return (bool) Db::getInstance()->getValue('
SELECT `contains_states`
FROM `' . _DB_PREFIX_ . 'country`
WHERE `id_country` = ' . (int) $idCountry);
}
/**
* Apply Zone to selected Countries.
*
* @param array $idsCountries Country array
* @param int $idZone Zone ID
*
* @return bool Indicates whether the Zone was successfully applied
*/
public function affectZoneToSelection($idsCountries, $idZone)
{
// cast every array values to int (security)
$idsCountries = array_map('intval', $idsCountries);
return Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'country` SET `id_zone` = ' . (int) $idZone . ' WHERE `id_country` IN (' . implode(',', $idsCountries) . ')
');
}
/**
* Replace letters of zip code format And check this format on the zip code.
*
* @param string $zipCode zip code
*
* @return bool Indicates whether the zip code is correct
*/
public function checkZipCode($zipCode)
{
if (empty($this->zip_code_format)) {
return true;
}
$zipRegexp = '/^' . $this->zip_code_format . '$/ui';
$zipRegexp = str_replace('N', '[0-9]', $zipRegexp);
$zipRegexp = str_replace('L', '[a-zA-Z]', $zipRegexp);
$zipRegexp = str_replace('C', $this->iso_code, $zipRegexp);
return (bool) preg_match($zipRegexp, $zipCode);
}
/**
* Add module restrictions.
*
* @param array $shops Shops array
* @param array $countries Countries array
* @param array $modules Modules array
*
* @return bool Indictes whether the restrictions were successfully applied
*/
public static function addModuleRestrictions(array $shops = array(), array $countries = array(), array $modules = array())
{
if (!count($shops)) {
$shops = Shop::getShops(true, null, true);
}
if (!count($countries)) {
if (null !== Context::getContext()->cookie) {
$id_lang = (int) Context::getContext()->cookie->id_lang;
} else {
$id_lang = (int) Context::getContext()->language->id;
}
$countries = Country::getCountries($id_lang);
}
if (!count($modules)) {
$modules = Module::getPaymentModules();
}
$sql = false;
foreach ($shops as $idShop) {
foreach ($countries as $country) {
foreach ($modules as $module) {
$sql .= '(' . (int) $module['id_module'] . ', ' . (int) $idShop . ', ' . (int) $country['id_country'] . '),';
}
}
}
if ($sql) {
$sql = 'INSERT IGNORE INTO `' . _DB_PREFIX_ . 'module_country` (`id_module`, `id_shop`, `id_country`) VALUES ' . rtrim($sql, ',');
return Db::getInstance()->execute($sql);
} else {
return true;
}
}
/**
* Adds current Country 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 Country has been successfully added
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function add($autoDate = true, $nullValues = false)
{
$return = parent::add($autoDate, $nullValues) && self::addModuleRestrictions(array(), array(array('id_country' => $this->id)), array());
return $return;
}
}

993
classes/Currency.php Normal file
View File

@@ -0,0 +1,993 @@
<?php
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Core\Localization\CLDR\LocaleRepository;
class CurrencyCore extends ObjectModel
{
public $id;
/**
* Name of the currency.
*
* @var string
*/
public $name;
/**
* Localized names of the currency
*
* @var string[]
*/
protected $localizedNames;
/**
* Alphabetic ISO 4217 code of this currency.
*
* @var string
*/
public $iso_code;
/**
* Numeric ISO 4217 code of this currency
*
* @var string
*/
public $iso_code_num;
/**
* Numeric ISO 4217 code of this currency.
*
* @var string
*/
public $numeric_iso_code;
/**
* Exchange rate from default currency.
*
* @var float
*/
public $conversion_rate;
/**
* Is this currency deleted ?
* If currency is deleted, it stays in database. This is just a state (soft delete).
*
* @var bool
*/
public $deleted = 0;
/**
* Is this currency active ?
*
* @var int|bool active
*/
public $active;
/**
* Currency's symbol
*
* @var string
*/
public $sign;
/**
* Currency's symbol.
*
* @var string
*/
public $symbol;
/**
* Localized Currency's symbol.
*
* @var string[]
*/
private $localizedSymbols;
/**
* CLDR price formatting pattern
* e.g.: In french (fr-FR), price formatting pattern is : #,##0.00 ¤.
*
* @var string
*/
public $format;
/**
* @var int
*/
public $blank;
/**
* Use decimals when displaying a price in this currency
*
* @deprecated since 1.7.0
*
* @var int
*/
public $decimals;
/**
* Number of decimal digits to use when displaying a price in this currency.
*
* @var int
*/
public $precision;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'currency',
'primary' => 'id_currency',
'multilang' => true,
'fields' => array(
'iso_code' => array('type' => self::TYPE_STRING, 'validate' => 'isLanguageIsoCode', 'required' => true, 'size' => 3),
'numeric_iso_code' => array('type' => self::TYPE_STRING, 'validate' => 'isNumericIsoCode', 'size' => 3),
'precision' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
'conversion_rate' => array('type' => self::TYPE_FLOAT, 'validate' => 'isUnsignedFloat', 'required' => true, 'shop' => true),
'deleted' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'active' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255),
'symbol' => array('type' => self::TYPE_STRING, 'lang' => true, 'size' => 255),
),
);
/** @var array Currency cache */
protected static $currencies = array();
protected static $countActiveCurrencies = array();
protected $webserviceParameters = array(
'objectsNodeName' => 'currencies',
'fields' => array(
'names' => array(
'getter' => 'getLocalizedNames',
'i18n' => true,
),
'name' => array(
'getter' => 'getName',
'modifier' => array(
'http_method' => WebserviceRequest::HTTP_POST | WebserviceRequest::HTTP_PUT,
'modifier' => 'setNameForWebservice',
),
),
'symbol' => array(
'getter' => 'getLocalizedSymbols',
),
'iso_code' => array(
'modifier' => array(
'http_method' => WebserviceRequest::HTTP_POST | WebserviceRequest::HTTP_PUT,
'modifier' => 'setIsoCodeForWebService',
),
),
),
);
/**
* contains the sign to display before price, according to its format.
*
* @var string
*/
public $prefix = null;
/**
* contains the sign to display after price, according to its format.
*
* @var string
*/
public $suffix = null;
/**
* CurrencyCore constructor.
*
* @param null $id
* @param false|null $idLang if null or false, default language will be used
* @param null $idShop
*/
public function __construct($id = null, $idLang = null, $idShop = null)
{
parent::__construct($id, $idLang, $idShop);
if ($this->iso_code) {
// As the CLDR used to return a string even if in multi shop / lang,
// We force only one string to be returned
if (empty($idLang)) {
$idLang = Context::getContext()->language->id;
}
if (is_array($this->symbol)) {
$this->localizedSymbols = $this->symbol;
$this->sign = $this->symbol = $this->symbol[$idLang];
} else {
$this->localizedSymbols = [$idLang => $this->symbol];
$this->sign = $this->symbol;
}
if (is_array($this->name)) {
$this->localizedNames = $this->name;
$this->name = Tools::ucfirst($this->name[$idLang]);
} else {
$this->localizedNames = [$idLang => $this->name];
$this->name = Tools::ucfirst($this->name);
}
$this->iso_code_num = $this->numeric_iso_code;
$this->blank = 1;
$this->decimals = 1;
}
if (!$this->conversion_rate) {
$this->conversion_rate = 1;
}
}
public function getWebserviceParameters($ws_params_attribute_name = null)
{
$parameters = parent::getWebserviceParameters($ws_params_attribute_name);
// name is an i18n field but is casted to single string in the constructor
// so we need to force the webservice to consider this field as non-i18n.
$parameters['fields']['name']['i18n'] = false;
$parameters['fields']['name']['required'] = true;
return $parameters;
}
/**
* If the field 'names' (localized names) is sent,
* it should be use instead of the field 'name' (non-localized).
* LocalizedNames is also updated to reflect the new information.
*/
public function setNameForWebservice()
{
if (!empty($this->names)) {
$this->name = $this->names;
if (is_array($this->names)) {
$this->localizedNames = $this->names;
} else {
foreach ($this->localizedNames as $lang => $name) {
$this->localizedNames[$lang] = $this->names;
}
}
} else {
foreach ($this->localizedNames as $lang => $localizedName) {
$this->localizedNames[$lang] = $this->name;
}
}
}
/**
* In 1.7.6, new fields have been introduced. To keep it backward compatible,
* we need to populate those fields with default values and they are all available
* using the ISO code through CLDR data.
*/
public function setIsoCodeForWebService()
{
$container = Context::getContext()->container;
/** @var LocaleRepository $localeCldr */
$localeCldr = $container->get('prestashop.core.localization.cldr.locale_repository');
/** @var Configuration $configuration */
$configuration = $container->get('prestashop.adapter.legacy.configuration');
$languages = Language::getIDs();
$defaultLanguage = new Language($configuration->get('PS_LANG_DEFAULT'));
$locale = $localeCldr->getLocale($defaultLanguage->getLocale());
$currency = $locale->getCurrency($this->iso_code);
if (!empty($currency)) {
if (empty($this->numeric_iso_code)) {
$this->numeric_iso_code = $currency->getNumericIsoCode();
$this->iso_code_num = $this->numeric_iso_code;
}
if (empty($this->precision)) {
$this->precision = (int) $currency->getDecimalDigits();
}
}
if (empty($this->symbol)) {
$name = $this->name;
$this->refreshLocalizedCurrencyData($languages, $localeCldr);
$this->name = $name;
}
if (is_array($this->symbol)) {
$this->localizedSymbols = $this->symbol;
} else {
foreach ($this->localizedSymbols as $lang => $symbol) {
$this->localizedSymbols[$lang] = $this->symbol;
}
}
}
/**
* reset static cache (eg unit testing purpose).
*/
public static function resetStaticCache()
{
static::$currencies = array();
static::$countActiveCurrencies = array();
}
/**
* Overriding check if currency rate is not empty and if currency with the same iso code already exists.
* If it's true, currency is not added.
*
* @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 Currency has been successfully added
*/
public function add($autoDate = true, $nullValues = false)
{
if ((float) $this->conversion_rate <= 0) {
return false;
}
return Currency::exists($this->iso_code) ? false : parent::add($autoDate, $nullValues);
}
/**
* Updates the current object in the database.
*
* @param bool $nullValues Whether we want to use NULL values instead of empty quotes values
*
* @return bool Indicates whether the CartRule has been successfully updated
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function update($nullValues = false)
{
if ((float) $this->conversion_rate <= 0) {
return false;
}
return parent::update($nullValues);
}
/**
* Check if a Currency already exists.
*
* @param int|string $isoCode int for iso code number string for iso code
* @param int $idShop Shop ID
*
* @return bool Indicates whether the Currency already exists
*/
public static function exists($isoCode, $idShop = 0)
{
$idCurrencyExists = Currency::getIdByIsoCode($isoCode, (int) $idShop);
return (bool) $idCurrencyExists;
}
/**
* Delete given Currencies.
*
* @param array $selection Currencies
*
* @return bool Indicates whether the selected Currencies have been succesfully deleted
*/
public function deleteSelection($selection)
{
if (!is_array($selection)) {
return false;
}
$res = array();
foreach ($selection as $id) {
$obj = new Currency((int) $id);
$res[$id] = $obj->delete();
}
foreach ($res as $value) {
if (!$value) {
return false;
}
}
return true;
}
/**
* Deletes current object from database.
*
* @return bool True if delete was successful
*
* @throws PrestaShopException
*/
public function delete()
{
if ($this->id == Configuration::get('PS_CURRENCY_DEFAULT')) {
$result = Db::getInstance()->getRow('SELECT `id_currency` FROM ' . _DB_PREFIX_ . 'currency WHERE `id_currency` != ' . (int) $this->id . ' AND `deleted` = 0');
if (!$result['id_currency']) {
return false;
}
Configuration::updateValue('PS_CURRENCY_DEFAULT', $result['id_currency']);
}
$this->deleted = 1;
// Remove currency restrictions
$res = Db::getInstance()->delete('module_currency', 'id_currency = ' . (int) $this->id);
return $res && $this->update();
}
/**
* Return formatted sign.
*
* @param string $side left or right
*
* @return string formated sign
*/
public function getSign($side = null)
{
return $this->sign;
}
/**
* Is this currency installed for a given shop ?
* (current shop by default).
*
* @param int|null $currencyId
* The currency to look for (
* @param int|null $shopId
* The given shop's id
*
* @return bool
* True if this currency is installed for the given shop
*/
public function isInstalled($currencyId = null, $shopId = null)
{
$currencyId = $currencyId ?: $this->id;
$shopId = $shopId ?: Context::getContext()->shop->id;
$sql = (new DbQuery())
->select('1')
->from('currency', 'c')
->innerJoin('currency_shop', 'cs', 'c.`id_currency` = cs.`id_currency`')
->where('c.`id_currency` = ' . (int) $currencyId)
->where('cs.`id_shop` = ' . (int) $shopId)
->where('c.`deleted` = 0')
->where('c.`active` = 1');
return (bool) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql);
}
/**
* Returns the name of the currency (using the translated name base on the id_lang
* provided on creation). This method is useful when $this->name contains an array
* but you still need to get its name as a string.
*
* @return string
*/
public function getName()
{
if (is_string($this->name)) {
return $this->name;
}
$id_lang = $this->id_lang;
if ($id_lang === null) {
$id_lang = Configuration::get('PS_LANG_DEFAULT');
}
return Tools::ucfirst($this->name[$id_lang]);
}
public function getSymbol()
{
if (is_string($this->symbol)) {
return $this->symbol;
}
$id_lang = $this->id_lang;
if (null === $id_lang) {
$id_lang = Configuration::get('PS_LANG_DEFAULT');
}
return Tools::ucfirst($this->symbol[$id_lang]);
}
/**
* Names indexed by language id
*
* @return string[]
*/
public function getLocalizedNames()
{
return $this->localizedNames;
}
/**
* Symbols indexed by language id
*
* @return string[]
*/
public function getLocalizedSymbols()
{
return $this->localizedSymbols;
}
/**
* Return available currencies.
*
* @param bool $object
* @param bool $active
* @param bool $groupBy
*
* @return array Currencies
*/
public static function getCurrencies($object = false, $active = true, $groupBy = false)
{
return static::addCldrDatasToCurrency(
static::findAll($active, $groupBy),
$object
);
}
/**
* Retrieve all currencies data from the database.
*
* @param bool $active If true only active are returned
* @param bool $groupBy Group by id_currency
* @param bool $currentShopOnly If true returns only currencies associated to current shop
*
* @return array Currency data from database
*
* @throws PrestaShopDatabaseException
*/
public static function findAll($active = true, $groupBy = false, $currentShopOnly = true)
{
$currencies = Db::getInstance()->executeS('
SELECT *
FROM `' . _DB_PREFIX_ . 'currency` c
' . ($currentShopOnly ? Shop::addSqlAssociation('currency', 'c') : '') . '
WHERE c.`deleted` = 0' .
($active ? ' AND c.`active` = 1' : '') .
($groupBy ? ' GROUP BY c.`id_currency`' : '') .
' ORDER BY `iso_code` ASC');
return $currencies;
}
/**
* Retrieve all currencies data from the database.
*
* @return array Currency data from database
*
* @throws PrestaShopDatabaseException
*/
public static function findAllInstalled()
{
$currencies = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'SELECT * FROM `' . _DB_PREFIX_ . 'currency` c ORDER BY `iso_code` ASC'
);
return $currencies;
}
public function getInstalledCurrencies($shopId = null)
{
$shopId = $shopId ?: Context::getContext()->shop->id;
$sql = (new DbQuery())
->select('c.*, cl.*')
->from('currency', 'c')
->innerJoin('currency_lang', 'cl', 'c.`id_currency` = cl.`id_currency`')
->innerJoin('currency_shop', 'cs', 'c.`id_currency` = cs.`id_currency`')
->where('cs.`id_shop` = ' . (int) $shopId)
->where('c.`deleted` = 0')
->where('c.`active` = 1');
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
}
/**
* Get Currencies by Shop ID.
*
* @param int $idShop Shop ID
*
* @return array|Currency
*/
public static function getCurrenciesByIdShop($idShop = 0)
{
$currencies = Db::getInstance()->executeS('
SELECT *
FROM `' . _DB_PREFIX_ . 'currency` c
LEFT JOIN `' . _DB_PREFIX_ . 'currency_shop` cs ON (cs.`id_currency` = c.`id_currency`)
' . ($idShop ? ' WHERE cs.`id_shop` = ' . (int) $idShop : '') . '
ORDER BY `iso_code` ASC');
return self::addCldrDatasToCurrency($currencies);
}
/**
* Add Cldr datas to result query or signe object/array.
*
* @param $currencies mixed object|array
* @param $isObject bool
*/
protected static function addCldrDatasToCurrency($currencies, $isObject = false)
{
if (is_array($currencies)) {
foreach ($currencies as $k => $c) {
$currencies[$k] = Currency::getCurrencyInstance($c['id_currency']);
if (!$isObject) {
$currencies[$k] = (array) $currencies[$k];
$currencies[$k]['id_currency'] = $currencies[$k]['id'];
}
}
} else {
$currencies = Currency::getCurrencyInstance($currencies['id_currency']);
if (!$isObject) {
$currencies = (array) $currencies;
$currencies['id_currency'] = $currencies['id'];
}
}
return $currencies;
}
public static function getPaymentCurrenciesSpecial($idModule, $idShop = null)
{
if (null === $idShop) {
$idShop = Context::getContext()->shop->id;
}
$sql = 'SELECT *
FROM ' . _DB_PREFIX_ . 'module_currency
WHERE id_module = ' . (int) $idModule . '
AND id_shop =' . (int) $idShop;
return Db::getInstance()->getRow($sql);
}
/**
* Get payment Currencies.
*
* @param int $idModule Module ID
* @param null $idShop Shop ID
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public static function getPaymentCurrencies($idModule, $idShop = null)
{
if (null === $idShop) {
$idShop = Context::getContext()->shop->id;
}
$sql = 'SELECT c.*
FROM `' . _DB_PREFIX_ . 'module_currency` mc
LEFT JOIN `' . _DB_PREFIX_ . 'currency` c ON c.`id_currency` = mc.`id_currency`
WHERE c.`deleted` = 0
AND mc.`id_module` = ' . (int) $idModule . '
AND c.`active` = 1
AND mc.id_shop = ' . (int) $idShop . '
ORDER BY c.`iso_code` ASC';
return Db::getInstance()->executeS($sql);
}
/**
* Check payment Currencies.
*
* @param int $idModule Module ID
* @param null $idShop Shop ID
*
* @return array|PDOStatement|resource|null
*/
public static function checkPaymentCurrencies($idModule, $idShop = null)
{
if (empty($idModule)) {
return array();
}
if (null === $idShop) {
$idShop = Context::getContext()->shop->id;
}
$sql = new DbQuery();
$sql->select('mc.*');
$sql->from('module_currency', 'mc');
$sql->where('mc.`id_module` = ' . (int) $idModule);
$sql->where('mc.`id_shop` = ' . (int) $idShop);
$currencies = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
return $currencies ? $currencies : array();
}
/**
* Get Currency.
*
* @param int $idCurrency Currency ID
*
* @return array|bool|object|null
*/
public static function getCurrency($idCurrency)
{
$sql = new DbQuery();
$sql->select('c.*');
$sql->from('currency', 'c');
$sql->where('`deleted` = 0');
$sql->where('`id_currency` = ' . (int) $idCurrency);
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql);
}
/**
* Get Currency ID by ISO code.
*
* @param string $isoCode ISO code
* @param int $idShop Shop ID
* @param bool $forceRefreshCache [default=false] Set to TRUE to forcefully refresh any currently cached results
*
* @return int Currency ID
*/
public static function getIdByIsoCode($isoCode, $idShop = 0, $forceRefreshCache = false)
{
$cacheId = 'Currency::getIdByIsoCode_' . pSQL($isoCode) . '-' . (int) $idShop;
if ($forceRefreshCache || !Cache::isStored($cacheId)) {
$query = Currency::getIdByQuery($idShop);
$query->where('iso_code = \'' . pSQL($isoCode) . '\'');
$result = (int) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query->build());
Cache::store($cacheId, $result);
return $result;
}
return Cache::retrieve($cacheId);
}
/**
* Get Currency ID query.
*
* @param int $idShop Shop ID
*
* @return DbQuery
*/
public static function getIdByQuery($idShop = 0)
{
$query = new DbQuery();
$query->select('c.id_currency');
$query->from('currency', 'c');
$query->where('deleted = 0');
if (Shop::isFeatureActive() && $idShop > 0) {
$query->leftJoin('currency_shop', 'cs', 'cs.id_currency = c.id_currency');
$query->where('id_shop = ' . (int) $idShop);
}
return $query;
}
/**
* Refresh the currency exchange rate
* The XML file define exchange rate for each from a default currency ($isoCodeSource).
*
* @param SimpleXMLElement $data XML content which contains all the exchange rates
* @param string $isoCodeSource The default currency used in the XML file
* @param Currency $defaultCurrency The default currency object
*/
public function refreshCurrency($data, $isoCodeSource, $defaultCurrency)
{
// fetch the exchange rate of the default currency
$exchangeRate = 1;
$tmp = $this->conversion_rate;
if ($defaultCurrency->iso_code != $isoCodeSource) {
foreach ($data->currency as $currency) {
if ($currency['iso_code'] == $defaultCurrency->iso_code) {
$exchangeRate = round((float) $currency['rate'], 6);
break;
}
}
}
if ($defaultCurrency->iso_code == $this->iso_code) {
$this->conversion_rate = 1;
} else {
if ($this->iso_code == $isoCodeSource) {
$rate = 1;
} else {
foreach ($data->currency as $obj) {
if ($this->iso_code == (string) ($obj['iso_code'])) {
$rate = (float) $obj['rate'];
break;
}
}
}
if (isset($rate)) {
$this->conversion_rate = round($rate / $exchangeRate, 6);
}
}
if ($tmp != $this->conversion_rate) {
$this->update();
}
}
/**
* Get default Currency.
*
* @return bool|Currency
*/
public static function getDefaultCurrency()
{
$idCurrency = (int) Configuration::get('PS_CURRENCY_DEFAULT');
if ($idCurrency == 0) {
return false;
}
return new Currency($idCurrency);
}
/**
* Refresh Currencies.
*
* @return string Error message
*/
public static function refreshCurrencies()
{
// Parse
if (!$feed = Tools::simplexml_load_file(_PS_CURRENCY_FEED_URL_)) {
return Context::getContext()->getTranslator()->trans('Cannot parse feed.', array(), 'Admin.Notifications.Error');
}
// Default feed currency (EUR)
$isoCodeSource = (string) ($feed->source['iso_code']);
if (!$defaultCurrency = Currency::getDefaultCurrency()) {
return Context::getContext()->getTranslator()->trans('No default currency', array(), 'Admin.Notifications.Error');
}
$currencies = Currency::getCurrencies(true, false, true);
foreach ($currencies as $currency) {
/** @var Currency $currency */
if ($currency->id != $defaultCurrency->id) {
$currency->refreshCurrency($feed->list, $isoCodeSource, $defaultCurrency);
}
}
return '';
}
/**
* Get Currency instance.
*
* @param int $id Currency ID
*
* @return Currency
*/
public static function getCurrencyInstance($id)
{
if (!isset(self::$currencies[$id])) {
self::$currencies[(int) ($id)] = new Currency($id);
}
return self::$currencies[(int) ($id)];
}
/**
* Get conversion rate.
*
* @return int|string
*
* @deprecated 1.7.2.0, use Currency::getConversionRate() instead
*/
public function getConversationRate()
{
Tools::displayAsDeprecated('Use Currency::getConversionRate() instead');
return $this->getConversionRate();
}
/**
* Get conversion rate.
*
* @return int|string
*/
public function getConversionRate()
{
return ($this->id != (int) Configuration::get('PS_CURRENCY_DEFAULT')) ? $this->conversion_rate : 1;
}
/**
* Count active Currencies.
*
* @param int|null $idShop Shop ID
*
* @return mixed Amount of active Currencies
* `false` if none found
*/
public static function countActiveCurrencies($idShop = null)
{
if ($idShop === null) {
$idShop = (int) Context::getContext()->shop->id;
}
if (!isset(self::$countActiveCurrencies[$idShop])) {
self::$countActiveCurrencies[$idShop] = Db::getInstance()->getValue('
SELECT COUNT(DISTINCT c.id_currency) FROM `' . _DB_PREFIX_ . 'currency` c
LEFT JOIN ' . _DB_PREFIX_ . 'currency_shop cs ON (cs.id_currency = c.id_currency AND cs.id_shop = ' . (int) $idShop . ')
WHERE c.`active` = 1
');
}
return self::$countActiveCurrencies[$idShop];
}
/**
* Is multi Currency activated?
*
* @param int|null $idShop Shop ID
*
* @return bool Indicates whether multi Currency is actived
*/
public static function isMultiCurrencyActivated($idShop = null)
{
return Currency::countActiveCurrencies($idShop) > 1;
}
/**
* This method aims to update localized data in currency from CLDR reference.
*
* @param array $languages
* @param LocaleRepository $localeRepoCLDR
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
* @throws \PrestaShop\PrestaShop\Core\Localization\Exception\LocalizationException
*/
public function refreshLocalizedCurrencyData(array $languages, LocaleRepository $localeRepoCLDR)
{
$symbolsByLang = $namesByLang = [];
foreach ($languages as $languageData) {
$language = new Language($languageData['id_lang']);
if (empty($language->locale)) {
// Language doesn't have locale we can't install this language
continue;
}
// CLDR locale give us the CLDR reference specification
$cldrLocale = $localeRepoCLDR->getLocale($language->locale);
// CLDR currency gives data from CLDR reference, for the given language
$cldrCurrency = $cldrLocale->getCurrency($this->iso_code);
if (empty($cldrCurrency)) {
// The currency may not be declared in the locale, eg with custom iso code
continue;
}
$symbol = (string) $cldrCurrency->getSymbol();
if (empty($symbol)) {
$symbol = $this->iso_code;
}
// symbol is localized
$namesByLang[$language->id] = $cldrCurrency->getDisplayName();
$symbolsByLang[$language->id] = $symbol;
}
$this->name = $namesByLang;
$this->symbol = $symbolsByLang;
}
}

77
classes/Curve.php Normal file
View File

@@ -0,0 +1,77 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class CurveCore
{
protected $values = array();
protected $label;
protected $type;
/** @prototype void public function setValues($values) */
public function setValues($values)
{
$this->values = $values;
}
public function getValues($time_mode = false)
{
ksort($this->values);
$string = '';
foreach ($this->values as $key => $value) {
$string .= '[' . addslashes((string) $key) . ($time_mode ? '000' : '') . ',' . (float) $value . '],';
}
return '{data:[' . rtrim($string, ',') . ']' . (!empty($this->label) ? ',label:"' . $this->label . '"' : '') . '' . (!empty($this->type) ? ',' . $this->type : '') . '}';
}
/** @prototype void public function setPoint(float $x, float $y) */
public function setPoint($x, $y)
{
$this->values[(string) $x] = (float) $y;
}
public function setLabel($label)
{
$this->label = $label;
}
public function setType($type)
{
$this->type = '';
if ($type == 'bars') {
$this->type = 'bars:{show:true,lineWidth:10}';
}
if ($type == 'steps') {
$this->type = 'lines:{show:true,steps:true}';
}
}
public function getPoint($x)
{
if (array_key_exists((string) $x, $this->values)) {
return $this->values[(string) $x];
}
}
}

1400
classes/Customer.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class CustomerAddressCore.
*
* Holds address info of a Customer.
* This class extends AddressCore to be differentiated from other AddressCore objects in DB.
*/
class CustomerAddressCore extends Address
{
}

187
classes/CustomerMessage.php Normal file
View File

@@ -0,0 +1,187 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class CustomerMessageCore.
*/
class CustomerMessageCore extends ObjectModel
{
public $id;
/** @var int CustomerThread ID */
public $id_customer_thread;
/** @var */
public $id_employee;
/** @var string $message */
public $message;
/** @var string $file_name */
public $file_name;
/** @var string $ip_address */
public $ip_address;
/** @var string $user_agent */
public $user_agent;
/** @var int $private */
public $private;
/** @var string $date_add */
public $date_add;
/** @var string $date_upd */
public $date_upd;
/** @var bool $read */
public $read;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'customer_message',
'primary' => 'id_customer_message',
'fields' => array(
'id_employee' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'id_customer_thread' => array('type' => self::TYPE_INT),
'ip_address' => array('type' => self::TYPE_STRING, 'validate' => 'isIp2Long', 'size' => 15),
'message' => array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'required' => true, 'size' => 16777216),
'file_name' => array('type' => self::TYPE_STRING),
'user_agent' => array('type' => self::TYPE_STRING),
'private' => array('type' => self::TYPE_INT),
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
'read' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
),
);
/** @var array $webserviceParameters */
protected $webserviceParameters = array(
'fields' => array(
'id_employee' => array(
'xlink_resource' => 'employees',
),
'id_customer_thread' => array(
'xlink_resource' => 'customer_threads',
),
),
);
/**
* Get CustomerMessages by Order ID.
*
* @param int $idOrder Order ID
* @param bool $private Private
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public static function getMessagesByOrderId($idOrder, $private = true)
{
return Db::getInstance()->executeS('
SELECT cm.*,
c.`firstname` AS cfirstname,
c.`lastname` AS clastname,
e.`firstname` AS efirstname,
e.`lastname` AS elastname,
(COUNT(cm.id_customer_message) = 0 AND ct.id_customer != 0) AS is_new_for_me
FROM `' . _DB_PREFIX_ . 'customer_message` cm
LEFT JOIN `' . _DB_PREFIX_ . 'customer_thread` ct
ON ct.`id_customer_thread` = cm.`id_customer_thread`
LEFT JOIN `' . _DB_PREFIX_ . 'customer` c
ON ct.`id_customer` = c.`id_customer`
LEFT OUTER JOIN `' . _DB_PREFIX_ . 'employee` e
ON e.`id_employee` = cm.`id_employee`
WHERE ct.id_order = ' . (int) $idOrder . '
' . (!$private ? 'AND cm.`private` = 0' : '') . '
GROUP BY cm.id_customer_message
ORDER BY cm.date_add DESC
');
}
/**
* Get total CustomerMessages.
*
* @param string|null $where Additional SQL query
*
* @return int Amount of CustomerMessages found
*/
public static function getTotalCustomerMessages($where = null)
{
if (null === $where) {
return (int) Db::getInstance()->getValue(
'
SELECT COUNT(*)
FROM ' . _DB_PREFIX_ . 'customer_message
LEFT JOIN `' . _DB_PREFIX_ . 'customer_thread` ct ON (cm.`id_customer_thread` = ct.`id_customer_thread`)
WHERE 1' . Shop::addSqlRestriction()
);
} else {
return (int) Db::getInstance()->getValue(
'
SELECT COUNT(*)
FROM ' . _DB_PREFIX_ . 'customer_message cm
LEFT JOIN `' . _DB_PREFIX_ . 'customer_thread` ct ON (cm.`id_customer_thread` = ct.`id_customer_thread`)
WHERE ' . $where . Shop::addSqlRestriction()
);
}
}
/**
* Deletes current CustomerMessage from the database.
*
* @return bool `true` if delete was successful
*
* @throws PrestaShopException
*/
public function delete()
{
if (!empty($this->file_name)) {
@unlink(_PS_UPLOAD_DIR_ . $this->file_name);
}
return parent::delete();
}
/**
* Get the last message for a thread customer.
*
* @param $id_customer_thread Thread customer reference
*
* @return string Last message
*/
public static function getLastMessageForCustomerThread($id_customer_thread)
{
return (string) Db::getInstance()->getValue(
'
SELECT message
FROM ' . _DB_PREFIX_ . 'customer_message
WHERE id_customer_thread = ' . (int) $id_customer_thread . '
ORDER BY date_add DESC'
);
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* 2007-2020 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Core\Session\SessionInterface;
class CustomerSessionCore extends ObjectModel implements SessionInterface
{
public $id;
/** @var Id Customer */
public $id_customer;
/** @var string Token */
public $token;
/**
* @see ObjectModel::$definition
*/
public static $definition = [
'table' => 'customer_session',
'primary' => 'id_customer_session',
'fields' => [
'id_customer' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
'token' => ['type' => self::TYPE_STRING, 'validate' => 'isSha1', 'size' => 40, 'copy_post' => false],
],
];
/**
* {@inheritdoc}
*/
public function getId()
{
return $this->id;
}
/**
* {@inheritdoc}
*/
public function setUserId($idCustomer)
{
$this->id_customer = (int) $idCustomer;
}
/**
* {@inheritdoc}
*/
public function getUserId()
{
return (int) $this->id_customer;
}
/**
* {@inheritdoc}
*/
public function setToken($token)
{
$this->token = (string) $token;
}
/**
* {@inheritdoc}
*/
public function getToken()
{
return $this->token;
}
}

294
classes/CustomerThread.php Normal file
View File

@@ -0,0 +1,294 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class CustomerThreadCore extends ObjectModel
{
public $id;
public $id_shop;
public $id_lang;
public $id_contact;
public $id_customer;
public $id_order;
public $id_product;
public $status;
public $email;
public $token;
public $date_add;
public $date_upd;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'customer_thread',
'primary' => 'id_customer_thread',
'fields' => array(
'id_lang' => array(
'type' => self::TYPE_INT,
'validate' => 'isUnsignedId',
'required' => true,
),
'id_contact' => array(
'type' => self::TYPE_INT,
'validate' => 'isUnsignedId',
'required' => true,
),
'id_shop' => array(
'type' => self::TYPE_INT,
'validate' => 'isUnsignedId',
),
'id_customer' => array(
'type' => self::TYPE_INT,
'validate' => 'isUnsignedId',
),
'id_order' => array(
'type' => self::TYPE_INT,
'validate' => 'isUnsignedId',
),
'id_product' => array(
'type' => self::TYPE_INT,
'validate' => 'isUnsignedId',
),
'email' => array(
'type' => self::TYPE_STRING,
'validate' => 'isEmail',
'size' => 255,
),
'token' => array(
'type' => self::TYPE_STRING,
'validate' => 'isGenericName',
'required' => true,
),
'status' => array(
'type' => self::TYPE_STRING,
),
'date_add' => array(
'type' => self::TYPE_DATE,
'validate' => 'isDate',
),
'date_upd' => array(
'type' => self::TYPE_DATE,
'validate' => 'isDate',
),
),
);
protected $webserviceParameters = array(
'fields' => array(
'id_lang' => array(
'xlink_resource' => 'languages',
),
'id_shop' => array(
'xlink_resource' => 'shops',
),
'id_customer' => array(
'xlink_resource' => 'customers',
),
'id_order' => array(
'xlink_resource' => 'orders',
),
'id_product' => array(
'xlink_resource' => 'products',
),
),
'associations' => array(
'customer_messages' => array(
'resource' => 'customer_message',
'id' => array(
'required' => true,
),
),
),
);
public function getWsCustomerMessages()
{
return Db::getInstance()->executeS('
SELECT `id_customer_message` id
FROM `' . _DB_PREFIX_ . 'customer_message`
WHERE `id_customer_thread` = ' . (int) $this->id);
}
public function delete()
{
if (!Validate::isUnsignedId($this->id)) {
return false;
}
$return = true;
$result = Db::getInstance()->executeS(
'
SELECT `id_customer_message`
FROM `' . _DB_PREFIX_ . 'customer_message`
WHERE `id_customer_thread` = ' . (int) $this->id
);
if (count($result)) {
foreach ($result as $res) {
$message = new CustomerMessage((int) $res['id_customer_message']);
if (!Validate::isLoadedObject($message)) {
$return = false;
} else {
$return &= $message->delete();
}
}
}
$return &= parent::delete();
return $return;
}
public static function getCustomerMessages($id_customer, $read = null, $id_order = null)
{
$sql = 'SELECT *
FROM ' . _DB_PREFIX_ . 'customer_thread ct
LEFT JOIN ' . _DB_PREFIX_ . 'customer_message cm
ON ct.id_customer_thread = cm.id_customer_thread
WHERE id_customer = ' . (int) $id_customer;
if ($read !== null) {
$sql .= ' AND cm.`read` = ' . (int) $read;
}
if ($id_order !== null) {
$sql .= ' AND ct.`id_order` = ' . (int) $id_order;
}
return Db::getInstance()->executeS($sql);
}
public static function getIdCustomerThreadByEmailAndIdOrder($email, $id_order)
{
return Db::getInstance()->getValue(
'
SELECT cm.id_customer_thread
FROM ' . _DB_PREFIX_ . 'customer_thread cm
WHERE cm.email = \'' . pSQL($email) . '\'
AND cm.id_shop = ' . (int) Context::getContext()->shop->id . '
AND cm.id_order = ' . (int) $id_order
);
}
public static function getContacts()
{
return Db::getInstance()->executeS('
SELECT cl.*, COUNT(*) as total, (
SELECT id_customer_thread
FROM ' . _DB_PREFIX_ . 'customer_thread ct2
WHERE status = "open" AND ct.id_contact = ct2.id_contact
' . Shop::addSqlRestriction() . '
ORDER BY date_upd ASC
LIMIT 1
) as id_customer_thread
FROM ' . _DB_PREFIX_ . 'customer_thread ct
LEFT JOIN ' . _DB_PREFIX_ . 'contact_lang cl
ON (cl.id_contact = ct.id_contact AND cl.id_lang = ' . (int) Context::getContext()->language->id . ')
WHERE ct.status = "open"
AND ct.id_contact IS NOT NULL
AND cl.id_contact IS NOT NULL
' . Shop::addSqlRestriction() . '
GROUP BY ct.id_contact HAVING COUNT(*) > 0
');
}
public static function getTotalCustomerThreads($where = null)
{
if (null === $where) {
return (int) Db::getInstance()->getValue(
'
SELECT COUNT(*)
FROM ' . _DB_PREFIX_ . 'customer_thread
WHERE 1 ' . Shop::addSqlRestriction()
);
} else {
return (int) Db::getInstance()->getValue(
'
SELECT COUNT(*)
FROM ' . _DB_PREFIX_ . 'customer_thread
WHERE ' . $where . Shop::addSqlRestriction()
);
}
}
public static function getMessageCustomerThreads($id_customer_thread)
{
return Db::getInstance()->executeS('
SELECT ct.*, cm.*, cl.name subject, CONCAT(e.firstname, \' \', e.lastname) employee_name,
CONCAT(c.firstname, \' \', c.lastname) customer_name, c.firstname
FROM ' . _DB_PREFIX_ . 'customer_thread ct
LEFT JOIN ' . _DB_PREFIX_ . 'customer_message cm
ON (ct.id_customer_thread = cm.id_customer_thread)
LEFT JOIN ' . _DB_PREFIX_ . 'contact_lang cl
ON (cl.id_contact = ct.id_contact AND cl.id_lang = ' . (int) Context::getContext()->language->id . ')
LEFT JOIN ' . _DB_PREFIX_ . 'employee e
ON e.id_employee = cm.id_employee
LEFT JOIN ' . _DB_PREFIX_ . 'customer c
ON (IFNULL(ct.id_customer, ct.email) = IFNULL(c.id_customer, c.email))
WHERE ct.id_customer_thread = ' . (int) $id_customer_thread . '
ORDER BY cm.date_add ASC
');
}
public static function getNextThread($id_customer_thread)
{
$context = Context::getContext();
return Db::getInstance()->getValue('
SELECT id_customer_thread
FROM ' . _DB_PREFIX_ . 'customer_thread ct
WHERE ct.status = "open"
AND ct.date_upd = (
SELECT date_add FROM ' . _DB_PREFIX_ . 'customer_message
WHERE (id_employee IS NULL OR id_employee = 0)
AND id_customer_thread = ' . (int) $id_customer_thread . '
ORDER BY date_add DESC LIMIT 1
)
' . ($context->cookie->{'customer_threadFilter_cl!id_contact'} ?
'AND ct.id_contact = ' . (int) $context->cookie->{'customer_threadFilter_cl!id_contact'} : '') . '
' . ($context->cookie->{'customer_threadFilter_l!id_lang'} ?
'AND ct.id_lang = ' . (int) $context->cookie->{'customer_threadFilter_l!id_lang'} : '') .
' ORDER BY ct.date_upd ASC
');
}
public static function getCustomerMessagesOrder($id_customer, $id_order)
{
$sql = 'SELECT cm.*, c.`firstname` AS cfirstname, c.`lastname` AS clastname,
e.`firstname` AS efirstname, e.`lastname` AS elastname
FROM ' . _DB_PREFIX_ . 'customer_thread ct
LEFT JOIN ' . _DB_PREFIX_ . 'customer_message cm
ON ct.id_customer_thread = cm.id_customer_thread
LEFT JOIN `' . _DB_PREFIX_ . 'customer` c
ON ct.`id_customer` = c.`id_customer`
LEFT OUTER JOIN `' . _DB_PREFIX_ . 'employee` e
ON e.`id_employee` = cm.`id_employee`
WHERE ct.id_customer = ' . (int) $id_customer .
' AND ct.`id_order` = ' . (int) $id_order . '
GROUP BY cm.id_customer_message
ORDER BY cm.date_add DESC
LIMIT 2';
return Db::getInstance()->executeS($sql);
}
}

430
classes/Customization.php Normal file
View File

@@ -0,0 +1,430 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class CustomizationCore.
*/
class CustomizationCore extends ObjectModel
{
/** @var int */
public $id_product_attribute;
/** @var int */
public $id_address_delivery;
/** @var int */
public $id_cart;
/** @var int */
public $id_product;
/** @var int */
public $quantity;
/** @var int */
public $quantity_refunded;
/** @var int */
public $quantity_returned;
/** @var bool */
public $in_cart;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'customization',
'primary' => 'id_customization',
'fields' => array(
/* Classic fields */
'id_product_attribute' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_address_delivery' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_cart' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_product' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'quantity' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'quantity_refunded' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'quantity_returned' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'in_cart' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true),
),
);
protected $webserviceParameters = array(
'fields' => array(
'id_address_delivery' => array(
'xlink_resource' => array(
'resourceName' => 'addresses',
),
),
'id_cart' => array(
'xlink_resource' => array(
'resourceName' => 'carts',
),
),
'id_product' => array(
'xlink_resource' => array(
'resourceName' => 'products',
),
),
),
'associations' => array(
'customized_data_text_fields' => array(
'resource' => 'customized_data_text_field',
'virtual_entity' => true,
'fields' => array(
'id_customization_field' => array('required' => true, 'xlink_resource' => 'product_customization_fields'),
'value' => array(),
),
),
'customized_data_images' => array(
'resource' => 'customized_data_image',
'virtual_entity' => true,
'setter' => false,
'fields' => array(
'id_customization_field' => array('xlink_resource' => 'product_customization_fields'),
'value' => array(),
),
),
),
);
/**
* Get returned Customizations.
*
* @param int $idOrder Order ID
*
* @return array|bool
*/
public static function getReturnedCustomizations($idOrder)
{
if (($result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT ore.`id_order_return`, ord.`id_order_detail`, ord.`id_customization`, ord.`product_quantity`
FROM `' . _DB_PREFIX_ . 'order_return` ore
INNER JOIN `' . _DB_PREFIX_ . 'order_return_detail` ord ON (ord.`id_order_return` = ore.`id_order_return`)
WHERE ore.`id_order` = ' . (int) ($idOrder) . ' AND ord.`id_customization` != 0')) === false) {
return false;
}
$customizations = array();
foreach ($result as $row) {
$customizations[(int) ($row['id_customization'])] = $row;
}
return $customizations;
}
/**
* Get ordered Customizations.
*
* @param int $idCart Cart ID
*
* @return array|bool Ordered Customizations
* `false` if not found
*/
public static function getOrderedCustomizations($idCart)
{
if (!$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT `id_customization`, `quantity` FROM `' . _DB_PREFIX_ . 'customization` WHERE `id_cart` = ' . (int) ($idCart))) {
return false;
}
$customizations = array();
foreach ($result as $row) {
$customizations[(int) ($row['id_customization'])] = $row;
}
return $customizations;
}
/**
* Get price of Customization.
*
* @param int $idCustomization Customization ID
*
* @return float|int Price of customization
*/
public static function getCustomizationPrice($idCustomization)
{
if (!(int) $idCustomization) {
return 0;
}
return (float) Db::getInstance()->getValue(
'
SELECT SUM(`price`) FROM `' . _DB_PREFIX_ . 'customized_data`
WHERE `id_customization` = ' . (int) $idCustomization
);
}
/**
* Get weight of Customization.
*
* @param int $idCustomization Customization ID
*
* @return float|int Weight
*/
public static function getCustomizationWeight($idCustomization)
{
if (!(int) $idCustomization) {
return 0;
}
return (float) Db::getInstance()->getValue(
'
SELECT SUM(`weight`) FROM `' . _DB_PREFIX_ . 'customized_data`
WHERE `id_customization` = ' . (int) $idCustomization
);
}
/**
* Count Customization quantity by Product.
*
* @param array $customizations Customizations
*
* @return array Customization quantities by Product
*/
public static function countCustomizationQuantityByProduct($customizations)
{
$total = array();
foreach ($customizations as $customization) {
$total[(int) $customization['id_order_detail']] = !isset($total[(int) $customization['id_order_detail']]) ? (int) $customization['quantity'] : $total[(int) $customization['id_order_detail']] + (int) $customization['quantity'];
}
return $total;
}
/**
* Get label.
*
* @param int $idCustomization Customization ID
* @param int $idLang Language IOD
* @param int|null $idShop Shop ID
*
* @return bool|false|string|null
*/
public static function getLabel($idCustomization, $idLang, $idShop = null)
{
if (!(int) $idCustomization || !(int) $idLang) {
return false;
}
if (Shop::isFeatureActive() && !(int) $idShop) {
$idShop = (int) Context::getContext()->shop->id;
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
'
SELECT `name`
FROM `' . _DB_PREFIX_ . 'customization_field_lang`
WHERE `id_customization_field` = ' . (int) $idCustomization . ((int) $idShop ? ' AND `id_shop` = ' . (int) $idShop : '') . '
AND `id_lang` = ' . (int) $idLang
);
return $result;
}
/**
* Retrieve quantities from IDs.
*
* @param array $idsCustomizations Customization IDs
*
* @return array Quantities
*/
public static function retrieveQuantitiesFromIds($idsCustomizations)
{
$quantities = array();
$inValues = '';
foreach ($idsCustomizations as $key => $idCustomization) {
if ($key > 0) {
$inValues .= ',';
}
$inValues .= (int) $idCustomization;
}
if (!empty($inValues)) {
$results = Db::getInstance()->executeS(
'SELECT `id_customization`, `id_product`, `quantity`, `quantity_refunded`, `quantity_returned`
FROM `' . _DB_PREFIX_ . 'customization`
WHERE `id_customization` IN (' . $inValues . ')'
);
foreach ($results as $row) {
$quantities[$row['id_customization']] = $row;
}
}
return $quantities;
}
/**
* Count quantity by Cart.
*
* @param int $idCart Cart ID
*
* @return array
*/
public static function countQuantityByCart($idCart)
{
$quantity = array();
$results = Db::getInstance()->executeS('
SELECT `id_product`, `id_product_attribute`, SUM(`quantity`) AS quantity
FROM `' . _DB_PREFIX_ . 'customization`
WHERE `id_cart` = ' . (int) $idCart . '
GROUP BY `id_cart`, `id_product`, `id_product_attribute`
');
foreach ($results as $row) {
$quantity[$row['id_product']][$row['id_product_attribute']] = $row['quantity'];
}
return $quantity;
}
/**
* This method is allow to know if a feature is used or active.
*
* @since 1.5.0.1
*
* @return bool
*/
public static function isFeatureActive()
{
return Configuration::get('PS_CUSTOMIZATION_FEATURE_ACTIVE');
}
/**
* This method is allow to know if a Customization entity is currently used.
*
* @since 1.5.0.1
*
* @param $table
* @param $hasActiveColumn
*
* @return bool
*/
public static function isCurrentlyUsed($table = null, $hasActiveColumn = false)
{
return (bool) Db::getInstance()->getValue('
SELECT `id_customization_field`
FROM `' . _DB_PREFIX_ . 'customization_field`
');
}
/**
* Get customized text fields
* (for webservice).
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public function getWsCustomizedDataTextFields()
{
if (!$results = Db::getInstance()->executeS('
SELECT id_customization_field, value
FROM `' . _DB_PREFIX_ . 'customization_field` cf
LEFT JOIN `' . _DB_PREFIX_ . 'customized_data` cd ON (cf.id_customization_field = cd.index)
WHERE `id_product` = ' . (int) $this->id_product . '
AND id_customization = ' . (int) $this->id . '
AND cf.type = ' . (int) Product::CUSTOMIZE_TEXTFIELD)) {
return array();
}
return $results;
}
/**
* Get customized images data
* (for webservice).
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public function getWsCustomizedDataImages()
{
if (!$results = Db::getInstance()->executeS('
SELECT id_customization_field, value
FROM `' . _DB_PREFIX_ . 'customization_field` cf
LEFT JOIN `' . _DB_PREFIX_ . 'customized_data` cd ON (cf.id_customization_field = cd.index)
WHERE `id_product` = ' . (int) $this->id_product . '
AND id_customization = ' . (int) $this->id . '
AND cf.type = ' . (int) Product::CUSTOMIZE_FILE)) {
return array();
}
return $results;
}
/**
* Set customized text fields
* (for webservice).
*
* @param array $values
*
* @return bool
*/
public function setWsCustomizedDataTextFields($values)
{
$cart = new Cart($this->id_cart);
if (!Validate::isLoadedObject($cart)) {
WebserviceRequest::getInstance()->setError(500, $this->trans('Could not load cart id=%s', array($this->id_cart), 'Admin.Notifications.Error'), 137);
return false;
}
Db::getInstance()->execute('
DELETE FROM `' . _DB_PREFIX_ . 'customized_data`
WHERE id_customization = ' . (int) $this->id . '
AND type = ' . (int) Product::CUSTOMIZE_TEXTFIELD);
foreach ($values as $value) {
$query = 'INSERT INTO `' . _DB_PREFIX_ . 'customized_data` (`id_customization`, `type`, `index`, `value`)
VALUES (' . (int) $this->id . ', ' . (int) Product::CUSTOMIZE_TEXTFIELD . ', ' . (int) $value['id_customization_field'] . ', \'' . pSQL($value['value']) . '\')';
if (!Db::getInstance()->execute($query)) {
return false;
}
}
return true;
}
/**
* Delete the current context shops langs.
*
* @param int $idCustomizationField
* @param int[] $shopList
*
* @return bool
*
* @throws PrestaShopDatabaseException
*/
public static function deleteCustomizationFieldLangByShop($idCustomizationField, $shopList)
{
$return = Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'customization_field_lang`
WHERE `id_customization_field` = ' . (int) $idCustomizationField . '
AND `id_shop` IN (' . implode(',', $shopList) . ')');
if (!$return) {
throw new PrestaShopDatabaseException('An error occurred while deletion the customization fields lang');
}
return $return;
}
}

View File

@@ -0,0 +1,76 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class CustomizationFieldCore.
*/
class CustomizationFieldCore extends ObjectModel
{
/** @var int */
public $id_product;
/** @var int Customization type (0 File, 1 Textfield) (See Product class) */
public $type;
/** @var bool Field is required */
public $required;
/** @var bool Field was added by a module */
public $is_module;
/** @var string Label for customized field */
public $name;
/** @var bool Soft delete */
public $is_deleted;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'customization_field',
'primary' => 'id_customization_field',
'multilang' => true,
'multilang_shop' => true,
'fields' => array(
/* Classic fields */
'id_product' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'type' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'required' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true),
'is_module' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => false),
'is_deleted' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => false),
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'required' => true, 'size' => 255),
),
);
/** @var array $webserviceParameters */
protected $webserviceParameters = array(
'fields' => array(
'id_product' => array(
'xlink_resource' => array(
'resourceName' => 'products',
),
),
),
);
}

74
classes/DateRange.php Normal file
View File

@@ -0,0 +1,74 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class DateRangeCore.
*/
class DateRangeCore extends ObjectModel
{
/** @var string $time_start */
public $time_start;
/** @var string $time_end */
public $time_end;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'date_range',
'primary' => 'id_date_range',
'fields' => array(
'time_start' => array('type' => self::TYPE_DATE, 'validate' => 'isDate', 'required' => true),
'time_end' => array('type' => self::TYPE_DATE, 'validate' => 'isDate', 'required' => true),
),
);
/**
* Get current range.
*
* @return mixed
*/
public static function getCurrentRange()
{
$result = Db::getInstance()->getRow('
SELECT `id_date_range`, `time_end`
FROM `' . _DB_PREFIX_ . 'date_range`
WHERE `time_end` = (SELECT MAX(`time_end`) FROM `' . _DB_PREFIX_ . 'date_range`)');
if (!$result['id_date_range'] || strtotime($result['time_end']) < strtotime(date('Y-m-d H:i:s'))) {
// The default range is set to 1 day less 1 second (in seconds)
$rangeSize = 86399;
$dateRange = new DateRange();
$dateRange->time_start = date('Y-m-d');
$dateRange->time_end = strftime('%Y-%m-%d %H:%M:%S', strtotime($dateRange->time_start) + $rangeSize);
$dateRange->add();
return $dateRange->id;
}
return $result['id_date_range'];
}
}

107
classes/Delivery.php Normal file
View File

@@ -0,0 +1,107 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class DeliveryCore.
*/
class DeliveryCore extends ObjectModel
{
/** @var int */
public $id_delivery;
/** @var int * */
public $id_shop;
/** @var int * */
public $id_shop_group;
/** @var int */
public $id_carrier;
/** @var int */
public $id_range_price;
/** @var int */
public $id_range_weight;
/** @var int */
public $id_zone;
/** @var float */
public $price;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'delivery',
'primary' => 'id_delivery',
'fields' => array(
'id_carrier' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_range_price' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_range_weight' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_zone' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_shop' => array('type' => self::TYPE_INT),
'id_shop_group' => array('type' => self::TYPE_INT),
'price' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice', 'required' => true),
),
);
protected $webserviceParameters = array(
'objectsNodeName' => 'deliveries',
'fields' => array(
'id_carrier' => array('xlink_resource' => 'carriers'),
'id_range_price' => array('xlink_resource' => 'price_ranges'),
'id_range_weight' => array('xlink_resource' => 'weight_ranges'),
'id_zone' => array('xlink_resource' => 'zones'),
),
);
/**
* Get Object fields and values in array.
*
* @return array
*/
public function getFields()
{
$fields = parent::getFields();
// @todo add null management in definitions
if ($this->id_shop) {
$fields['id_shop'] = (int) $this->id_shop;
} else {
$fields['id_shop'] = null;
}
if ($this->id_shop_group) {
$fields['id_shop_group'] = (int) $this->id_shop_group;
} else {
$fields['id_shop_group'] = null;
}
return $fields;
}
}

1179
classes/Dispatcher.php Normal file

File diff suppressed because it is too large Load Diff

748
classes/Employee.php Normal file
View File

@@ -0,0 +1,748 @@
<?php
/**
* 2007-2020 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Adapter\CoreException;
use PrestaShop\PrestaShop\Adapter\ServiceLocator;
use PrestaShop\PrestaShop\Core\Crypto\Hashing;
/**
* Class EmployeeCore.
*/
class EmployeeCore extends ObjectModel
{
/** @var int $id Employee ID */
public $id;
/** @var string Determine employee profile */
public $id_profile;
/** @var string employee language */
public $id_lang;
/** @var string Lastname */
public $lastname;
/** @var string Firstname */
public $firstname;
/** @var string e-mail */
public $email;
/** @var string Password */
public $passwd;
/** @var datetime Password */
public $last_passwd_gen;
public $stats_date_from;
public $stats_date_to;
public $stats_compare_from;
public $stats_compare_to;
public $stats_compare_option = 1;
public $preselect_date_range;
/** @var string Display back office background in the specified color */
public $bo_color;
public $default_tab;
/** @var string employee's chosen theme */
public $bo_theme;
/** @var string employee's chosen css file */
public $bo_css = 'theme.css';
/** @var int employee desired screen width */
public $bo_width;
/** @var bool, false */
public $bo_menu = 1;
/* Deprecated */
public $bo_show_screencast = false;
/** @var bool Status */
public $active = 1;
/** @var bool Optin status */
public $optin = 1;
public $remote_addr;
/* employee notifications */
public $id_last_order;
public $id_last_customer_message;
public $id_last_customer;
/** @var string Unique token for forgot passsword feature */
public $reset_password_token;
/** @var string token validity date for forgot password feature */
public $reset_password_validity;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'employee',
'primary' => 'id_employee',
'fields' => array(
'lastname' => array('type' => self::TYPE_STRING, 'validate' => 'isName', 'required' => true, 'size' => 255),
'firstname' => array('type' => self::TYPE_STRING, 'validate' => 'isName', 'required' => true, 'size' => 255),
'email' => array('type' => self::TYPE_STRING, 'validate' => 'isEmail', 'required' => true, 'size' => 255),
'id_lang' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'required' => true),
'passwd' => array('type' => self::TYPE_STRING, 'validate' => 'isPasswd', 'required' => true, 'size' => 255),
'last_passwd_gen' => array('type' => self::TYPE_STRING),
'active' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'optin' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'id_profile' => array('type' => self::TYPE_INT, 'validate' => 'isInt', 'required' => true),
'bo_color' => array('type' => self::TYPE_STRING, 'validate' => 'isColor', 'size' => 32),
'default_tab' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
'bo_theme' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 32),
'bo_css' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 64),
'bo_width' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
'bo_menu' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'stats_date_from' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
'stats_date_to' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
'stats_compare_from' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
'stats_compare_to' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
'stats_compare_option' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
'preselect_date_range' => array('type' => self::TYPE_STRING, 'size' => 32),
'id_last_order' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
'id_last_customer_message' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
'id_last_customer' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
'reset_password_token' => array('type' => self::TYPE_STRING, 'validate' => 'isSha1', 'size' => 40, 'copy_post' => false),
'reset_password_validity' => array('type' => self::TYPE_DATE, 'validate' => 'isDateOrNull', 'copy_post' => false),
),
);
protected $webserviceParameters = array(
'fields' => array(
'id_lang' => array('xlink_resource' => 'languages'),
'last_passwd_gen' => array('setter' => null),
'stats_date_from' => array('setter' => null),
'stats_date_to' => array('setter' => null),
'stats_compare_from' => array('setter' => null),
'stats_compare_to' => array('setter' => null),
'passwd' => array('setter' => 'setWsPasswd'),
),
);
protected $associated_shops = array();
/**
* EmployeeCore constructor.
*
* @param int|null $id Employee ID
* @param int|null $idLang Language ID
* @param int|null $idShop Shop ID
*/
public function __construct($id = null, $idLang = null, $idShop = null)
{
parent::__construct($id, null, $idShop);
if (null !== $idLang) {
$this->id_lang = (int) (Language::getLanguage($idLang) !== false) ? $idLang : Configuration::get('PS_LANG_DEFAULT');
}
if ($this->id) {
$this->associated_shops = $this->getAssociatedShops();
}
$this->image_dir = _PS_EMPLOYEE_IMG_DIR_;
}
/**
* @see ObjectModel::getFields()
*
* @return array
*/
public function getFields()
{
if (empty($this->stats_date_from) || $this->stats_date_from == '0000-00-00') {
$this->stats_date_from = date('Y-m-d', strtotime('-1 month'));
}
if (empty($this->stats_compare_from) || $this->stats_compare_from == '0000-00-00') {
$this->stats_compare_from = null;
}
if (empty($this->stats_date_to) || $this->stats_date_to == '0000-00-00') {
$this->stats_date_to = date('Y-m-d');
}
if (empty($this->stats_compare_to) || $this->stats_compare_to == '0000-00-00') {
$this->stats_compare_to = null;
}
return parent::getFields();
}
/**
* Adds current Employee 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 Employee has been successfully added
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function add($autoDate = true, $nullValues = true)
{
$this->last_passwd_gen = date('Y-m-d H:i:s', strtotime('-' . Configuration::get('PS_PASSWD_TIME_BACK') . 'minutes'));
$this->saveOptin();
$this->updateTextDirection();
return parent::add($autoDate, $nullValues);
}
/**
* Updates the current object in the database.
*
* @param bool $nullValues Whether we want to use NULL values instead of empty quotes values
*
* @return bool Indicates whether the Employee has been successfully updated
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function update($nullValues = false)
{
if (empty($this->stats_date_from) || $this->stats_date_from == '0000-00-00') {
$this->stats_date_from = date('Y-m-d');
}
if (empty($this->stats_date_to) || $this->stats_date_to == '0000-00-00') {
$this->stats_date_to = date('Y-m-d');
}
$currentEmployee = new Employee((int) $this->id);
if ($currentEmployee->optin != $this->optin) {
$this->saveOptin();
}
$this->updateTextDirection();
return parent::update($nullValues);
}
protected function saveOptin()
{
if ($this->optin && !defined('PS_INSTALLATION_IN_PROGRESS')) {
$language = new Language($this->id_lang);
$params = http_build_query(array(
'email' => $this->email,
'method' => 'addMemberToNewsletter',
'language' => $language->iso_code,
'visitorType' => 1,
'source' => 'backoffice',
));
Tools::file_get_contents('https://www.prestashop.com/ajax/controller.php?' . $params);
}
}
/**
* Update Employee text direction.
*/
protected function updateTextDirection()
{
if (!defined('_PS_ADMIN_DIR_')) {
return;
}
$path = _PS_ADMIN_DIR_ . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $this->bo_theme . DIRECTORY_SEPARATOR . 'css' . DIRECTORY_SEPARATOR;
$language = new Language($this->id_lang);
if ($language->is_rtl && !strpos($this->bo_css, '_rtl')) {
$boCss = preg_replace('/^(.*)\.css$/', '$1_rtl.css', $this->bo_css);
if (file_exists($path . $boCss)) {
$this->bo_css = $boCss;
}
} elseif (!$language->is_rtl && strpos($this->bo_css, '_rtl')) {
$boCss = preg_replace('/^(.*)_rtl\.css$/', '$1.css', $this->bo_css);
if (file_exists($path . $boCss)) {
$this->bo_css = $boCss;
}
}
}
/**
* Return list of employees.
*
* @param bool $activeOnly Filter employee by active status
*
* @return array|false Employees or false
*/
public static function getEmployees($activeOnly = true)
{
return Db::getInstance()->executeS('
SELECT `id_employee`, `firstname`, `lastname`
FROM `' . _DB_PREFIX_ . 'employee`
' . ($activeOnly ? ' WHERE `active` = 1' : '') . '
ORDER BY `lastname` ASC
');
}
/**
* Return employee instance from its e-mail (optionally check password).
*
* @param string $email e-mail
* @param string $plaintextPassword Password is also checked if specified
* @param bool $activeOnly Filter employee by active status
*
* @return bool|Employee|EmployeeCore Employee instance
* `false` if not found
*/
public function getByEmail($email, $plaintextPassword = null, $activeOnly = true)
{
if (!Validate::isEmail($email) || ($plaintextPassword != null && !Validate::isPlaintextPassword($plaintextPassword))) {
die(Tools::displayError());
}
$sql = new DbQuery();
$sql->select('e.*');
$sql->from('employee', 'e');
$sql->where('e.`email` = \'' . pSQL($email) . '\'');
if ($activeOnly) {
$sql->where('e.`active` = 1');
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql);
if (!$result) {
return false;
}
/** @var Hashing $crypto */
$crypto = ServiceLocator::get(Hashing::class);
$passwordHash = $result['passwd'];
$shouldCheckPassword = null !== $plaintextPassword;
if ($shouldCheckPassword && !$crypto->checkHash($plaintextPassword, $passwordHash)) {
return false;
}
$this->id = $result['id_employee'];
$this->id_profile = $result['id_profile'];
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;
}
/**
* Check if Employee exists.
*
* @param string $email Employee email
*
* @return bool Indicates whether the Employee exists
*/
public static function employeeExists($email)
{
if (!Validate::isEmail($email)) {
die(Tools::displayError());
}
return (bool) Db::getInstance()->getValue('
SELECT `id_employee`
FROM `' . _DB_PREFIX_ . 'employee`
WHERE `email` = \'' . pSQL($email) . '\'
');
}
/**
* Check if employee password is the right one.
*
* @param string $passwordHash Password
*
* @return bool result
*/
public static function checkPassword($idEmployee, $passwordHash)
{
if (!Validate::isUnsignedId($idEmployee)) {
die(Tools::displayError());
}
$sql = new DbQuery();
$sql->select('e.`id_employee`');
$sql->from('employee', 'e');
$sql->where('e.`id_employee` = ' . (int) $idEmployee);
$sql->where('e.`passwd` = \'' . pSQL($passwordHash) . '\'');
$sql->where('e.`active` = 1');
// Get result from DB
return Db::getInstance()->getValue($sql);
}
/**
* Count amount of Employees with the given Profile ID.
*
* @param int $idProfile Profile ID
* @param bool $activeOnly Only active Employees
*
* @return false|string|null
*/
public static function countProfile($idProfile, $activeOnly = false)
{
return Db::getInstance()->getValue(
'
SELECT COUNT(*)
FROM `' . _DB_PREFIX_ . 'employee`
WHERE `id_profile` = ' . (int) $idProfile . '
' . ($activeOnly ? ' AND `active` = 1' : '')
);
}
/**
* Check if this Employee is the only SuperAdmin left.
*
* @return bool Indicates whether this Employee is the last one
*/
public function isLastAdmin()
{
return $this->isSuperAdmin()
&& Employee::countProfile($this->id_profile, true) == 1
&& $this->active;
}
/**
* Set password
* (for webservice).
*
* @param string $passwd Password
*
* @return bool Indicates whether the password was succesfully set
*/
public function setWsPasswd($passwd)
{
try {
/** @var \PrestaShop\PrestaShop\Core\Crypto\Hashing $crypto */
$crypto = ServiceLocator::get('\\PrestaShop\\PrestaShop\\Core\\Crypto\\Hashing');
} catch (CoreException $e) {
return false;
}
if ($this->id != 0) {
if ($this->passwd != $passwd) {
$this->passwd = $crypto->hash($passwd);
}
} else {
$this->passwd = $crypto->hash($passwd);
}
return true;
}
/**
* Check employee informations saved into cookie and return employee validity.
*
* @return bool employee validity
*/
public function isLoggedBack()
{
if (!Cache::isStored('isLoggedBack' . $this->id)) {
/* Employee is valid only if it can be load and if cookie password is the same as database one */
$result = (
$this->id
&& Validate::isUnsignedId($this->id)
&& Context::getContext()->cookie
&& Context::getContext()->cookie->isSessionAlive()
&& Employee::checkPassword($this->id, Context::getContext()->cookie->passwd)
&& (
!isset(Context::getContext()->cookie->remote_addr)
|| Context::getContext()->cookie->remote_addr == ip2long(Tools::getRemoteAddr())
|| !Configuration::get('PS_COOKIE_CHECKIP')
)
);
Cache::store('isLoggedBack' . $this->id, $result);
return $result;
}
return Cache::retrieve('isLoggedBack' . $this->id);
}
/**
* Logout.
*/
public function logout()
{
if (isset(Context::getContext()->cookie)) {
Context::getContext()->cookie->logout();
Context::getContext()->cookie->write();
}
$this->id = null;
}
/**
* Get favorite Module list.
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public function favoriteModulesList()
{
return Db::getInstance()->executeS(
'
SELECT `module`
FROM `' . _DB_PREFIX_ . 'module_preference`
WHERE `id_employee` = ' . (int) $this->id . ' AND `favorite` = 1 AND (`interest` = 1 OR `interest` IS NULL)'
);
}
/**
* Check if the employee is associated to a specific shop.
*
* @param int $idShop
*
* @return bool
*
* @since 1.5.0
*/
public function hasAuthOnShop($idShop)
{
return $this->isSuperAdmin() || in_array($idShop, $this->associated_shops);
}
/**
* Check if the employee is associated to a specific shop group.
*
* @param int $id_shop_group ShopGroup ID
*
* @return bool
*
* @since 1.5.0
*/
public function hasAuthOnShopGroup($idShopGroup)
{
if ($this->isSuperAdmin()) {
return true;
}
foreach ($this->associated_shops as $idShop) {
if ($idShopGroup == Shop::getGroupFromShop($idShop, true)) {
return true;
}
}
return false;
}
/**
* Get default id_shop with auth for current employee.
*
* @return int
*
* @since 1.5.0
*/
public function getDefaultShopID()
{
if ($this->isSuperAdmin() || in_array(Configuration::get('PS_SHOP_DEFAULT'), $this->associated_shops)) {
return Configuration::get('PS_SHOP_DEFAULT');
}
return $this->associated_shops[0];
}
/**
* Get Employees by Profile.
*
* @param int $idProfile Profile ID
* @param bool $activeOnly Only active Employees
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public static function getEmployeesByProfile($idProfile, $activeOnly = false)
{
return Db::getInstance()->executeS(
'
SELECT *
FROM `' . _DB_PREFIX_ . 'employee`
WHERE `id_profile` = ' . (int) $idProfile . '
' . ($activeOnly ? ' AND `active` = 1' : '')
);
}
/**
* Check if current employee is super administrator.
*
* @return bool
*/
public function isSuperAdmin()
{
return $this->id_profile == _PS_ADMIN_PROFILE_;
}
/**
* Get Employee image.
*
* @return string Image URL
*/
public function getImage()
{
if (!Validate::isLoadedObject($this)) {
return Tools::getAdminImageUrl('prestashop-avatar.png');
}
return Tools::getShopProtocol() . 'profile.prestashop.com/' . urlencode($this->email) . '.jpg';
}
/**
* Get last elements for notify.
*
* @param $element
*
* @return int
*/
public function getLastElementsForNotify($element)
{
$element = bqSQL($element);
$max = Db::getInstance()->getValue('
SELECT MAX(`id_' . $element . '`) as `id_' . $element . '`
FROM `' . _DB_PREFIX_ . $element . ($element == 'order' ? 's' : '') . '`');
// if no rows in table, set max to 0
if ((int) $max < 1) {
$max = 0;
}
return (int) $max;
}
/**
* Set last connection date.
*
* @param int $idEmployee Employee ID
*
* @return bool
*/
public static function setLastConnectionDate($idEmployee)
{
return Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'employee`
SET `last_connection_date` = CURRENT_DATE()
WHERE `id_employee` = ' . (int) $idEmployee . '
AND (`last_connection_date` < CURRENT_DATE()
OR `last_connection_date` IS NULL)
');
}
/**
* Fill Reset password unique token with random sha1 and its validity date. For forgot password feature.
*/
public function stampResetPasswordToken()
{
$salt = $this->id . '+' . uniqid(mt_rand(0, mt_getrandmax()), true);
$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 employee triggering the forgot password link too often).
*/
public function hasRecentResetPasswordToken()
{
if (!$this->reset_password_token || $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 || $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;
}
/**
* Is the Employee allowed to do the given action.
*
* @param $action
* @param $tab
*
* @return bool
*/
public function can($action, $tab)
{
$access = Profile::getProfileAccess($this->id_profile, Tab::getIdFromClassName($tab));
return $access[$action] == '1';
}
/**
* Returns the default tab class name.
*
* @return string|null
*/
public function getDefaultTabClassName()
{
if ($tabId = (int) $this->default_tab) {
return Tab::getClassNameById($tabId) ?: null;
}
return null;
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* 2007-2020 PrestaShop SA and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2020 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Core\Session\SessionInterface;
class EmployeeSessionCore extends ObjectModel implements SessionInterface
{
public $id;
/** @var int Id Employee */
public $id_employee;
/** @var string Token */
public $token;
/**
* @see ObjectModel::$definition
*/
public static $definition = [
'table' => 'employee_session',
'primary' => 'id_employee_session',
'fields' => [
'id_employee' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
'token' => ['type' => self::TYPE_STRING, 'validate' => 'isSha1', 'size' => 40, 'copy_post' => false],
],
];
/**
* {@inheritdoc}
*/
public function getId()
{
return $this->id;
}
/**
* {@inheritdoc}
*/
public function setUserId($idEmployee)
{
$this->id_employee = (int) $idEmployee;
}
/**
* {@inheritdoc}
*/
public function getUserId()
{
return (int) $this->id_employee;
}
/**
* {@inheritdoc}
*/
public function setToken($token)
{
$this->token = (string) $token;
}
/**
* {@inheritdoc}
*/
public function getToken()
{
return $this->token;
}
}

369
classes/Feature.php Normal file
View File

@@ -0,0 +1,369 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class FeatureCore.
*/
class FeatureCore extends ObjectModel
{
/** @var string Name */
public $name;
/** @var int $position */
public $position;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'feature',
'primary' => 'id_feature',
'multilang' => true,
'fields' => array(
'position' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 128),
),
);
protected $webserviceParameters = array(
'objectsNodeName' => 'product_features',
'objectNodeName' => 'product_feature',
'fields' => array(),
);
/**
* Get a feature data for a given id_feature and id_lang.
*
* @param int $idLang Language ID
* @param int $idFeature Feature ID
*
* @return array Array with feature's data
*/
public static function getFeature($idLang, $idFeature)
{
return Db::getInstance()->getRow(
'
SELECT *
FROM `' . _DB_PREFIX_ . 'feature` f
LEFT JOIN `' . _DB_PREFIX_ . 'feature_lang` fl
ON ( f.`id_feature` = fl.`id_feature` AND fl.`id_lang` = ' . (int) $idLang . ')
WHERE f.`id_feature` = ' . (int) $idFeature
);
}
/**
* Get all features for a given language.
*
* @param int $idLang Language id
*
* @return array Multiple arrays with feature's data
*/
public static function getFeatures($idLang, $withShop = true)
{
return Db::getInstance()->executeS('
SELECT DISTINCT f.id_feature, f.*, fl.*
FROM `' . _DB_PREFIX_ . 'feature` f
' . ($withShop ? Shop::addSqlAssociation('feature', 'f') : '') . '
LEFT JOIN `' . _DB_PREFIX_ . 'feature_lang` fl ON (f.`id_feature` = fl.`id_feature` AND fl.`id_lang` = ' . (int) $idLang . ')
ORDER BY f.`position` ASC');
}
/**
* Delete several objects from database.
*
* @param array $selection Array with items to delete
*
* @return bool Deletion result
*/
public function deleteSelection($selection)
{
/* Also delete Attributes */
foreach ($selection as $value) {
$obj = new Feature($value);
if (!$obj->delete()) {
return false;
}
}
return true;
}
/**
* Adds current Feature 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 Feature has been successfully added
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function add($autoDate = true, $nullValues = false)
{
if ($this->position <= 0) {
$this->position = Feature::getHigherPosition() + 1;
}
$return = parent::add($autoDate, true);
Hook::exec('actionFeatureSave', array('id_feature' => $this->id));
return $return;
}
/**
* Updates the current Feature in the database.
*
* @param bool $nullValues Whether we want to use NULL values instead of empty quotes values
*
* @return bool Indicates whether the Feature has been successfully updated
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function update($nullValues = false)
{
$this->clearCache();
$result = 1;
$fields = $this->getFieldsLang();
foreach ($fields as $field) {
foreach (array_keys($field) as $key) {
if (!Validate::isTableOrIdentifier($key)) {
die(Tools::displayError());
}
}
$sql = 'SELECT `id_lang` FROM `' . pSQL(_DB_PREFIX_ . $this->def['table']) . '_lang`
WHERE `' . $this->def['primary'] . '` = ' . (int) $this->id . '
AND `id_lang` = ' . (int) $field['id_lang'];
$mode = Db::getInstance()->getRow($sql);
$result &= (!$mode) ? Db::getInstance()->insert($this->def['table'] . '_lang', $field) :
Db::getInstance()->update(
$this->def['table'] . '_lang',
$field,
'`' . $this->def['primary'] . '` = ' . (int) $this->id . ' AND `id_lang` = ' . (int) $field['id_lang']
);
}
if ($result) {
$result &= parent::update($nullValues);
if ($result) {
Hook::exec('actionFeatureSave', array('id_feature' => $this->id));
}
}
return $result;
}
/**
* Deletes current Feature from the database.
*
* @return bool `true` if delete was successful
*
* @throws PrestaShopException
*/
public function delete()
{
/* Also delete related attributes */
Db::getInstance()->execute('
DELETE
`' . _DB_PREFIX_ . 'feature_value_lang`
FROM
`' . _DB_PREFIX_ . 'feature_value_lang`
JOIN `' . _DB_PREFIX_ . 'feature_value`
ON (`' . _DB_PREFIX_ . 'feature_value_lang`.id_feature_value = `' . _DB_PREFIX_ . 'feature_value`.id_feature_value)
WHERE
`' . _DB_PREFIX_ . 'feature_value`.`id_feature` = ' . (int) $this->id . '
');
Db::getInstance()->execute(
'
DELETE FROM `' . _DB_PREFIX_ . 'feature_value`
WHERE `id_feature` = ' . (int) $this->id
);
// Also delete related products
Db::getInstance()->execute(
'
DELETE FROM `' . _DB_PREFIX_ . 'feature_product`
WHERE `id_feature` = ' . (int) $this->id
);
$return = parent::delete();
if ($return) {
Hook::exec('actionFeatureDelete', array('id_feature' => $this->id));
}
/* Reinitializing position */
$this->cleanPositions();
return $return;
}
/**
* Count number of features for a given language.
*
* @param int $idLang Language id
*
*@return int Number of feature
*/
public static function nbFeatures($idLang)
{
return Db::getInstance()->getValue('
SELECT COUNT(*) as nb
FROM `' . _DB_PREFIX_ . 'feature` ag
LEFT JOIN `' . _DB_PREFIX_ . 'feature_lang` agl
ON (ag.`id_feature` = agl.`id_feature` AND `id_lang` = ' . (int) $idLang . ')
');
}
/**
* Create a feature from import.
*
* @param string $name Feature name
* @param bool $position Feature position
*
* @return int Feature ID
*/
public static function addFeatureImport($name, $position = false)
{
$rq = Db::getInstance()->getRow('
SELECT `id_feature`
FROM ' . _DB_PREFIX_ . 'feature_lang
WHERE `name` = \'' . pSQL($name) . '\'
GROUP BY `id_feature`
');
if (empty($rq)) {
// Feature doesn't exist, create it
$feature = new Feature();
$feature->name = array_fill_keys(Language::getIDs(), (string) $name);
if ($position) {
$feature->position = (int) $position;
} else {
$feature->position = Feature::getHigherPosition() + 1;
}
$feature->add();
return $feature->id;
} elseif (isset($rq['id_feature']) && $rq['id_feature']) {
if (is_numeric($position) && $feature = new Feature((int) $rq['id_feature'])) {
$feature->position = (int) $position;
if (Validate::isLoadedObject($feature)) {
$feature->update();
}
}
return (int) $rq['id_feature'];
}
}
/**
* This metohd is allow to know if a feature is used or active.
*
* @return bool
*
* @since 1.5.0.1
*/
public static function isFeatureActive()
{
return (bool) Configuration::get('PS_FEATURE_FEATURE_ACTIVE');
}
/**
* Move a feature.
*
* @param bool $way Up (1) or Down (0)
* @param int $position
*
* @return bool Update result
*/
public function updatePosition($way, $position, $idFeature = null)
{
if (!$res = Db::getInstance()->executeS(
'
SELECT `position`, `id_feature`
FROM `' . _DB_PREFIX_ . 'feature`
WHERE `id_feature` = ' . (int) ($idFeature ? $idFeature : $this->id) . '
ORDER BY `position` ASC'
)) {
return false;
}
foreach ($res as $feature) {
if ((int) $feature['id_feature'] == (int) $this->id) {
$moved_feature = $feature;
}
}
if (!isset($moved_feature) || !isset($position)) {
return false;
}
// < and > statements rather than BETWEEN operator
// since BETWEEN is treated differently according to databases
return Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'feature`
SET `position`= `position` ' . ($way ? '- 1' : '+ 1') . '
WHERE `position`
' . ($way
? '> ' . (int) $moved_feature['position'] . ' AND `position` <= ' . (int) $position
: '< ' . (int) $moved_feature['position'] . ' AND `position` >= ' . (int) $position))
&& Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'feature`
SET `position` = ' . (int) $position . '
WHERE `id_feature`=' . (int) $moved_feature['id_feature']);
}
/**
* Reorder feature position
* Call it after deleting a feature.
*
* @return bool $return
*/
public static function cleanPositions()
{
Db::getInstance()->execute('SET @i = -1', false);
$sql = 'UPDATE `' . _DB_PREFIX_ . 'feature` SET `position` = @i:=@i+1 ORDER BY `position` ASC';
return (bool) Db::getInstance()->execute($sql);
}
/**
* getHigherPosition.
*
* Get the higher feature position
*
* @return int $position
*/
public static function getHigherPosition()
{
$sql = 'SELECT MAX(`position`)
FROM `' . _DB_PREFIX_ . 'feature`';
$position = Db::getInstance()->getValue($sql);
return (is_numeric($position)) ? $position : -1;
}
}

259
classes/FeatureValue.php Normal file
View File

@@ -0,0 +1,259 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class FeatureValueCore.
*/
class FeatureValueCore extends ObjectModel
{
/** @var int Group id which attribute belongs */
public $id_feature;
/** @var string Name */
public $value;
/** @var bool Custom */
public $custom = 0;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'feature_value',
'primary' => 'id_feature_value',
'multilang' => true,
'fields' => array(
'id_feature' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'custom' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
/* Lang fields */
'value' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 255),
),
);
protected $webserviceParameters = array(
'objectsNodeName' => 'product_feature_values',
'objectNodeName' => 'product_feature_value',
'fields' => array(
'id_feature' => array('xlink_resource' => 'product_features'),
),
);
/**
* Get all values for a given feature.
*
* @param bool $idFeature Feature id
*
* @return array Array with feature's values
*/
public static function getFeatureValues($idFeature)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'
SELECT *
FROM `' . _DB_PREFIX_ . 'feature_value`
WHERE `id_feature` = ' . (int) $idFeature
);
}
/**
* Get all values for a given feature and language.
*
* @param int $idLang Language id
* @param bool $idFeature Feature id
*
* @return array Array with feature's values
*/
public static function getFeatureValuesWithLang($idLang, $idFeature, $custom = false)
{
return Db::getInstance()->executeS('
SELECT *
FROM `' . _DB_PREFIX_ . 'feature_value` v
LEFT JOIN `' . _DB_PREFIX_ . 'feature_value_lang` vl
ON (v.`id_feature_value` = vl.`id_feature_value` AND vl.`id_lang` = ' . (int) $idLang . ')
WHERE v.`id_feature` = ' . (int) $idFeature . '
' . (!$custom ? 'AND (v.`custom` IS NULL OR v.`custom` = 0)' : '') . '
ORDER BY vl.`value` ASC
');
}
/**
* Get all language for a given value.
*
* @param bool $idFeatureValue Feature value id
*
* @return array Array with value's languages
*/
public static function getFeatureValueLang($idFeatureValue)
{
return Db::getInstance()->executeS('
SELECT *
FROM `' . _DB_PREFIX_ . 'feature_value_lang`
WHERE `id_feature_value` = ' . (int) $idFeatureValue . '
ORDER BY `id_lang`
');
}
/**
* Select the good lang in tab.
*
* @param array $lang Array with all language
* @param int $idLang Language id
*
* @return string String value name selected
*/
public static function selectLang($lang, $idLang)
{
foreach ($lang as $tab) {
if ($tab['id_lang'] == $idLang) {
return $tab['value'];
}
}
}
/**
* Add FeatureValue from import.
*
* @param int $idFeature
* @param string $value
* @param null $idProduct
* @param null $idLang
* @param bool $custom
*
* @return int
*/
public static function addFeatureValueImport($idFeature, $value, $idProduct = null, $idLang = null, $custom = false)
{
$idFeatureValue = false;
if (null !== $idProduct && $idProduct) {
$idFeatureValue = Db::getInstance()->getValue('
SELECT fp.`id_feature_value`
FROM ' . _DB_PREFIX_ . 'feature_product fp
INNER JOIN ' . _DB_PREFIX_ . 'feature_value fv USING (`id_feature_value`)
WHERE fp.`id_feature` = ' . (int) $idFeature . '
AND fv.`custom` = ' . (int) $custom . '
AND fp.`id_product` = ' . (int) $idProduct);
if ($custom && $idFeatureValue && null !== $idLang && $idLang) {
Db::getInstance()->execute('
UPDATE ' . _DB_PREFIX_ . 'feature_value_lang
SET `value` = \'' . pSQL($value) . '\'
WHERE `id_feature_value` = ' . (int) $idFeatureValue . '
AND `value` != \'' . pSQL($value) . '\'
AND `id_lang` = ' . (int) $idLang);
}
}
if (!$custom) {
$idFeatureValue = Db::getInstance()->getValue('
SELECT fv.`id_feature_value`
FROM ' . _DB_PREFIX_ . 'feature_value fv
LEFT JOIN ' . _DB_PREFIX_ . 'feature_value_lang fvl ON (fvl.`id_feature_value` = fv.`id_feature_value` AND fvl.`id_lang` = ' . (int) $idLang . ')
WHERE `value` = \'' . pSQL($value) . '\'
AND fv.`id_feature` = ' . (int) $idFeature . '
AND fv.`custom` = 0
GROUP BY fv.`id_feature_value`');
}
if ($idFeatureValue) {
return (int) $idFeatureValue;
}
// Feature doesn't exist, create it
$feature_value = new FeatureValue();
$feature_value->id_feature = (int) $idFeature;
$feature_value->custom = (bool) $custom;
$feature_value->value = array_fill_keys(Language::getIDs(false), $value);
$feature_value->add();
return (int) $feature_value->id;
}
/**
* Adds current FeatureValue 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 FeatureValue has been successfully added
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function add($autoDate = true, $nullValues = false)
{
$return = parent::add($autoDate, $nullValues);
if ($return) {
Hook::exec('actionFeatureValueSave', array('id_feature_value' => $this->id));
}
return $return;
}
/**
* Updates the current FeatureValue in the database.
*
* @param bool $nullValues Whether we want to use NULL values instead of empty quotes values
*
* @return bool Indicates whether the FeatureValue has been successfully updated
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function update($nullValues = false)
{
$return = parent::update($nullValues);
if ($return) {
Hook::exec('actionFeatureValueSave', array('id_feature_value' => $this->id));
}
return $return;
}
/**
* Deletes current FeatureValue from the database.
*
* @return bool `true` if delete was successful
*
* @throws PrestaShopException
*/
public function delete()
{
/* Also delete related products */
Db::getInstance()->execute(
'
DELETE FROM `' . _DB_PREFIX_ . 'feature_product`
WHERE `id_feature_value` = ' . (int) $this->id
);
$return = parent::delete();
if ($return) {
Hook::exec('actionFeatureValueDelete', array('id_feature_value' => $this->id));
}
return $return;
}
}

98
classes/FileUploader.php Normal file
View File

@@ -0,0 +1,98 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class FileUploaderCore
{
protected $allowedExtensions = array();
/** @var QqUploadedFileXhr|QqUploadedFileForm|false */
protected $file;
protected $sizeLimit;
public function __construct(array $allowedExtensions = array(), $sizeLimit = 10485760)
{
$allowedExtensions = array_map('strtolower', $allowedExtensions);
$this->allowedExtensions = $allowedExtensions;
$this->sizeLimit = $sizeLimit;
if (isset($_GET['qqfile'])) {
$this->file = new QqUploadedFileXhr();
} elseif (isset($_FILES['qqfile'])) {
$this->file = new QqUploadedFileForm();
} else {
$this->file = false;
}
}
protected function toBytes($str)
{
$val = trim($str);
$last = strtolower($str[strlen($str) - 1]);
switch ($last) {
case 'g':
$val *= 1024;
// no break
case 'm':
$val *= 1024;
// no break
case 'k':
$val *= 1024;
}
return $val;
}
/**
* Returns array('success'=>true) or array('error'=>'error message').
*/
public function handleUpload()
{
if (!$this->file) {
return array('error' => Context::getContext()->getTranslator()->trans('No files were uploaded.', array(), 'Admin.Notifications.Error'));
}
$size = $this->file->getSize();
if ($size == 0) {
return array('error' => Context::getContext()->getTranslator()->trans('Source file does not exist or is empty.', array(), 'Admin.Notifications.Error'));
}
if ($size > $this->sizeLimit) {
return array('error' => Context::getContext()->getTranslator()->trans('The uploaded file is too large.', array(), 'Admin.Notifications.Error'));
}
$pathinfo = pathinfo($this->file->getName());
$these = implode(', ', $this->allowedExtensions);
if (!isset($pathinfo['extension'])) {
return array('error' => Context::getContext()->getTranslator()->trans('File has an invalid extension, it should be one of these: %s.', array($these), 'Admin.Notifications.Error'));
}
$ext = $pathinfo['extension'];
if ($this->allowedExtensions && !in_array(strtolower($ext), $this->allowedExtensions)) {
return array('error' => Context::getContext()->getTranslator()->trans('File has an invalid extension, it should be one of these: %s.', array($these), 'Admin.Notifications.Error'));
}
return $this->file->save();
}
}

99
classes/Gender.php Normal file
View File

@@ -0,0 +1,99 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class GenderCore.
*
* @since 1.5.0
*/
class GenderCore extends ObjectModel
{
public $id;
public $id_gender;
public $name;
public $type;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'gender',
'primary' => 'id_gender',
'multilang' => true,
'fields' => array(
'type' => array('type' => self::TYPE_INT, 'required' => true),
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isString', 'required' => true, 'size' => 20),
),
);
/**
* GenderCore constructor.
*
* @param int|null $id
* @param int|null $idLang
* @param int|null $idShop
*/
public function __construct($id = null, $idLang = null, $idShop = null)
{
parent::__construct($id, $idLang, $idShop);
$this->image_dir = _PS_GENDERS_DIR_;
}
/**
* Get all Genders.
*
* @param int|null $idLang Language ID
*
* @return PrestaShopCollection
*/
public static function getGenders($idLang = null)
{
if (null === $idLang) {
$idLang = Context::getContext()->language->id;
}
$genders = new PrestaShopCollection('Gender', $idLang);
return $genders;
}
/**
* Get Gender image.
*
* @return string File path
*/
public function getImage()
{
if (!isset($this->id) || empty($this->id) || !file_exists(_PS_GENDERS_DIR_ . $this->id . '.jpg')) {
return _THEME_GENDERS_DIR_ . 'Unknown.jpg';
}
return _THEME_GENDERS_DIR_ . $this->id . '.jpg';
}
}

424
classes/Group.php Normal file
View File

@@ -0,0 +1,424 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class GroupCore extends ObjectModel
{
public $id;
/** @var string Lastname */
public $name;
/** @var string Reduction */
public $reduction;
/** @var int Price display method (tax inc/tax exc) */
public $price_display_method;
/** @var bool Show prices */
public $show_prices = 1;
/** @var string Object creation date */
public $date_add;
/** @var string Object last modification date */
public $date_upd;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'group',
'primary' => 'id_group',
'multilang' => true,
'fields' => array(
'reduction' => array('type' => self::TYPE_FLOAT, 'validate' => 'isFloat'),
'price_display_method' => array('type' => self::TYPE_INT, 'validate' => 'isPriceDisplayMethod', 'required' => true),
'show_prices' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 32),
),
);
protected static $cache_reduction = array();
protected static $group_price_display_method = array();
protected static $ps_group_feature_active = null;
protected static $groups = array();
protected static $ps_unidentified_group = null;
protected static $ps_customer_group = null;
protected $webserviceParameters = array();
public function __construct($id = null, $id_lang = null, $id_shop = null)
{
parent::__construct($id, $id_lang, $id_shop);
if ($this->id && !isset(Group::$group_price_display_method[$this->id])) {
self::$group_price_display_method[$this->id] = $this->price_display_method;
}
}
/**
* WARNING: For testing only. Do NOT rely on this method, it may be removed at any time.
*/
public static function clearCachedValues()
{
self::$cache_reduction = array();
self::$group_price_display_method = array();
self::$ps_group_feature_active = null;
self::$groups = array();
self::$ps_unidentified_group = null;
self::$ps_customer_group = null;
}
public static function getGroups($id_lang, $id_shop = false)
{
$shop_criteria = '';
if ($id_shop) {
$shop_criteria = Shop::addSqlAssociation('group', 'g');
}
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT DISTINCT g.`id_group`, g.`reduction`, g.`price_display_method`, g.`show_prices`, gl.`name`
FROM `' . _DB_PREFIX_ . 'group` g
LEFT JOIN `' . _DB_PREFIX_ . 'group_lang` AS gl ON (g.`id_group` = gl.`id_group` AND gl.`id_lang` = ' . (int) $id_lang . ')
' . $shop_criteria . '
ORDER BY g.`id_group` ASC');
}
public function getCustomers($count = false, $start = 0, $limit = 0, $shop_filtering = false)
{
if ($count) {
return Db::getInstance()->getValue('
SELECT COUNT(*)
FROM `' . _DB_PREFIX_ . 'customer_group` cg
LEFT JOIN `' . _DB_PREFIX_ . 'customer` c ON (cg.`id_customer` = c.`id_customer`)
WHERE cg.`id_group` = ' . (int) $this->id . '
' . ($shop_filtering ? Shop::addSqlRestriction(Shop::SHARE_CUSTOMER) : '') . '
AND c.`deleted` != 1');
}
return Db::getInstance()->executeS('
SELECT cg.`id_customer`, c.*
FROM `' . _DB_PREFIX_ . 'customer_group` cg
LEFT JOIN `' . _DB_PREFIX_ . 'customer` c ON (cg.`id_customer` = c.`id_customer`)
WHERE cg.`id_group` = ' . (int) $this->id . '
AND c.`deleted` != 1
' . ($shop_filtering ? Shop::addSqlRestriction(Shop::SHARE_CUSTOMER) : '') . '
ORDER BY cg.`id_customer` ASC
' . ($limit > 0 ? 'LIMIT ' . (int) $start . ', ' . (int) $limit : ''));
}
public static function getReduction($id_customer = null)
{
if (!isset(self::$cache_reduction['customer'][(int) $id_customer])) {
$id_group = $id_customer ? Customer::getDefaultGroupId((int) $id_customer) : (int) Group::getCurrent()->id;
self::$cache_reduction['customer'][(int) $id_customer] = Group::getReductionByIdGroup($id_group);
}
return self::$cache_reduction['customer'][(int) $id_customer];
}
public static function getReductionByIdGroup($id_group)
{
if (!isset(self::$cache_reduction['group'][$id_group])) {
self::$cache_reduction['group'][$id_group] = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
SELECT `reduction`
FROM `' . _DB_PREFIX_ . 'group`
WHERE `id_group` = ' . (int) $id_group);
}
return self::$cache_reduction['group'][$id_group];
}
/**
* Returns price display method for a group (i.e. price should be including tax or not).
*
* @param int $id_group
*
* @return int Returns 0 (PS_TAX_INC) if tax should be included, otherwise 1 (PS_TAX_EXC) - tax should be excluded
*/
public static function getPriceDisplayMethod($id_group)
{
if (!isset(Group::$group_price_display_method[$id_group])) {
self::$group_price_display_method[$id_group] = (int) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
'
SELECT `price_display_method`
FROM `' . _DB_PREFIX_ . 'group`
WHERE `id_group` = ' . (int) $id_group
);
}
return self::$group_price_display_method[$id_group];
}
/**
* Returns default price display method, i.e. for the 'Customers' group.
*
* @see getPriceDisplayMethod()
*
* @return int Returns 0 (PS_TAX_INC) if tax should be included, otherwise 1 (PS_TAX_EXC) - tax should be excluded
*/
public static function getDefaultPriceDisplayMethod()
{
return Group::getPriceDisplayMethod((int) Configuration::get('PS_CUSTOMER_GROUP'));
}
public function add($autodate = true, $null_values = false)
{
Configuration::updateGlobalValue('PS_GROUP_FEATURE_ACTIVE', '1');
if (parent::add($autodate, $null_values)) {
Category::setNewGroupForHome((int) $this->id);
Carrier::assignGroupToAllCarriers((int) $this->id);
return true;
}
return false;
}
public function update($autodate = true, $null_values = false)
{
if (!Configuration::getGlobalValue('PS_GROUP_FEATURE_ACTIVE') && $this->reduction > 0) {
Configuration::updateGlobalValue('PS_GROUP_FEATURE_ACTIVE', 1);
}
return parent::update($autodate, $null_values);
}
public function delete()
{
if ($this->id == (int) Configuration::get('PS_CUSTOMER_GROUP')) {
return false;
}
if (parent::delete()) {
Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'cart_rule_group` WHERE `id_group` = ' . (int) $this->id);
Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'customer_group` WHERE `id_group` = ' . (int) $this->id);
Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'category_group` WHERE `id_group` = ' . (int) $this->id);
Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'group_reduction` WHERE `id_group` = ' . (int) $this->id);
Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'product_group_reduction_cache` WHERE `id_group` = ' . (int) $this->id);
$this->truncateModulesRestrictions($this->id);
// Add default group (id 3) to customers without groups
Db::getInstance()->execute('INSERT INTO `' . _DB_PREFIX_ . 'customer_group` (
SELECT c.id_customer, ' . (int) Configuration::get('PS_CUSTOMER_GROUP') . ' FROM `' . _DB_PREFIX_ . 'customer` c
LEFT JOIN `' . _DB_PREFIX_ . 'customer_group` cg
ON cg.id_customer = c.id_customer
WHERE cg.id_customer IS NULL)');
// Set to the customer the default group
// Select the minimal id from customer_group
Db::getInstance()->execute('UPDATE `' . _DB_PREFIX_ . 'customer` cg
SET id_default_group =
IFNULL((
SELECT min(id_group) FROM `' . _DB_PREFIX_ . 'customer_group`
WHERE id_customer = cg.id_customer),
' . (int) Configuration::get('PS_CUSTOMER_GROUP') . ')
WHERE `id_default_group` = ' . (int) $this->id);
// Remove group restrictions
$res = Db::getInstance()->delete('module_group', 'id_group = ' . (int) $this->id);
return $res;
}
return false;
}
/**
* This method is allow to know if a feature is used or active.
*
* @since 1.5.0.1
*
* @return bool
*/
public static function isFeatureActive()
{
if (self::$ps_group_feature_active === null) {
self::$ps_group_feature_active = (bool) Configuration::get('PS_GROUP_FEATURE_ACTIVE');
}
return self::$ps_group_feature_active;
}
/**
* This method is allow to know if there are other groups than the default ones.
*
* @since 1.5.0.1
*
* @param $table
* @param $has_active_column
*
* @return bool
*/
public static function isCurrentlyUsed($table = null, $has_active_column = false)
{
return (bool) (Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('SELECT COUNT(*) FROM `' . _DB_PREFIX_ . 'group`') > 3);
}
/**
* Truncate all modules restrictions for the group.
*
* @param int $id_group
*
* @return bool
*/
public static function truncateModulesRestrictions($id_group)
{
return Db::getInstance()->execute('
DELETE FROM `' . _DB_PREFIX_ . 'module_group`
WHERE `id_group` = ' . (int) $id_group);
}
/**
* Truncate all restrictions by module.
*
* @param int $id_module
*
* @return bool
*/
public static function truncateRestrictionsByModule($id_module)
{
return Db::getInstance()->execute('
DELETE FROM `' . _DB_PREFIX_ . 'module_group`
WHERE `id_module` = ' . (int) $id_module);
}
/**
* Adding restrictions modules to the group with id $id_group.
*
* @param $id_group
* @param $modules
* @param array $shops
*
* @return bool
*/
public static function addModulesRestrictions($id_group, $modules, $shops = array(1))
{
if (!is_array($modules) || !count($modules) || !is_array($shops) || !count($shops)) {
return false;
}
// Delete all record for this group
Db::getInstance()->execute(
'DELETE FROM `' . _DB_PREFIX_ . 'module_group`
WHERE `id_group` = ' . (int) $id_group . '
AND `id_shop` IN ('
. (implode(',', array_map('intval', $shops)))
. ')'
);
$sql = 'INSERT INTO `' . _DB_PREFIX_ . 'module_group` (`id_module`, `id_shop`, `id_group`) VALUES ';
foreach ($modules as $module) {
foreach ($shops as $shop) {
$sql .= '("' . (int) $module . '", "' . (int) $shop . '", "' . (int) $id_group . '"),';
}
}
$sql = rtrim($sql, ',');
return (bool) Db::getInstance()->execute($sql);
}
/**
* Add restrictions for a new module.
* We authorize every groups to the new module.
*
* @param int $id_module
* @param array $shops
*
* @return bool
*/
public static function addRestrictionsForModule($id_module, $shops = array(1))
{
if (!is_array($shops) || !count($shops)) {
return false;
}
$res = true;
foreach ($shops as $shop) {
$res &= Db::getInstance()->execute('
INSERT INTO `' . _DB_PREFIX_ . 'module_group` (`id_module`, `id_shop`, `id_group`)
(SELECT ' . (int) $id_module . ', ' . (int) $shop . ', id_group FROM `' . _DB_PREFIX_ . 'group`)');
}
return $res;
}
/**
* Return current group object
* Use context.
*
* @return Group Group object
*/
public static function getCurrent()
{
if (self::$ps_unidentified_group === null) {
self::$ps_unidentified_group = Configuration::get('PS_UNIDENTIFIED_GROUP');
}
if (self::$ps_customer_group === null) {
self::$ps_customer_group = Configuration::get('PS_CUSTOMER_GROUP');
}
$customer = Context::getContext()->customer;
if (Validate::isLoadedObject($customer)) {
$id_group = (int) $customer->id_default_group;
} else {
$id_group = (int) self::$ps_unidentified_group;
}
if (!isset(self::$groups[$id_group])) {
self::$groups[$id_group] = new Group($id_group);
}
if (!self::$groups[$id_group]->isAssociatedToShop(Context::getContext()->shop->id)) {
$id_group = (int) self::$ps_customer_group;
if (!isset(self::$groups[$id_group])) {
self::$groups[$id_group] = new Group($id_group);
}
}
return self::$groups[$id_group];
}
/**
* Light back office search for Group.
*
* @param string $query Searched string
*
* @return array Corresponding groups
*/
public static function searchByName($query)
{
return Db::getInstance()->getRow('
SELECT g.*, gl.*
FROM `' . _DB_PREFIX_ . 'group` g
LEFT JOIN `' . _DB_PREFIX_ . 'group_lang` gl
ON (g.`id_group` = gl.`id_group`)
WHERE `name` = \'' . pSQL($query) . '\'
');
}
}

288
classes/GroupReduction.php Normal file
View File

@@ -0,0 +1,288 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class GroupReductionCore extends ObjectModel
{
public $id_group;
public $id_category;
public $reduction;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'group_reduction',
'primary' => 'id_group_reduction',
'fields' => array(
'id_group' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_category' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'reduction' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice', 'required' => true),
),
);
protected static $reduction_cache = array();
public function add($autodate = true, $null_values = false)
{
return parent::add($autodate, $null_values) && $this->_setCache();
}
public function update($null_values = false)
{
return parent::update($null_values) && $this->_updateCache();
}
public function delete()
{
$products = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'
SELECT cp.`id_product`
FROM `' . _DB_PREFIX_ . 'category_product` cp
WHERE cp.`id_category` = ' . (int) $this->id_category
);
$ids = array();
foreach ($products as $row) {
$ids[] = $row['id_product'];
}
if ($ids) {
Db::getInstance()->delete('product_group_reduction_cache', 'id_product IN (' . implode(', ', $ids) . ')');
}
return parent::delete();
}
protected function _clearCache()
{
return Db::getInstance()->delete('product_group_reduction_cache', 'id_group = ' . (int) $this->id_group);
}
protected function _setCache()
{
$products = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'
SELECT cp.`id_product`
FROM `' . _DB_PREFIX_ . 'category_product` cp
WHERE cp.`id_category` = ' . (int) $this->id_category
);
$values = array();
foreach ($products as $row) {
$values[] = '(' . (int) $row['id_product'] . ', ' . (int) $this->id_group . ', ' . (float) $this->reduction . ')';
}
if (count($values)) {
$query = 'INSERT INTO `' . _DB_PREFIX_ . 'product_group_reduction_cache` (`id_product`, `id_group`, `reduction`)
VALUES ' . implode(', ', $values) . ' ON DUPLICATE KEY UPDATE
`reduction` = IF(VALUES(`reduction`) > `reduction`, VALUES(`reduction`), `reduction`)';
return Db::getInstance()->execute($query);
}
return true;
}
protected function _updateCache()
{
$products = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'
SELECT cp.`id_product`
FROM `' . _DB_PREFIX_ . 'category_product` cp
WHERE cp.`id_category` = ' . (int) $this->id_category,
false
);
$ids = array();
foreach ($products as $product) {
$ids[] = $product['id_product'];
}
$result = true;
if ($ids) {
$result &= Db::getInstance()->update('product_group_reduction_cache', array(
'reduction' => (float) $this->reduction,
), 'id_product IN(' . implode(', ', $ids) . ') AND id_group = ' . (int) $this->id_group);
}
return $result;
}
public static function getGroupReductions($id_group, $id_lang)
{
$lang = $id_lang . Shop::addSqlRestrictionOnLang('cl');
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'
SELECT gr.`id_group_reduction`, gr.`id_group`, gr.`id_category`, gr.`reduction`, cl.`name` AS category_name
FROM `' . _DB_PREFIX_ . 'group_reduction` gr
LEFT JOIN `' . _DB_PREFIX_ . 'category_lang` cl ON (cl.`id_category` = gr.`id_category` AND cl.`id_lang` = ' . (int) $lang . ')
WHERE `id_group` = ' . (int) $id_group
);
}
public static function getValueForProduct($id_product, $id_group)
{
if (!Group::isFeatureActive()) {
return 0;
}
if (!isset(self::$reduction_cache[$id_product . '-' . $id_group])) {
self::$reduction_cache[$id_product . '-' . $id_group] = Db::getInstance()->getValue('
SELECT `reduction`
FROM `' . _DB_PREFIX_ . 'product_group_reduction_cache`
WHERE `id_product` = ' . (int) $id_product . ' AND `id_group` = ' . (int) $id_group);
}
// Should return string (decimal in database) and not a float
return self::$reduction_cache[$id_product . '-' . $id_group];
}
public static function doesExist($id_group, $id_category)
{
return (bool) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
SELECT `id_group`
FROM `' . _DB_PREFIX_ . 'group_reduction`
WHERE `id_group` = ' . (int) $id_group . ' AND `id_category` = ' . (int) $id_category);
}
public static function getGroupsByCategoryId($id_category)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'
SELECT gr.`id_group` as id_group, gr.`reduction` as reduction, id_group_reduction
FROM `' . _DB_PREFIX_ . 'group_reduction` gr
WHERE `id_category` = ' . (int) $id_category
);
}
/**
* @deprecated 1.5.3.0
*
* @param int $id_category
*
* @return array|null
*/
public static function getGroupByCategoryId($id_category)
{
Tools::displayAsDeprecated('Use GroupReduction::getGroupsByCategoryId($id_category)');
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
SELECT gr.`id_group` as id_group, gr.`reduction` as reduction, id_group_reduction
FROM `' . _DB_PREFIX_ . 'group_reduction` gr
WHERE `id_category` = ' . (int) $id_category, false);
}
public static function getGroupsReductionByCategoryId($id_category)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'
SELECT gr.`id_group_reduction` as id_group_reduction, id_group
FROM `' . _DB_PREFIX_ . 'group_reduction` gr
WHERE `id_category` = ' . (int) $id_category
);
}
/**
* @deprecated 1.5.3.0
*
* @param int $id_category
*
* @return array|null
*/
public static function getGroupReductionByCategoryId($id_category)
{
Tools::displayAsDeprecated('Use GroupReduction::getGroupsByCategoryId($id_category)');
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
SELECT gr.`id_group_reduction` as id_group_reduction
FROM `' . _DB_PREFIX_ . 'group_reduction` gr
WHERE `id_category` = ' . (int) $id_category, false);
}
public static function setProductReduction($id_product, $id_group = null, $id_category = null, $reduction = null)
{
$res = true;
GroupReduction::deleteProductReduction((int) $id_product);
$categories = Product::getProductCategories((int) $id_product);
if ($categories) {
foreach ($categories as $category) {
$reductions = GroupReduction::getGroupsByCategoryId((int) $category);
if ($reductions) {
foreach ($reductions as $reduction) {
$current_group_reduction = new GroupReduction((int) $reduction['id_group_reduction']);
$res &= $current_group_reduction->_setCache();
}
}
}
}
return $res;
}
public static function deleteProductReduction($id_product)
{
$query = 'DELETE FROM `' . _DB_PREFIX_ . 'product_group_reduction_cache` WHERE `id_product` = ' . (int) $id_product;
if (Db::getInstance()->execute($query) === false) {
return false;
}
return true;
}
public static function duplicateReduction($id_product_old, $id_product)
{
$res = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'
SELECT pgr.`id_product`, pgr.`id_group`, pgr.`reduction`
FROM `' . _DB_PREFIX_ . 'product_group_reduction_cache` pgr
WHERE pgr.`id_product` = ' . (int) $id_product_old
);
if (!$res) {
return true;
}
$query = '';
foreach ($res as $row) {
$query .= 'INSERT INTO `' . _DB_PREFIX_ . 'product_group_reduction_cache` (`id_product`, `id_group`, `reduction`) VALUES ';
$query .= '(' . (int) $id_product . ', ' . (int) $row['id_group'] . ', ' . (float) $row['reduction'] . ') ON DUPLICATE KEY UPDATE `reduction` = ' . (float) $row['reduction'] . ';';
}
return Db::getInstance()->execute($query);
}
public static function deleteCategory($id_category)
{
$query = 'DELETE FROM `' . _DB_PREFIX_ . 'group_reduction` WHERE `id_category` = ' . (int) $id_category;
if (Db::getInstance()->execute($query) === false) {
return false;
}
return true;
}
}

242
classes/Guest.php Normal file
View File

@@ -0,0 +1,242 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class GuestCore.
*/
class GuestCore extends ObjectModel
{
public $id_operating_system;
public $id_web_browser;
public $id_customer;
public $javascript;
public $screen_resolution_x;
public $screen_resolution_y;
public $screen_color;
public $sun_java;
public $adobe_flash;
public $adobe_director;
public $apple_quicktime;
public $real_player;
public $windows_media;
public $accept_language;
public $mobile_theme;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'guest',
'primary' => 'id_guest',
'fields' => array(
'id_operating_system' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'id_web_browser' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'id_customer' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'javascript' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'screen_resolution_x' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
'screen_resolution_y' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
'screen_color' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
'sun_java' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'adobe_flash' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'adobe_director' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'apple_quicktime' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'real_player' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'windows_media' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'accept_language' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 8),
'mobile_theme' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
),
);
protected $webserviceParameters = array(
'fields' => array(
'id_customer' => array('xlink_resource' => 'customers'),
),
);
/**
* Set user agent.
*/
public function userAgent()
{
$userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
$acceptLanguage = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : '';
$this->accept_language = $this->getLanguage($acceptLanguage);
$this->id_operating_system = $this->getOs($userAgent);
$this->id_web_browser = $this->getBrowser($userAgent);
$this->mobile_theme = Context::getContext()->getMobileDevice();
}
/**
* Get Guest Language.
*
* @param $acceptLanguage
*
* @return mixed|string
*/
protected function getLanguage($acceptLanguage)
{
// $langsArray is filled with all the languages accepted, ordered by priority
$langsArray = array();
preg_match_all('/([a-z]{2}(-[a-z]{2})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/', $acceptLanguage, $array);
if (count($array[1])) {
$langsArray = array_combine($array[1], $array[4]);
foreach ($langsArray as $lang => $val) {
if ($val === '') {
$langsArray[$lang] = 1;
}
}
arsort($langsArray, SORT_NUMERIC);
}
// Only the first language is returned
return count($langsArray) ? key($langsArray) : '';
}
/**
* Get browser.
*
* @param string $userAgent
*/
protected function getBrowser($userAgent)
{
$browserArray = array(
'Chrome' => 'Chrome/',
'Safari' => 'Safari',
'Safari iPad' => 'iPad',
'Firefox' => 'Firefox/',
'Opera' => 'Opera',
'IE 11' => 'Trident',
'IE 10' => 'MSIE 10',
'IE 9' => 'MSIE 9',
'IE 8' => 'MSIE 8',
'IE 7' => 'MSIE 7',
'IE 6' => 'MSIE 6',
);
foreach ($browserArray as $k => $value) {
if (strstr($userAgent, $value)) {
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
SELECT `id_web_browser`
FROM `' . _DB_PREFIX_ . 'web_browser` wb
WHERE wb.`name` = \'' . pSQL($k) . '\'');
return $result['id_web_browser'];
}
}
return null;
}
/**
* Get OS.
*
* @param string $userAgent
*/
protected function getOs($userAgent)
{
$osArray = array(
'Windows 10' => 'Windows NT 10',
'Windows 8.1' => 'Windows NT 6.3',
'Windows 8' => 'Windows NT 6.2',
'Windows 7' => 'Windows NT 6.1',
'Windows Vista' => 'Windows NT 6.0',
'Windows XP' => 'Windows NT 5',
'MacOsX' => 'Mac OS X',
'Android' => 'Android',
'Linux' => 'X11',
);
foreach ($osArray as $k => $value) {
if (strstr($userAgent, $value)) {
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
SELECT `id_operating_system`
FROM `' . _DB_PREFIX_ . 'operating_system` os
WHERE os.`name` = \'' . pSQL($k) . '\'');
return $result['id_operating_system'];
}
}
return null;
}
/**
* Get Guest ID from Customer ID.
*
* @param int $idCustomer Customer ID
*
* @return bool
*/
public static function getFromCustomer($idCustomer)
{
if (!Validate::isUnsignedId($idCustomer)) {
return false;
}
$result = Db::getInstance()->getRow('
SELECT `id_guest`
FROM `' . _DB_PREFIX_ . 'guest`
WHERE `id_customer` = ' . (int) ($idCustomer));
return $result['id_guest'];
}
/**
* Merge with Customer.
*
* @param int $idGuest Guest ID
* @param int $idCustomer Customer ID
*/
public function mergeWithCustomer($idGuest, $idCustomer)
{
// Since the guests are merged, the guest id in the connections table must be changed too
Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'connections` c
SET c.`id_guest` = ' . (int) ($idGuest) . '
WHERE c.`id_guest` = ' . (int) ($this->id));
// The current guest is removed from the database
$this->delete();
// $this is still filled with values, so it's id is changed for the old guest
$this->id = (int) ($idGuest);
$this->id_customer = (int) ($idCustomer);
// $this is now the old guest but filled with the most up to date values
$this->update();
}
/**
* Set new guest.
*
* @param Cookie $cookie
*/
public static function setNewGuest($cookie)
{
$guest = new Guest(isset($cookie->id_customer) ? Guest::getFromCustomer((int) ($cookie->id_customer)) : null);
$guest->userAgent();
$guest->save();
$cookie->id_guest = (int) ($guest->id);
}
}

990
classes/Hook.php Normal file
View File

@@ -0,0 +1,990 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
use PrestaShop\PrestaShop\Core\Module\WidgetInterface;
class HookCore extends ObjectModel
{
/**
* @var string Hook name identifier
*/
public $name;
/**
* @var string Hook title (displayed in BO)
*/
public $title;
/**
* @var string Hook description
*/
public $description;
/**
* @var bool
*/
public $position = false;
/**
* @var array List of executed hooks on this page
*/
public static $executed_hooks = array();
public static $native_module;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'hook',
'primary' => 'id_hook',
'fields' => array(
'name' => array('type' => self::TYPE_STRING, 'validate' => 'isHookName', 'required' => true, 'size' => 64),
'title' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName'),
'description' => array('type' => self::TYPE_HTML, 'validate' => 'isCleanHtml'),
'position' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
),
);
/**
* @deprecated 1.5.0
*/
protected static $_hook_modules_cache = null;
/**
* @deprecated 1.5.0
*/
protected static $_hook_modules_cache_exec = null;
/**
* List of all deprecated hooks.
*
* @var array
*/
protected static $deprecated_hooks = array(
// Back office
'backOfficeFooter' => array('from' => '1.7.0.0'),
'displayBackOfficeFooter' => array('from' => '1.7.0.0'),
// Shipping step
'displayCarrierList' => array('from' => '1.7.0.0'),
'extraCarrier' => array('from' => '1.7.0.0'),
// Payment step
'hookBackBeforePayment' => array('from' => '1.7.0.0'),
'hookDisplayBeforePayment' => array('from' => '1.7.0.0'),
'hookOverrideTOSDisplay' => array('from' => '1.7.0.0'),
// Product page
'displayProductTabContent' => array('from' => '1.7.0.0'),
'displayProductTab' => array('from' => '1.7.0.0'),
// Controller
'actionAjaxDieBefore' => array('from' => '1.6.1.1'),
);
const MODULE_LIST_BY_HOOK_KEY = 'hook_module_exec_list_';
public function add($autodate = true, $null_values = false)
{
Cache::clean('hook_idsbyname');
return parent::add($autodate, $null_values);
}
public static function normalizeHookName($hookName)
{
if (strtolower($hookName) == 'displayheader') {
return 'displayHeader';
}
$hookAliasList = Hook::getHookAliasList();
if (isset($hookAliasList[strtolower($hookName)])) {
return $hookAliasList[strtolower($hookName)];
}
return $hookName;
}
public static function isDisplayHookName($hook_name)
{
$hook_name = strtolower(self::normalizeHookName($hook_name));
if ($hook_name === 'header' || $hook_name === 'displayheader') {
// this hook is to add resources to the <head> section of the page
// so it doesn't display anything by itself
return false;
}
return strpos($hook_name, 'display') === 0;
}
/**
* Return Hooks List.
*
* @param bool $position
*
* @return array Hooks List
*/
public static function getHooks($position = false, $only_display_hooks = false)
{
$hooks = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'
SELECT * FROM `' . _DB_PREFIX_ . 'hook` h
' . ($position ? 'WHERE h.`position` = 1' : '') . '
ORDER BY `name`'
);
if ($only_display_hooks) {
return array_filter($hooks, function ($hook) {
return self::isDisplayHookName($hook['name']);
});
} else {
return $hooks;
}
}
/**
* Return hook ID from name.
*
* @param string $hook_name Hook name
*
* @return int Hook ID
*/
public static function getIdByName($hook_name)
{
$hook_name = strtolower($hook_name);
if (!Validate::isHookName($hook_name)) {
return false;
}
$cache_id = 'hook_idsbyname';
if (!Cache::isStored($cache_id)) {
// Get all hook ID by name and alias
$hook_ids = array();
$db = Db::getInstance();
$result = $db->executeS('
SELECT `id_hook`, `name`
FROM `' . _DB_PREFIX_ . 'hook`
UNION
SELECT `id_hook`, ha.`alias` as name
FROM `' . _DB_PREFIX_ . 'hook_alias` ha
INNER JOIN `' . _DB_PREFIX_ . 'hook` h ON ha.name = h.name', false);
while ($row = $db->nextRow($result)) {
$hook_ids[strtolower($row['name'])] = $row['id_hook'];
}
Cache::store($cache_id, $hook_ids);
} else {
$hook_ids = Cache::retrieve($cache_id);
}
return isset($hook_ids[$hook_name]) ? $hook_ids[$hook_name] : false;
}
/**
* Return hook ID from name.
*/
public static function getNameById($hook_id)
{
$cache_id = 'hook_namebyid_' . $hook_id;
if (!Cache::isStored($cache_id)) {
$result = Db::getInstance()->getValue('
SELECT `name`
FROM `' . _DB_PREFIX_ . 'hook`
WHERE `id_hook` = ' . (int) $hook_id);
Cache::store($cache_id, $result);
return $result;
}
return Cache::retrieve($cache_id);
}
/**
* Get list of hook alias.
*
* @since 1.5.0
*
* @return array
*
* @deprecated 1.7.1.0
*/
public static function getHookAliasList()
{
$cache_id = 'hook_alias';
if (!Cache::isStored($cache_id)) {
$hook_alias_list = Db::getInstance()->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'hook_alias`');
$hook_alias = array();
if ($hook_alias_list) {
foreach ($hook_alias_list as $ha) {
$hook_alias[strtolower($ha['alias'])] = $ha['name'];
}
}
Cache::store($cache_id, $hook_alias);
return $hook_alias;
}
return Cache::retrieve($cache_id);
}
/**
* Get the list of hook aliases.
*
* @since 1.7.1.0
*
* @return array
*/
private static function getHookAliasesList()
{
$cacheId = 'hook_aliases';
if (!Cache::isStored($cacheId)) {
$hookAliasList = Db::getInstance()->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'hook_alias`');
$hookAliases = array();
if ($hookAliasList) {
foreach ($hookAliasList as $ha) {
if (!isset($hookAliases[$ha['name']])) {
$hookAliases[$ha['name']] = array();
}
$hookAliases[$ha['name']][] = $ha['alias'];
}
}
Cache::store($cacheId, $hookAliases);
return $hookAliases;
}
return Cache::retrieve($cacheId);
}
/**
* Return backward compatibility hook names.
*
* @since 1.7.1.0
*
* @param $hookName
*
* @return array
*/
private static function getHookAliasesFor($hookName)
{
$cacheId = 'hook_aliases_' . $hookName;
if (!Cache::isStored($cacheId)) {
$aliasesList = Hook::getHookAliasesList();
if (isset($aliasesList[$hookName])) {
Cache::store($cacheId, $aliasesList[$hookName]);
return $aliasesList[$hookName];
}
$retroName = array_keys(array_filter($aliasesList, function ($elem) use ($hookName) {
return in_array($hookName, $elem);
}));
if (empty($retroName)) {
Cache::store($cacheId, array());
return array();
}
Cache::store($cacheId, $retroName);
return $retroName;
}
return Cache::retrieve($cacheId);
}
/**
* Check if a hook or one of its old names is callable on a module.
*
* @since 1.7.1.0
*
* @param $module
* @param $hookName
*
* @return bool
*/
private static function isHookCallableOn($module, $hookName)
{
$aliases = Hook::getHookAliasesFor($hookName);
$aliases[] = $hookName;
return array_reduce($aliases, function ($prev, $curr) use ($module) {
return $prev || is_callable(array($module, 'hook' . $curr));
}, false);
}
/**
* Call a hook (or one of its old name) on a module.
*
* @since 1.7.1.0
*
* @param $module
* @param $hookName
* @param $hookArgs
*
* @return string
*/
private static function callHookOn($module, $hookName, $hookArgs)
{
if (is_callable(array($module, 'hook' . $hookName))) {
return Hook::coreCallHook($module, 'hook' . $hookName, $hookArgs);
}
foreach (Hook::getHookAliasesFor($hookName) as $hook) {
if (is_callable(array($module, 'hook' . $hook))) {
return Hook::coreCallHook($module, 'hook' . $hook, $hookArgs);
}
}
return '';
}
/**
* Return backward compatibility hook name.
*
* @since 1.5.0
*
* @param string $hook_name Hook name
*
* @return int Hook ID
*
* @deprecated 1.7.1.0
*/
public static function getRetroHookName($hook_name)
{
$alias_list = Hook::getHookAliasList();
if (isset($alias_list[strtolower($hook_name)])) {
return $alias_list[strtolower($hook_name)];
}
$retro_hook_name = array_search($hook_name, $alias_list);
if ($retro_hook_name === false) {
return '';
}
return $retro_hook_name;
}
/**
* Get list of all registered hooks with modules.
*
* @since 1.5.0
*
* @return array
*/
public static function getHookModuleList()
{
$cache_id = 'hook_module_list';
if (!Cache::isStored($cache_id)) {
$results = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT h.id_hook, h.name as h_name, title, description, h.position, hm.position as hm_position, m.id_module, m.name, active
FROM `' . _DB_PREFIX_ . 'hook_module` hm
STRAIGHT_JOIN `' . _DB_PREFIX_ . 'hook` h ON (h.id_hook = hm.id_hook AND hm.id_shop = ' . (int) Context::getContext()->shop->id . ')
STRAIGHT_JOIN `' . _DB_PREFIX_ . 'module` as m ON (m.id_module = hm.id_module)
ORDER BY hm.position');
$list = array();
foreach ($results as $result) {
if (!isset($list[$result['id_hook']])) {
$list[$result['id_hook']] = array();
}
$list[$result['id_hook']][$result['id_module']] = array(
'id_hook' => $result['id_hook'],
'title' => $result['title'],
'description' => $result['description'],
'hm.position' => $result['position'],
'm.position' => $result['hm_position'],
'id_module' => $result['id_module'],
'name' => $result['name'],
'active' => $result['active'],
);
}
Cache::store($cache_id, $list);
// @todo remove this in 1.6, we keep it in 1.5 for retrocompatibility
Hook::$_hook_modules_cache = $list;
return $list;
}
return Cache::retrieve($cache_id);
}
/**
* Return Hooks List.
*
* @since 1.5.0
*
* @param int $id_hook
* @param int $id_module
*
* @return array Modules List
*/
public static function getModulesFromHook($id_hook, $id_module = null)
{
$hm_list = Hook::getHookModuleList();
$module_list = (isset($hm_list[$id_hook])) ? $hm_list[$id_hook] : array();
if ($id_module) {
return (isset($module_list[$id_module])) ? array($module_list[$id_module]) : array();
}
return $module_list;
}
public static function isModuleRegisteredOnHook($module_instance, $hook_name, $id_shop)
{
$prefix = _DB_PREFIX_;
$id_hook = (int) Hook::getIdByName($hook_name);
$id_shop = (int) $id_shop;
$id_module = (int) $module_instance->id;
$sql = "SELECT * FROM {$prefix}hook_module
WHERE `id_hook` = {$id_hook}
AND `id_module` = {$id_module}
AND `id_shop` = {$id_shop}";
$rows = Db::getInstance()->executeS($sql);
return !empty($rows);
}
public static function registerHook($module_instance, $hook_name, $shop_list = null)
{
$return = true;
if (is_array($hook_name)) {
$hook_names = $hook_name;
} else {
$hook_names = array($hook_name);
}
foreach ($hook_names as $hook_name) {
// Check hook name validation and if module is installed
if (!Validate::isHookName($hook_name)) {
throw new PrestaShopException('Invalid hook name');
}
if (!isset($module_instance->id) || !is_numeric($module_instance->id)) {
return false;
}
// Retrocompatibility
$hook_name_bak = $hook_name;
if ($alias = Hook::getRetroHookName($hook_name)) {
$hook_name = $alias;
}
Hook::exec('actionModuleRegisterHookBefore', array('object' => $module_instance, 'hook_name' => $hook_name));
// Get hook id
$id_hook = Hook::getIdByName($hook_name);
// If hook does not exist, we create it
if (!$id_hook) {
$new_hook = new Hook();
$new_hook->name = pSQL($hook_name);
$new_hook->title = pSQL($hook_name);
$new_hook->position = 1;
$new_hook->add();
$id_hook = $new_hook->id;
if (!$id_hook) {
return false;
}
}
// If shop lists is null, we fill it with all shops
if (null === $shop_list) {
$shop_list = Shop::getCompleteListOfShopsID();
}
$shop_list_employee = Shop::getShops(true, null, true);
foreach ($shop_list as $shop_id) {
// Check if already register
$sql = 'SELECT hm.`id_module`
FROM `' . _DB_PREFIX_ . 'hook_module` hm, `' . _DB_PREFIX_ . 'hook` h
WHERE hm.`id_module` = ' . (int) $module_instance->id . ' AND h.`id_hook` = ' . $id_hook . '
AND h.`id_hook` = hm.`id_hook` AND `id_shop` = ' . (int) $shop_id;
if (Db::getInstance()->getRow($sql)) {
continue;
}
// Get module position in hook
$sql = 'SELECT MAX(`position`) AS position
FROM `' . _DB_PREFIX_ . 'hook_module`
WHERE `id_hook` = ' . (int) $id_hook . ' AND `id_shop` = ' . (int) $shop_id;
if (!$position = Db::getInstance()->getValue($sql)) {
$position = 0;
}
// Register module in hook
$return &= Db::getInstance()->insert('hook_module', array(
'id_module' => (int) $module_instance->id,
'id_hook' => (int) $id_hook,
'id_shop' => (int) $shop_id,
'position' => (int) ($position + 1),
));
if (!in_array($shop_id, $shop_list_employee)) {
$where = '`id_module` = ' . (int) $module_instance->id . ' AND `id_shop` = ' . (int) $shop_id;
$return &= Db::getInstance()->delete('module_shop', $where);
}
}
Hook::exec('actionModuleRegisterHookAfter', array('object' => $module_instance, 'hook_name' => $hook_name));
}
return $return;
}
public static function unregisterHook($module_instance, $hook_name, $shop_list = null)
{
if (is_numeric($hook_name)) {
// $hook_name passed it the id_hook
$hook_id = $hook_name;
$hook_name = Hook::getNameById((int) $hook_id);
} else {
$hook_id = Hook::getIdByName($hook_name);
}
if (!$hook_id) {
return false;
}
Hook::exec('actionModuleUnRegisterHookBefore', array('object' => $module_instance, 'hook_name' => $hook_name));
// Unregister module on hook by id
$sql = 'DELETE FROM `' . _DB_PREFIX_ . 'hook_module`
WHERE `id_module` = ' . (int) $module_instance->id . ' AND `id_hook` = ' . (int) $hook_id
. (($shop_list) ? ' AND `id_shop` IN(' . implode(', ', array_map('intval', $shop_list)) . ')' : '');
$result = Db::getInstance()->execute($sql);
// Clean modules position
$module_instance->cleanPositions($hook_id, $shop_list);
Hook::exec('actionModuleUnRegisterHookAfter', array('object' => $module_instance, 'hook_name' => $hook_name));
return $result;
}
/**
* Get list of modules we can execute per hook.
*
* @since 1.5.0
*
* @param string $hook_name Get list of modules for this hook if given
*
* @return array
*/
public static function getHookModuleExecList($hook_name = null)
{
$context = Context::getContext();
$cache_id = self::MODULE_LIST_BY_HOOK_KEY . (isset($context->shop->id) ? '_' . $context->shop->id : '') . ((isset($context->customer)) ? '_' . $context->customer->id : '');
if (!Cache::isStored($cache_id) || $hook_name == 'displayPayment' || $hook_name == 'displayPaymentEU' || $hook_name == 'paymentOptions' || $hook_name == 'displayBackOfficeHeader') {
$frontend = true;
$groups = array();
$use_groups = Group::isFeatureActive();
if (isset($context->employee)) {
$frontend = false;
} else {
// Get groups list
if ($use_groups) {
if (isset($context->customer) && $context->customer->isLogged()) {
$groups = $context->customer->getGroups();
} elseif (isset($context->customer) && $context->customer->isLogged(true)) {
$groups = array((int) Configuration::get('PS_GUEST_GROUP'));
} else {
$groups = array((int) Configuration::get('PS_UNIDENTIFIED_GROUP'));
}
}
}
// SQL Request
$sql = new DbQuery();
$sql->select('h.`name` as hook, m.`id_module`, h.`id_hook`, m.`name` as module');
$sql->from('module', 'm');
if ($hook_name != 'displayBackOfficeHeader') {
$sql->join(Shop::addSqlAssociation('module', 'm', true, 'module_shop.enable_device & ' . (int) Context::getContext()->getDevice()));
$sql->innerJoin('module_shop', 'ms', 'ms.`id_module` = m.`id_module`');
}
$sql->innerJoin('hook_module', 'hm', 'hm.`id_module` = m.`id_module`');
$sql->innerJoin('hook', 'h', 'hm.`id_hook` = h.`id_hook`');
if ($hook_name != 'paymentOptions') {
$sql->where('h.`name` != "paymentOptions"');
} elseif ($frontend) {
// For payment modules, we check that they are available in the contextual country
if (Validate::isLoadedObject($context->country)) {
$sql->where('((h.`name` = "displayPayment" OR h.`name` = "displayPaymentEU" OR h.`name` = "paymentOptions")AND (SELECT `id_country` FROM `' . _DB_PREFIX_ . 'module_country` mc WHERE mc.`id_module` = m.`id_module` AND `id_country` = ' . (int) $context->country->id . ' AND `id_shop` = ' . (int) $context->shop->id . ' LIMIT 1) = ' . (int) $context->country->id . ')');
}
if (Validate::isLoadedObject($context->currency)) {
$sql->where('((h.`name` = "displayPayment" OR h.`name` = "displayPaymentEU" OR h.`name` = "paymentOptions") AND (SELECT `id_currency` FROM `' . _DB_PREFIX_ . 'module_currency` mcr WHERE mcr.`id_module` = m.`id_module` AND `id_currency` IN (' . (int) $context->currency->id . ', -1, -2) LIMIT 1) IN (' . (int) $context->currency->id . ', -1, -2))');
}
if (Validate::isLoadedObject($context->cart)) {
$carrier = new Carrier($context->cart->id_carrier);
if (Validate::isLoadedObject($carrier)) {
$sql->where('((h.`name` = "displayPayment" OR h.`name` = "displayPaymentEU" OR h.`name` = "paymentOptions") AND (SELECT `id_reference` FROM `' . _DB_PREFIX_ . 'module_carrier` mcar WHERE mcar.`id_module` = m.`id_module` AND `id_reference` = ' . (int) $carrier->id_reference . ' AND `id_shop` = ' . (int) $context->shop->id . ' LIMIT 1) = ' . (int) $carrier->id_reference . ')');
}
}
}
if (Validate::isLoadedObject($context->shop)) {
$sql->where('hm.`id_shop` = ' . (int) $context->shop->id);
}
if ($frontend) {
if ($use_groups) {
$sql->leftJoin('module_group', 'mg', 'mg.`id_module` = m.`id_module`');
if (Validate::isLoadedObject($context->shop)) {
$sql->where('mg.id_shop = ' . ((int) $context->shop->id) . (count($groups) ? ' AND mg.`id_group` IN (' . implode(', ', $groups) . ')' : ''));
} elseif (count($groups)) {
$sql->where('mg.`id_group` IN (' . implode(', ', $groups) . ')');
}
}
}
$sql->groupBy('hm.id_hook, hm.id_module');
$sql->orderBy('hm.`position`');
$list = array();
if ($result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql)) {
foreach ($result as $row) {
$row['hook'] = strtolower($row['hook']);
if (!isset($list[$row['hook']])) {
$list[$row['hook']] = array();
}
$list[$row['hook']][] = array(
'id_hook' => $row['id_hook'],
'module' => $row['module'],
'id_module' => $row['id_module'],
);
}
}
if ($hook_name != 'displayPayment' && $hook_name != 'displayPaymentEU' && $hook_name != 'paymentOptions' && $hook_name != 'displayBackOfficeHeader') {
Cache::store($cache_id, $list);
// @todo remove this in 1.6, we keep it in 1.5 for backward compatibility
self::$_hook_modules_cache_exec = $list;
}
} else {
$list = Cache::retrieve($cache_id);
}
// If hook_name is given, just get list of modules for this hook
if ($hook_name) {
$retro_hook_name = strtolower(Hook::getRetroHookName($hook_name));
$hook_name = strtolower($hook_name);
$return = array();
$inserted_modules = array();
if (isset($list[$hook_name])) {
$return = $list[$hook_name];
}
foreach ($return as $module) {
$inserted_modules[] = $module['id_module'];
}
if (isset($list[$retro_hook_name])) {
foreach ($list[$retro_hook_name] as $retro_module_call) {
if (!in_array($retro_module_call['id_module'], $inserted_modules)) {
$return[] = $retro_module_call;
}
}
}
return count($return) > 0 ? $return : false;
} else {
return $list;
}
}
/**
* Execute modules for specified hook.
*
* @param string $hook_name Hook Name
* @param array $hook_args Parameters for the functions
* @param int $id_module Execute hook for this module only
* @param bool $array_return If specified, module output will be set by name in an array
* @param bool $check_exceptions Check permission exceptions
* @param bool $use_push Force change to be refreshed on Dashboard widgets
* @param int $id_shop If specified, hook will be execute the shop with this ID
* @param bool $chain If specified, hook will chain the return of hook module
*
* @throws PrestaShopException
*
* @return string/array modules output
*/
public static function exec(
$hook_name,
$hook_args = array(),
$id_module = null,
$array_return = false,
$check_exceptions = true,
$use_push = false,
$id_shop = null,
$chain = false
) {
if (defined('PS_INSTALLATION_IN_PROGRESS')) {
return;
}
$hookRegistry = self::getHookRegistry();
$isRegistryEnabled = null !== $hookRegistry;
if ($isRegistryEnabled) {
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
$hookRegistry->selectHook($hook_name, $hook_args, $backtrace[0]['file'], $backtrace[0]['line']);
}
// $chain & $array_return are incompatible so if chained is set to true, we disable the array_return option
if (true === $chain) {
$array_return = false;
}
static $disable_non_native_modules = null;
if ($disable_non_native_modules === null) {
$disable_non_native_modules = (bool) Configuration::get('PS_DISABLE_NON_NATIVE_MODULE');
}
// Check arguments validity
if (($id_module && !is_numeric($id_module)) || !Validate::isHookName($hook_name)) {
throw new PrestaShopException('Invalid id_module or hook_name');
}
// If no modules associated to hook_name or recompatible hook name, we stop the function
if (!$module_list = Hook::getHookModuleExecList($hook_name)) {
if ($isRegistryEnabled) {
$hookRegistry->collect();
}
if ($array_return) {
return array();
} else {
return '';
}
}
// Check if hook exists
if (!$id_hook = Hook::getIdByName($hook_name)) {
if ($isRegistryEnabled) {
$hookRegistry->collect();
}
if ($array_return) {
return array();
} else {
return false;
}
}
if (array_key_exists($hook_name, self::$deprecated_hooks)) {
$deprecVersion = isset(self::$deprecated_hooks[$hook_name]['from']) ?
self::$deprecated_hooks[$hook_name]['from'] :
_PS_VERSION_;
Tools::displayAsDeprecated('The hook ' . $hook_name . ' is deprecated in PrestaShop v.' . $deprecVersion);
}
// Store list of executed hooks on this page
Hook::$executed_hooks[$id_hook] = $hook_name;
$context = Context::getContext();
if (!isset($hook_args['cookie']) || !$hook_args['cookie']) {
$hook_args['cookie'] = $context->cookie;
}
if (!isset($hook_args['cart']) || !$hook_args['cart']) {
$hook_args['cart'] = $context->cart;
}
// Look on modules list
$altern = 0;
if ($array_return) {
$output = array();
} else {
$output = '';
}
if ($disable_non_native_modules && !isset(Hook::$native_module)) {
Hook::$native_module = Module::getNativeModuleList();
}
$different_shop = false;
if ($id_shop !== null && Validate::isUnsignedId($id_shop) && $id_shop != $context->shop->getContextShopID()) {
$old_context = $context->shop->getContext();
$old_shop = clone $context->shop;
$shop = new Shop((int) $id_shop);
if (Validate::isLoadedObject($shop)) {
$context->shop = $shop;
$context->shop->setContext(Shop::CONTEXT_SHOP, $shop->id);
$different_shop = true;
}
}
foreach ($module_list as $key => $array) {
// Check errors
if ($id_module && $id_module != $array['id_module']) {
continue;
}
if ((bool) $disable_non_native_modules && Hook::$native_module && count(Hook::$native_module) && !in_array($array['module'], Hook::$native_module)) {
continue;
}
// Check permissions
if ($check_exceptions) {
$exceptions = Module::getExceptionsStatic($array['id_module'], $array['id_hook']);
$controller_obj = Context::getContext()->controller;
if ($controller_obj === null) {
$controller = null;
} else {
$controller = isset($controller_obj->controller_name) ?
$controller_obj->controller_name :
$controller_obj->php_self;
}
//check if current controller is a module controller
if (isset($controller_obj->module) && Validate::isLoadedObject($controller_obj->module)) {
$controller = 'module-' . $controller_obj->module->name . '-' . $controller;
}
if (in_array($controller, $exceptions)) {
continue;
}
//Backward compatibility of controller names
$matching_name = array(
'authentication' => 'auth',
);
if (isset($matching_name[$controller]) && in_array($matching_name[$controller], $exceptions)) {
continue;
}
if (Validate::isLoadedObject($context->employee) && !Module::getPermissionStatic($array['id_module'], 'view', $context->employee)) {
continue;
}
}
if (!($moduleInstance = Module::getInstanceByName($array['module']))) {
continue;
}
if ($use_push && !$moduleInstance->allow_push) {
continue;
}
if ($isRegistryEnabled) {
$hookRegistry->hookedByModule($moduleInstance);
}
if (Hook::isHookCallableOn($moduleInstance, $hook_name)) {
$hook_args['altern'] = ++$altern;
if ($use_push && isset($moduleInstance->push_filename) && file_exists($moduleInstance->push_filename)) {
Tools::waitUntilFileIsModified($moduleInstance->push_filename, $moduleInstance->push_time_limit);
}
if (0 !== $key && true === $chain) {
$hook_args = $output;
}
$display = Hook::callHookOn($moduleInstance, $hook_name, $hook_args);
if ($array_return) {
$output[$moduleInstance->name] = $display;
} else {
if (true === $chain) {
$output = $display;
} else {
$output .= $display;
}
}
if ($isRegistryEnabled) {
$hookRegistry->hookedByCallback($moduleInstance, $hook_args);
}
} elseif (Hook::isDisplayHookName($hook_name)) {
if ($moduleInstance instanceof WidgetInterface) {
if (0 !== $key && true === $chain) {
$hook_args = $output;
}
$display = Hook::coreRenderWidget($moduleInstance, $hook_name, $hook_args);
if ($array_return) {
$output[$moduleInstance->name] = $display;
} else {
if (true === $chain) {
$output = $display;
} else {
$output .= $display;
}
}
}
if ($isRegistryEnabled) {
$hookRegistry->hookedByWidget($moduleInstance, $hook_args);
}
}
}
if ($different_shop) {
$context->shop = $old_shop;
$context->shop->setContext($old_context, $shop->id);
}
if (true === $chain) {
if (isset($output['cookie'])) {
unset($output['cookie']);
}
if (isset($output['cart'])) {
unset($output['cart']);
}
}
if ($isRegistryEnabled) {
$hookRegistry->hookWasCalled();
$hookRegistry->collect();
}
return $output;
}
public static function coreCallHook($module, $method, $params)
{
return $module->{$method}($params);
}
public static function coreRenderWidget($module, $hook_name, $params)
{
return $module->renderWidget($hook_name, $params);
}
/**
* @return \PrestaShopBundle\DataCollector\HookRegistry|null
*/
private static function getHookRegistry()
{
$sfContainer = SymfonyContainer::getInstance();
if (null !== $sfContainer && 'dev' === $sfContainer->getParameter('kernel.environment')) {
return $sfContainer->get('prestashop.hooks_registry');
}
return null;
}
}

874
classes/Image.php Normal file
View File

@@ -0,0 +1,874 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ImageCore.
*/
class ImageCore extends ObjectModel
{
public $id;
/** @var int Image ID */
public $id_image;
/** @var int Product ID */
public $id_product;
/** @var int Position used to order images of the same product */
public $position;
/** @var bool Image is cover */
public $cover;
/** @var string Legend */
public $legend;
/** @var string image extension */
public $image_format = 'jpg';
/** @var string path to index.php file to be copied to new image folders */
public $source_index;
/** @var string image folder */
protected $folder;
/** @var string image path without extension */
protected $existing_path;
/** @var int access rights of created folders (octal) */
protected static $access_rights = 0775;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'image',
'primary' => 'id_image',
'multilang' => true,
'fields' => array(
'id_product' => array('type' => self::TYPE_INT, 'shop' => 'both', 'validate' => 'isUnsignedId', 'required' => true),
'position' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
'cover' => array('type' => self::TYPE_BOOL, 'allow_null' => true, 'validate' => 'isBool', 'shop' => true),
'legend' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 128),
),
);
protected static $_cacheGetSize = array();
/**
* ImageCore constructor.
*
* @param null $id
* @param null $idLang
*/
public function __construct($id = null, $idLang = null)
{
parent::__construct($id, $idLang);
$this->image_dir = _PS_PROD_IMG_DIR_;
$this->source_index = _PS_PROD_IMG_DIR_ . 'index.php';
}
/**
* Adds current Image 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 Image has been successfully added
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function add($autoDate = true, $nullValues = false)
{
if ($this->position <= 0) {
$this->position = Image::getHighestPosition($this->id_product) + 1;
}
if ($this->cover) {
$this->cover = 1;
} else {
$this->cover = null;
}
return parent::add($autoDate, $nullValues);
}
/**
* Updates the current Image in the database.
*
* @param bool $nullValues Whether we want to use NULL values instead of empty quotes values
*
* @return bool Indicates whether the Image has been successfully updated
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function update($nullValues = false)
{
if ($this->cover) {
$this->cover = 1;
} else {
$this->cover = null;
}
return parent::update($nullValues);
}
/**
* Deletes current Image from the database.
*
* @return bool `true` if delete was successful
*
* @throws PrestaShopException
*/
public function delete()
{
if (!parent::delete()) {
return false;
}
if ($this->hasMultishopEntries()) {
return true;
}
if (!$this->deleteProductAttributeImage() || !$this->deleteImage()) {
return false;
}
// update positions
Db::getInstance()->execute('SET @position:=0', false);
Db::getInstance()->execute('UPDATE `' . _DB_PREFIX_ . 'image` SET position=(@position:=@position+1)
WHERE `id_product` = ' . (int) $this->id_product . ' ORDER BY position ASC');
return true;
}
/**
* Return first image (by position) associated with a product attribute.
*
* @param int $idShop Shop ID
* @param int $idLang Language ID
* @param int $idProduct Product ID
* @param int $idProductAttribute Product Attribute ID
*
* @return array
*/
public static function getBestImageAttribute($idShop, $idLang, $idProduct, $idProductAttribute)
{
$cacheId = 'Image::getBestImageAttribute' . '-' . (int) $idProduct . '-' . (int) $idProductAttribute . '-' . (int) $idLang . '-' . (int) $idShop;
if (!Cache::isStored($cacheId)) {
$row = Db::getInstance()->getRow('
SELECT image_shop.`id_image` id_image, il.`legend`
FROM `' . _DB_PREFIX_ . 'image` i
INNER JOIN `' . _DB_PREFIX_ . 'image_shop` image_shop
ON (i.id_image = image_shop.id_image AND image_shop.id_shop = ' . (int) $idShop . ')
INNER JOIN `' . _DB_PREFIX_ . 'product_attribute_image` pai
ON (pai.`id_image` = i.`id_image` AND pai.`id_product_attribute` = ' . (int) $idProductAttribute . ')
LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il
ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $idLang . ')
WHERE i.`id_product` = ' . (int) $idProduct . ' ORDER BY i.`position` ASC');
Cache::store($cacheId, $row);
} else {
$row = Cache::retrieve($cacheId);
}
return $row;
}
/**
* Return available images for a product.
*
* @param int $idLang Language ID
* @param int $idProduct Product ID
* @param int $idProductAttribute Product Attribute ID
*
* @return array Images
*/
public static function getImages($idLang, $idProduct, $idProductAttribute = null)
{
$attributeFilter = ($idProductAttribute ? ' AND ai.`id_product_attribute` = ' . (int) $idProductAttribute : '');
$sql = 'SELECT *
FROM `' . _DB_PREFIX_ . 'image` i
LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (i.`id_image` = il.`id_image`)';
if ($idProductAttribute) {
$sql .= ' LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_image` ai ON (i.`id_image` = ai.`id_image`)';
}
$sql .= ' WHERE i.`id_product` = ' . (int) $idProduct . ' AND il.`id_lang` = ' . (int) $idLang . $attributeFilter . '
ORDER BY i.`position` ASC';
return Db::getInstance()->executeS($sql);
}
/**
* Check if a product has an image available.
*
* @param int $idLang Language ID
* @param int $idProduct Product ID
* @param int $idProductAttribute Product Attribute ID
*
* @return bool
*/
public static function hasImages($idLang, $idProduct, $idProductAttribute = null)
{
$attribute_filter = ($idProductAttribute ? ' AND ai.`id_product_attribute` = ' . (int) $idProductAttribute : '');
$sql = 'SELECT 1
FROM `' . _DB_PREFIX_ . 'image` i
LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (i.`id_image` = il.`id_image`)';
if ($idProductAttribute) {
$sql .= ' LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_image` ai ON (i.`id_image` = ai.`id_image`)';
}
$sql .= ' WHERE i.`id_product` = ' . (int) $idProduct . ' AND il.`id_lang` = ' . (int) $idLang . $attribute_filter;
return (bool) Db::getInstance()->getValue($sql);
}
/**
* Return Images.
*
* @return array Images
*/
public static function getAllImages()
{
return Db::getInstance()->executeS('
SELECT `id_image`, `id_product`
FROM `' . _DB_PREFIX_ . 'image`
ORDER BY `id_image` ASC');
}
/**
* Return number of images for a product.
*
* @param int $idProduct Product ID
*
* @return int number of images
*/
public static function getImagesTotal($idProduct)
{
$result = Db::getInstance()->getRow('
SELECT COUNT(`id_image`) AS total
FROM `' . _DB_PREFIX_ . 'image`
WHERE `id_product` = ' . (int) $idProduct);
return $result['total'];
}
/**
* Return highest position of images for a product.
*
* @param int $idProduct Product ID
*
* @return int highest position of images
*/
public static function getHighestPosition($idProduct)
{
$result = Db::getInstance()->getRow('
SELECT MAX(`position`) AS max
FROM `' . _DB_PREFIX_ . 'image`
WHERE `id_product` = ' . (int) $idProduct);
return $result['max'];
}
/**
* Delete product cover.
*
* @param int $idProduct Product ID
*
* @return bool result
*/
public static function deleteCover($idProduct)
{
if (!Validate::isUnsignedId($idProduct)) {
die(Tools::displayError());
}
if (file_exists(_PS_TMP_IMG_DIR_ . 'product_' . $idProduct . '.jpg')) {
unlink(_PS_TMP_IMG_DIR_ . 'product_' . $idProduct . '.jpg');
}
return Db::getInstance()->execute(
'
UPDATE `' . _DB_PREFIX_ . 'image`
SET `cover` = NULL
WHERE `id_product` = ' . (int) $idProduct
) &&
Db::getInstance()->execute(
'
UPDATE `' . _DB_PREFIX_ . 'image_shop` image_shop
SET image_shop.`cover` = NULL
WHERE image_shop.id_shop IN (' . implode(',', array_map('intval', Shop::getContextListShopID())) . ') AND image_shop.`id_product` = ' . (int) $idProduct
);
}
/**
*Get product cover.
*
* @param int $idProduct Product ID
*
* @return bool result
*/
public static function getCover($idProduct)
{
return Db::getInstance()->getRow('
SELECT * FROM `' . _DB_PREFIX_ . 'image_shop` image_shop
WHERE image_shop.`id_product` = ' . (int) $idProduct . '
AND image_shop.`cover`= 1');
}
/**
*Get global product cover.
*
* @param int $idProduct Product ID
*
* @return bool result
*/
public static function getGlobalCover($idProduct)
{
return Db::getInstance()->getRow('
SELECT * FROM `' . _DB_PREFIX_ . 'image` i
WHERE i.`id_product` = ' . (int) $idProduct . '
AND i.`cover`= 1');
}
/**
* Copy images from a product to another.
*
* @param int $idProductOld Source product ID
* @param bool $idProductNew Destination product ID
*/
public static function duplicateProductImages($idProductOld, $idProductNew, $combinationImages)
{
$imagesTypes = ImageType::getImagesTypes('products');
$result = Db::getInstance()->executeS('
SELECT `id_image`
FROM `' . _DB_PREFIX_ . 'image`
WHERE `id_product` = ' . (int) $idProductOld);
foreach ($result as $row) {
$imageOld = new Image($row['id_image']);
$imageNew = clone $imageOld;
unset($imageNew->id);
$imageNew->id_product = (int) $idProductNew;
// A new id is generated for the cloned image when calling add()
if ($imageNew->add()) {
$newPath = $imageNew->getPathForCreation();
foreach ($imagesTypes as $imageType) {
if (file_exists(_PS_PROD_IMG_DIR_ . $imageOld->getExistingImgPath() . '-' . $imageType['name'] . '.jpg')) {
if (!Configuration::get('PS_LEGACY_IMAGES')) {
$imageNew->createImgFolder();
}
copy(
_PS_PROD_IMG_DIR_ . $imageOld->getExistingImgPath() . '-' . $imageType['name'] . '.jpg',
$newPath . '-' . $imageType['name'] . '.jpg'
);
if (Configuration::get('WATERMARK_HASH')) {
$oldImagePath = _PS_PROD_IMG_DIR_ . $imageOld->getExistingImgPath() . '-' . $imageType['name'] . '-' . Configuration::get('WATERMARK_HASH') . '.jpg';
if (file_exists($oldImagePath)) {
copy($oldImagePath, $newPath . '-' . $imageType['name'] . '-' . Configuration::get('WATERMARK_HASH') . '.jpg');
}
}
}
}
if (file_exists(_PS_PROD_IMG_DIR_ . $imageOld->getExistingImgPath() . '.jpg')) {
copy(_PS_PROD_IMG_DIR_ . $imageOld->getExistingImgPath() . '.jpg', $newPath . '.jpg');
}
Image::replaceAttributeImageAssociationId($combinationImages, (int) $imageOld->id, (int) $imageNew->id);
// Duplicate shop associations for images
$imageNew->duplicateShops($idProductOld);
} else {
return false;
}
}
return Image::duplicateAttributeImageAssociations($combinationImages);
}
/**
* @param array $combinationImages
* @param int $savedId
* @param int $idImage
*/
protected static function replaceAttributeImageAssociationId(&$combinationImages, $savedId, $idImage)
{
if (!isset($combinationImages['new']) || !is_array($combinationImages['new'])) {
return;
}
foreach ($combinationImages['new'] as $id_product_attribute => $image_ids) {
foreach ($image_ids as $key => $imageId) {
if ((int) $imageId == (int) $savedId) {
$combinationImages['new'][$id_product_attribute][$key] = (int) $idImage;
}
}
}
}
/**
* Duplicate product attribute image associations.
*
* @param array $combinationImages
*
* @return bool
*/
public static function duplicateAttributeImageAssociations($combinationImages)
{
if (!isset($combinationImages['new']) || !is_array($combinationImages['new'])) {
return true;
}
$query = 'INSERT INTO `' . _DB_PREFIX_ . 'product_attribute_image` (`id_product_attribute`, `id_image`) VALUES ';
foreach ($combinationImages['new'] as $idProductAttribute => $imageIds) {
foreach ($imageIds as $imageId) {
$query .= '(' . (int) $idProductAttribute . ', ' . (int) $imageId . '), ';
}
}
$query = rtrim($query, ', ');
return Db::getInstance()->execute($query);
}
/**
* Change an image position and update relative positions.
*
* @param int $way position is moved up if 0, moved down if 1
* @param int $position new position of the moved image
*
* @return int success
*/
public function updatePosition($way, $position)
{
if (!isset($this->id) || !$position) {
return false;
}
// < and > statements rather than BETWEEN operator
// since BETWEEN is treated differently according to databases
$result = (Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'image`
SET `position`= `position` ' . ($way ? '- 1' : '+ 1') . '
WHERE `position`
' . ($way
? '> ' . (int) $this->position . ' AND `position` <= ' . (int) $position
: '< ' . (int) $this->position . ' AND `position` >= ' . (int) $position) . '
AND `id_product`=' . (int) $this->id_product)
&& Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'image`
SET `position` = ' . (int) $position . '
WHERE `id_image` = ' . (int) $this->id_image));
return $result;
}
/**
* @param string $type
*
* @return mixed
*/
public static function getSize($type)
{
if (!isset(self::$_cacheGetSize[$type]) || self::$_cacheGetSize[$type] === null) {
self::$_cacheGetSize[$type] = Db::getInstance()->getRow('
SELECT `width`, `height`
FROM ' . _DB_PREFIX_ . 'image_type
WHERE `name` = \'' . pSQL($type) . '\'
');
}
return self::$_cacheGetSize[$type];
}
/**
* @param array $params
*
* @return mixed
*/
public static function getWidth($params)
{
$result = self::getSize($params['type']);
return $result['width'];
}
/**
* @param array $params
*
* @return mixed
*/
public static function getHeight($params)
{
$result = self::getSize($params['type']);
return $result['height'];
}
/**
* Clear all images in tmp dir.
*/
public static function clearTmpDir()
{
foreach (scandir(_PS_TMP_IMG_DIR_, SCANDIR_SORT_NONE) as $d) {
if (preg_match('/(.*)\.jpg$/', $d)) {
unlink(_PS_TMP_IMG_DIR_ . $d);
}
}
}
/**
* Delete Image - Product attribute associations for this image.
*/
public function deleteProductAttributeImage()
{
return Db::getInstance()->execute(
'
DELETE
FROM `' . _DB_PREFIX_ . 'product_attribute_image`
WHERE `id_image` = ' . (int) $this->id
);
}
/**
* Delete the product image from disk and remove the containing folder if empty
* Handles both legacy and new image filesystems.
*/
public function deleteImage($forceDelete = false)
{
if (!$this->id) {
return false;
}
// Delete base image
if (file_exists($this->image_dir . $this->getExistingImgPath() . '.' . $this->image_format)) {
unlink($this->image_dir . $this->getExistingImgPath() . '.' . $this->image_format);
} else {
return false;
}
$filesToDelete = array();
// Delete auto-generated images
$image_types = ImageType::getImagesTypes();
foreach ($image_types as $imageType) {
$filesToDelete[] = $this->image_dir . $this->getExistingImgPath() . '-' . $imageType['name'] . '.' . $this->image_format;
if (Configuration::get('WATERMARK_HASH')) {
$filesToDelete[] = $this->image_dir . $this->getExistingImgPath() . '-' . $imageType['name'] . '-' . Configuration::get('WATERMARK_HASH') . '.' . $this->image_format;
}
}
// Delete watermark image
$filesToDelete[] = $this->image_dir . $this->getExistingImgPath() . '-watermark.' . $this->image_format;
// delete index.php
$filesToDelete[] = $this->image_dir . $this->getImgFolder() . 'index.php';
// Delete tmp images
$filesToDelete[] = _PS_TMP_IMG_DIR_ . 'product_' . $this->id_product . '.' . $this->image_format;
$filesToDelete[] = _PS_TMP_IMG_DIR_ . 'product_mini_' . $this->id_product . '.' . $this->image_format;
foreach ($filesToDelete as $file) {
if (file_exists($file) && !@unlink($file)) {
return false;
}
}
// Can we delete the image folder?
if (is_dir($this->image_dir . $this->getImgFolder())) {
$deleteFolder = true;
foreach (scandir($this->image_dir . $this->getImgFolder(), SCANDIR_SORT_NONE) as $file) {
if (($file != '.' && $file != '..')) {
$deleteFolder = false;
break;
}
}
}
if (isset($deleteFolder) && $deleteFolder) {
@rmdir($this->image_dir . $this->getImgFolder());
}
return true;
}
/**
* Recursively deletes all product images in the given folder tree and removes empty folders.
*
* @param string $path folder containing the product images to delete
* @param string $format image format
*
* @return bool success
*/
public static function deleteAllImages($path, $format = 'jpg')
{
if (!$path || !$format || !is_dir($path)) {
return false;
}
foreach (scandir($path, SCANDIR_SORT_NONE) as $file) {
if (preg_match('/^[0-9]+(\-(.*))?\.' . $format . '$/', $file)) {
unlink($path . $file);
} elseif (is_dir($path . $file) && (preg_match('/^[0-9]$/', $file))) {
Image::deleteAllImages($path . $file . '/', $format);
}
}
// Can we remove the image folder?
if (is_numeric(basename($path))) {
$removeFolder = true;
foreach (scandir($path, SCANDIR_SORT_NONE) as $file) {
if (($file != '.' && $file != '..' && $file != 'index.php')) {
$removeFolder = false;
break;
}
}
if ($removeFolder) {
// we're only removing index.php if it's a folder we want to delete
if (file_exists($path . 'index.php')) {
@unlink($path . 'index.php');
}
@rmdir($path);
}
}
return true;
}
/**
* Returns image path in the old or in the new filesystem.
*
* @ returns string image path
*/
public function getExistingImgPath()
{
if (!$this->id) {
return false;
}
if (!$this->existing_path) {
if (Configuration::get('PS_LEGACY_IMAGES') && file_exists(_PS_PROD_IMG_DIR_ . $this->id_product . '-' . $this->id . '.' . $this->image_format)) {
$this->existing_path = $this->id_product . '-' . $this->id;
} else {
$this->existing_path = $this->getImgPath();
}
}
return $this->existing_path;
}
/**
* Returns the path to the folder containing the image in the new filesystem.
*
* @return string path to folder
*/
public function getImgFolder()
{
if (!$this->id) {
return false;
}
if (!$this->folder) {
$this->folder = Image::getImgFolderStatic($this->id);
}
return $this->folder;
}
/**
* Create parent folders for the image in the new filesystem.
*
* @return bool success
*/
public function createImgFolder()
{
if (!$this->id) {
return false;
}
if (!file_exists(_PS_PROD_IMG_DIR_ . $this->getImgFolder())) {
// Apparently sometimes mkdir cannot set the rights, and sometimes chmod can't. Trying both.
$success = @mkdir(_PS_PROD_IMG_DIR_ . $this->getImgFolder(), self::$access_rights, true);
$chmod = @chmod(_PS_PROD_IMG_DIR_ . $this->getImgFolder(), self::$access_rights);
// Create an index.php file in the new folder
if (($success || $chmod)
&& !file_exists(_PS_PROD_IMG_DIR_ . $this->getImgFolder() . 'index.php')
&& file_exists($this->source_index)) {
return @copy($this->source_index, _PS_PROD_IMG_DIR_ . $this->getImgFolder() . 'index.php');
}
}
return true;
}
/**
* Returns the path to the image without file extension.
*
* @return string path
*/
public function getImgPath()
{
if (!$this->id) {
return false;
}
$path = $this->getImgFolder() . $this->id;
return $path;
}
/**
* Returns the path to the folder containing the image in the new filesystem.
*
* @param mixed $idImage
*
* @return string path to folder
*/
public static function getImgFolderStatic($idImage)
{
if (!is_numeric($idImage)) {
return false;
}
$folders = str_split((string) $idImage);
return implode('/', $folders) . '/';
}
/**
* Move all legacy product image files from the image folder root to their subfolder in the new filesystem.
* If max_execution_time is provided, stops before timeout and returns string "timeout".
* If any image cannot be moved, stops and returns "false".
*
* @param int $maxExecutionTime
*
* @return mixed success or timeout
*/
public static function moveToNewFileSystem($maxExecutionTime = 0)
{
$startTime = time();
$image = null;
$tmpFolder = 'duplicates/';
foreach (scandir(_PS_PROD_IMG_DIR_, SCANDIR_SORT_NONE) as $file) {
// matches the base product image or the thumbnails
if (preg_match('/^([0-9]+\-)([0-9]+)(\-(.*))?\.jpg$/', $file, $matches)) {
// don't recreate an image object for each image type
if (!$image || $image->id !== (int) $matches[2]) {
$image = new Image((int) $matches[2]);
}
// image exists in DB and with the correct product?
if (Validate::isLoadedObject($image) && $image->id_product == (int) rtrim($matches[1], '-')) {
// create the new folder if it does not exist
if (!$image->createImgFolder()) {
return false;
}
// if there's already a file at the new image path, move it to a dump folder
// most likely the preexisting image is a demo image not linked to a product and it's ok to replace it
$newPath = _PS_PROD_IMG_DIR_ . $image->getImgPath() . (isset($matches[3]) ? $matches[3] : '') . '.jpg';
if (file_exists($newPath)) {
if (!file_exists(_PS_PROD_IMG_DIR_ . $tmpFolder)) {
@mkdir(_PS_PROD_IMG_DIR_ . $tmpFolder, self::$access_rights);
@chmod(_PS_PROD_IMG_DIR_ . $tmpFolder, self::$access_rights);
}
$tmpPath = _PS_PROD_IMG_DIR_ . $tmpFolder . basename($file);
if (!@rename($newPath, $tmpPath) || !file_exists($tmpPath)) {
return false;
}
}
// move the image
if (!@rename(_PS_PROD_IMG_DIR_ . $file, $newPath) || !file_exists($newPath)) {
return false;
}
}
}
if ((int) $maxExecutionTime != 0 && (time() - $startTime > (int) $maxExecutionTime - 4)) {
return 'timeout';
}
}
return true;
}
/**
* Try to create and delete some folders to check if moving images to new file system will be possible.
*
* @return bool success
*/
public static function testFileSystem()
{
$folder1 = _PS_PROD_IMG_DIR_ . 'testfilesystem/';
$testFolder = $folder1 . 'testsubfolder/';
// check if folders are already existing from previous failed test
if (file_exists($testFolder)) {
@rmdir($testFolder);
@rmdir($folder1);
}
if (file_exists($testFolder)) {
return false;
}
@mkdir($testFolder, self::$access_rights, true);
@chmod($testFolder, self::$access_rights);
if (!is_writable($testFolder)) {
return false;
}
@rmdir($testFolder);
@rmdir($folder1);
if (file_exists($folder1)) {
return false;
}
return true;
}
/**
* Returns the path where a product image should be created (without file format).
*
* @return string path
*/
public function getPathForCreation()
{
if (!$this->id) {
return false;
}
if (Configuration::get('PS_LEGACY_IMAGES')) {
if (!$this->id_product) {
return false;
}
$path = $this->id_product . '-' . $this->id;
} else {
$path = $this->getImgPath();
$this->createImgFolder();
}
return _PS_PROD_IMG_DIR_ . $path;
}
}

657
classes/ImageManager.php Normal file
View File

@@ -0,0 +1,657 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ImageManagerCore.
*
* This class includes functions for image manipulation
*
* @since 1.5.0
*/
class ImageManagerCore
{
const ERROR_FILE_NOT_EXIST = 1;
const ERROR_FILE_WIDTH = 2;
const ERROR_MEMORY_LIMIT = 3;
/**
* Generate a cached thumbnail for object lists (eg. carrier, order statuses...etc).
*
* @param string $image Real image filename
* @param string $cacheImage Cached filename
* @param int $size Desired size
* @param string $imageType Image type
* @param bool $disableCache When turned on a timestamp will be added to the image URI to disable the HTTP cache
* @param bool $regenerate When turned on and the file already exist, the file will be regenerated
*
*@return string
*/
public static function thumbnail($image, $cacheImage, $size, $imageType = 'jpg', $disableCache = true, $regenerate = false)
{
if (!file_exists($image)) {
return '';
}
if (file_exists(_PS_TMP_IMG_DIR_ . $cacheImage) && $regenerate) {
@unlink(_PS_TMP_IMG_DIR_ . $cacheImage);
}
if ($regenerate || !file_exists(_PS_TMP_IMG_DIR_ . $cacheImage)) {
$infos = getimagesize($image);
// Evaluate the memory required to resize the image: if it's too much, you can't resize it.
if (!ImageManager::checkImageMemoryLimit($image)) {
return false;
}
$x = $infos[0];
$y = $infos[1];
$maxX = $size * 3;
// Size is already ok
if ($y < $size && $x <= $maxX) {
copy($image, _PS_TMP_IMG_DIR_ . $cacheImage);
} else {
// We need to resize */
$ratioX = $x / ($y / $size);
if ($ratioX > $maxX) {
$ratioX = $maxX;
$size = $y / ($x / $maxX);
}
ImageManager::resize($image, _PS_TMP_IMG_DIR_ . $cacheImage, $ratioX, $size, $imageType);
}
}
return '<img src="' . self::getThumbnailPath($cacheImage, $disableCache) . '" alt="" class="imgm img-thumbnail" />';
}
/**
* @param $cacheImage
* @param $disableCache
*
* @return string
*/
public static function getThumbnailPath($cacheImage, $disableCache)
{
$cacheParam = $disableCache ? '?time=' . time() : '';
if (Context::getContext()->controller->controller_type == 'admin') {
return '../img/tmp/' . $cacheImage . $cacheParam;
}
return _PS_TMP_IMG_ . $cacheImage . $cacheParam;
}
/**
* Check if memory limit is too long or not.
*
* @param mixed $image
*
* @return bool
*/
public static function checkImageMemoryLimit($image)
{
$infos = @getimagesize($image);
if (!is_array($infos) || !isset($infos['bits'])) {
return true;
}
$memoryLimit = Tools::getMemoryLimit();
// memory_limit == -1 => unlimited memory
if (isset($infos['bits']) && function_exists('memory_get_usage') && (int) $memoryLimit != -1) {
$currentMemory = memory_get_usage();
$bits = $infos['bits'] / 8;
$channel = isset($infos['channels']) ? $infos['channels'] : 1;
// Evaluate the memory required to resize the image: if it's too much, you can't resize it.
// For perfs, avoid computing static maths formulas in the code. pow(2, 16) = 65536 ; 1024 * 1024 = 1048576
if (($infos[0] * $infos[1] * $bits * $channel + 65536) * 1.8 + $currentMemory > $memoryLimit - 1048576) {
return false;
}
}
return true;
}
/**
* Resize, cut and optimize image.
*
* @param string $sourceFile Image object from $_FILE
* @param string $destinationFile Destination filename
* @param int $destinationWidth Desired width (optional)
* @param int $destinationHeight Desired height (optional)
* @param string $fileType Desired file_type (may be override by PS_IMAGE_QUALITY)
* @param bool $forceType Don't override $file_type
* @param int $error Out error code
* @param int $targetWidth Needed by AdminImportController to speed up the import process
* @param int $targetHeight Needed by AdminImportController to speed up the import process
* @param int $quality Needed by AdminImportController to speed up the import process
* @param int $sourceWidth Needed by AdminImportController to speed up the import process
* @param int $sourceHeight Needed by AdminImportController to speed up the import process
*
*@return bool Operation result
*/
public static function resize(
$sourceFile,
$destinationFile,
$destinationWidth = null,
$destinationHeight = null,
$fileType = 'jpg',
$forceType = false,
&$error = 0,
&$targetWidth = null,
&$targetHeight = null,
$quality = 5,
&$sourceWidth = null,
&$sourceHeight = null
) {
clearstatcache(true, $sourceFile);
if (!file_exists($sourceFile) || !filesize($sourceFile)) {
return !($error = self::ERROR_FILE_NOT_EXIST);
}
list($tmpWidth, $tmpHeight, $type) = getimagesize($sourceFile);
$rotate = 0;
if (function_exists('exif_read_data') && function_exists('mb_strtolower')) {
$exif = @exif_read_data($sourceFile);
if ($exif && isset($exif['Orientation'])) {
switch ($exif['Orientation']) {
case 3:
$sourceWidth = $tmpWidth;
$sourceHeight = $tmpHeight;
$rotate = 180;
break;
case 6:
$sourceWidth = $tmpHeight;
$sourceHeight = $tmpWidth;
$rotate = -90;
break;
case 8:
$sourceWidth = $tmpHeight;
$sourceHeight = $tmpWidth;
$rotate = 90;
break;
default:
$sourceWidth = $tmpWidth;
$sourceHeight = $tmpHeight;
}
} else {
$sourceWidth = $tmpWidth;
$sourceHeight = $tmpHeight;
}
} else {
$sourceWidth = $tmpWidth;
$sourceHeight = $tmpHeight;
}
// If PS_IMAGE_QUALITY is activated, the generated image will be a PNG with .jpg as a file extension.
// This allow for higher quality and for transparency. JPG source files will also benefit from a higher quality
// because JPG reencoding by GD, even with max quality setting, degrades the image.
if (Configuration::get('PS_IMAGE_QUALITY') == 'png_all'
|| (Configuration::get('PS_IMAGE_QUALITY') == 'png' && $type == IMAGETYPE_PNG) && !$forceType) {
$fileType = 'png';
}
if (!$sourceWidth) {
return !($error = self::ERROR_FILE_WIDTH);
}
if (!$destinationWidth) {
$destinationWidth = $sourceWidth;
}
if (!$destinationHeight) {
$destinationHeight = $sourceHeight;
}
$widthDiff = $destinationWidth / $sourceWidth;
$heightDiff = $destinationHeight / $sourceHeight;
$psImageGenerationMethod = Configuration::get('PS_IMAGE_GENERATION_METHOD');
if ($widthDiff > 1 && $heightDiff > 1) {
$nextWidth = $sourceWidth;
$nextHeight = $sourceHeight;
} else {
if ($psImageGenerationMethod == 2 || (!$psImageGenerationMethod && $widthDiff > $heightDiff)) {
$nextHeight = $destinationHeight;
$nextWidth = round(($sourceWidth * $nextHeight) / $sourceHeight);
$destinationWidth = (int) (!$psImageGenerationMethod ? $destinationWidth : $nextWidth);
} else {
$nextWidth = $destinationWidth;
$nextHeight = round($sourceHeight * $destinationWidth / $sourceWidth);
$destinationHeight = (int) (!$psImageGenerationMethod ? $destinationHeight : $nextHeight);
}
}
if (!ImageManager::checkImageMemoryLimit($sourceFile)) {
return !($error = self::ERROR_MEMORY_LIMIT);
}
$targetWidth = $destinationWidth;
$targetHeight = $destinationHeight;
$destImage = imagecreatetruecolor($destinationWidth, $destinationHeight);
// If image is a PNG and the output is PNG, fill with transparency. Else fill with white background.
if ($fileType == 'png' && $type == IMAGETYPE_PNG) {
imagealphablending($destImage, false);
imagesavealpha($destImage, true);
$transparent = imagecolorallocatealpha($destImage, 255, 255, 255, 127);
imagefilledrectangle($destImage, 0, 0, $destinationWidth, $destinationHeight, $transparent);
} else {
$white = imagecolorallocate($destImage, 255, 255, 255);
imagefilledrectangle($destImage, 0, 0, $destinationWidth, $destinationHeight, $white);
}
$srcImage = ImageManager::create($type, $sourceFile);
if ($rotate) {
$srcImage = imagerotate($srcImage, $rotate, 0);
}
if ($destinationWidth >= $sourceWidth && $destinationHeight >= $sourceHeight) {
imagecopyresized($destImage, $srcImage, (int) (($destinationWidth - $nextWidth) / 2), (int) (($destinationHeight - $nextHeight) / 2), 0, 0, $nextWidth, $nextHeight, $sourceWidth, $sourceHeight);
} else {
ImageManager::imagecopyresampled($destImage, $srcImage, (int) (($destinationWidth - $nextWidth) / 2), (int) (($destinationHeight - $nextHeight) / 2), 0, 0, $nextWidth, $nextHeight, $sourceWidth, $sourceHeight, $quality);
}
$writeFile = ImageManager::write($fileType, $destImage, $destinationFile);
Hook::exec('actionOnImageResizeAfter', array('dst_file' => $destinationFile, 'file_type' => $fileType));
@imagedestroy($srcImage);
file_put_contents(
dirname($destinationFile) . DIRECTORY_SEPARATOR . 'fileType',
$fileType
);
return $writeFile;
}
/**
* @param $dstImage
* @param $srcImage
* @param $dstX
* @param $dstY
* @param $srcX
* @param $srcY
* @param $dstW
* @param $dstH
* @param $srcW
* @param $srcH
* @param int $quality
*
* @return bool
*/
public static function imagecopyresampled(
&$dstImage,
$srcImage,
$dstX,
$dstY,
$srcX,
$srcY,
$dstW,
$dstH,
$srcW,
$srcH,
$quality = 3
) {
// Plug-and-Play fastimagecopyresampled function replaces much slower imagecopyresampled.
// Just include this function and change all "imagecopyresampled" references to "fastimagecopyresampled".
// Typically from 30 to 60 times faster when reducing high resolution images down to thumbnail size using the default quality setting.
// Author: Tim Eckel - Date: 09/07/07 - Version: 1.1 - Project: FreeRingers.net - Freely distributable - These comments must remain.
//
// Optional "quality" parameter (defaults is 3). Fractional values are allowed, for example 1.5. Must be greater than zero.
// Between 0 and 1 = Fast, but mosaic results, closer to 0 increases the mosaic effect.
// 1 = Up to 350 times faster. Poor results, looks very similar to imagecopyresized.
// 2 = Up to 95 times faster. Images appear a little sharp, some prefer this over a quality of 3.
// 3 = Up to 60 times faster. Will give high quality smooth results very close to imagecopyresampled, just faster.
// 4 = Up to 25 times faster. Almost identical to imagecopyresampled for most images.
// 5 = No speedup. Just uses imagecopyresampled, no advantage over imagecopyresampled.
if (empty($srcImage) || empty($dstImage) || $quality <= 0) {
return false;
}
if ($quality < 5 && (($dstW * $quality) < $srcW || ($dstH * $quality) < $srcH)) {
$temp = imagecreatetruecolor($dstW * $quality + 1, $dstH * $quality + 1);
imagecopyresized($temp, $srcImage, 0, 0, $srcX, $srcY, $dstW * $quality + 1, $dstH * $quality + 1, $srcW, $srcH);
imagecopyresampled($dstImage, $temp, $dstX, $dstY, 0, 0, $dstW, $dstH, $dstW * $quality, $dstH * $quality);
imagedestroy($temp);
} else {
imagecopyresampled($dstImage, $srcImage, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
}
return true;
}
/**
* Check if file is a real image.
*
* @param string $filename File path to check
* @param string $fileMimeType File known mime type (generally from $_FILES)
* @param array $mimeTypeList Allowed MIME types
*
* @return bool
*/
public static function isRealImage($filename, $fileMimeType = null, $mimeTypeList = null)
{
// Detect mime content type
$mimeType = false;
if (!$mimeTypeList) {
$mimeTypeList = array('image/gif', 'image/jpg', 'image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png');
}
// Try 4 different methods to determine the mime type
if (function_exists('getimagesize')) {
$imageInfo = @getimagesize($filename);
if ($imageInfo) {
$mimeType = $imageInfo['mime'];
} else {
$fileMimeType = false;
}
} elseif (function_exists('finfo_open')) {
$const = defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME;
$finfo = finfo_open($const);
$mimeType = finfo_file($finfo, $filename);
finfo_close($finfo);
} elseif (function_exists('mime_content_type')) {
$mimeType = mime_content_type($filename);
} elseif (function_exists('exec')) {
$mimeType = trim(exec('file -b --mime-type ' . escapeshellarg($filename)));
if (!$mimeType) {
$mimeType = trim(exec('file --mime ' . escapeshellarg($filename)));
}
if (!$mimeType) {
$mimeType = trim(exec('file -bi ' . escapeshellarg($filename)));
}
}
if ($fileMimeType && (empty($mimeType) || $mimeType == 'regular file' || $mimeType == 'text/plain')) {
$mimeType = $fileMimeType;
}
// For each allowed MIME type, we are looking for it inside the current MIME type
foreach ($mimeTypeList as $type) {
if (strstr($mimeType, $type)) {
return true;
}
}
return false;
}
/**
* Check if image file extension is correct.
*
* @param string $filename Real filename
* @param array|null $authorizedExtensions
*
* @return bool True if it's correct
*/
public static function isCorrectImageFileExt($filename, $authorizedExtensions = null)
{
// Filter on file extension
if ($authorizedExtensions === null) {
$authorizedExtensions = array('gif', 'jpg', 'jpeg', 'jpe', 'png');
}
$nameExplode = explode('.', $filename);
if (count($nameExplode) >= 2) {
$currentExtension = strtolower($nameExplode[count($nameExplode) - 1]);
if (!in_array($currentExtension, $authorizedExtensions)) {
return false;
}
} else {
return false;
}
return true;
}
/**
* Validate image upload (check image type and weight).
*
* @param array $file Upload $_FILE value
* @param int $maxFileSize Maximum upload size
*
* @return bool|string Return false if no error encountered
*/
public static function validateUpload($file, $maxFileSize = 0, $types = null)
{
if ((int) $maxFileSize > 0 && $file['size'] > (int) $maxFileSize) {
return Context::getContext()->getTranslator()->trans('Image is too large (%1$d kB). Maximum allowed: %2$d kB', array($file['size'] / 1024, $maxFileSize / 1024), 'Admin.Notifications.Error');
}
if (!ImageManager::isRealImage($file['tmp_name'], $file['type']) || !ImageManager::isCorrectImageFileExt($file['name'], $types) || preg_match('/\%00/', $file['name'])) {
return Context::getContext()->getTranslator()->trans('Image format not recognized, allowed formats are: .gif, .jpg, .png', array(), 'Admin.Notifications.Error');
}
if ($file['error']) {
return Context::getContext()->getTranslator()->trans('Error while uploading image; please change your server\'s settings. (Error code: %s)', array($file['error']), 'Admin.Notifications.Error');
}
return false;
}
/**
* Validate icon upload.
*
* @param array $file Upload $_FILE value
* @param int $maxFileSize Maximum upload size
*
* @return bool|string Return false if no error encountered
*/
public static function validateIconUpload($file, $maxFileSize = 0)
{
if ((int) $maxFileSize > 0 && $file['size'] > $maxFileSize) {
return Context::getContext()->getTranslator()->trans('Image is too large (%1$d kB). Maximum allowed: %2$d kB', array($file['size'] / 1000, $maxFileSize / 1000), 'Admin.Notifications.Error');
}
if (substr($file['name'], -4) != '.ico') {
return Context::getContext()->getTranslator()->trans('Image format not recognized, allowed formats are: .ico', array(), 'Admin.Notifications.Error');
}
if ($file['error']) {
return Context::getContext()->getTranslator()->trans('Error while uploading image; please change your server\'s settings.', array(), 'Admin.Notifications.Error');
}
return false;
}
/**
* Cut image.
*
* @param array $srcFile Origin filename
* @param string $dstFile Destination filename
* @param int $dstWidth Desired width
* @param int $dstHeight Desired height
* @param string $fileType
* @param int $dstX
* @param int $dstY
*
* @return bool Operation result
*/
public static function cut($srcFile, $dstFile, $dstWidth = null, $dstHeight = null, $fileType = 'jpg', $dstX = 0, $dstY = 0)
{
if (!file_exists($srcFile)) {
return false;
}
// Source information
$srcInfo = getimagesize($srcFile);
$src = array(
'width' => $srcInfo[0],
'height' => $srcInfo[1],
'ressource' => ImageManager::create($srcInfo[2], $srcFile),
);
// Destination information
$dest = array();
$dest['x'] = $dstX;
$dest['y'] = $dstY;
$dest['width'] = null !== $dstWidth ? $dstWidth : $src['width'];
$dest['height'] = null !== $dstHeight ? $dstHeight : $src['height'];
$dest['ressource'] = ImageManager::createWhiteImage($dest['width'], $dest['height']);
$white = imagecolorallocate($dest['ressource'], 255, 255, 255);
imagecopyresampled($dest['ressource'], $src['ressource'], 0, 0, $dest['x'], $dest['y'], $dest['width'], $dest['height'], $dest['width'], $dest['height']);
imagecolortransparent($dest['ressource'], $white);
$return = ImageManager::write($fileType, $dest['ressource'], $dstFile);
Hook::exec('actionOnImageCutAfter', array('dst_file' => $dstFile, 'file_type' => $fileType));
@imagedestroy($src['ressource']);
return $return;
}
/**
* Create an image with GD extension from a given type.
*
* @param string $type
* @param string $filename
*
* @return resource
*/
public static function create($type, $filename)
{
switch ($type) {
case IMAGETYPE_GIF:
return imagecreatefromgif($filename);
break;
case IMAGETYPE_PNG:
return imagecreatefrompng($filename);
break;
case IMAGETYPE_JPEG:
default:
return imagecreatefromjpeg($filename);
break;
}
}
/**
* Create an empty image with white background.
*
* @param int $width
* @param int $height
*
* @return resource
*/
public static function createWhiteImage($width, $height)
{
$image = imagecreatetruecolor($width, $height);
$white = imagecolorallocate($image, 255, 255, 255);
imagefill($image, 0, 0, $white);
return $image;
}
/**
* Generate and write image.
*
* @param string $type
* @param resource $resource
* @param string $filename
*
* @return bool
*/
public static function write($type, $resource, $filename)
{
static $psPngQuality = null;
static $psJpegQuality = null;
if ($psPngQuality === null) {
$psPngQuality = Configuration::get('PS_PNG_QUALITY');
}
if ($psJpegQuality === null) {
$psJpegQuality = Configuration::get('PS_JPEG_QUALITY');
}
switch ($type) {
case 'gif':
$success = imagegif($resource, $filename);
break;
case 'png':
$quality = ($psPngQuality === false ? 7 : $psPngQuality);
$success = imagepng($resource, $filename, (int) $quality);
break;
case 'jpg':
case 'jpeg':
default:
$quality = ($psJpegQuality === false ? 90 : $psJpegQuality);
imageinterlace($resource, 1); /// make it PROGRESSIVE
$success = imagejpeg($resource, $filename, (int) $quality);
break;
}
imagedestroy($resource);
@chmod($filename, 0664);
return $success;
}
/**
* Return the mime type by the file extension.
*
* @param string $fileName
*
* @return string
*/
public static function getMimeTypeByExtension($fileName)
{
$types = array(
'image/gif' => array('gif'),
'image/jpeg' => array('jpg', 'jpeg'),
'image/png' => array('png'),
);
$extension = substr($fileName, strrpos($fileName, '.') + 1);
$mimeType = null;
foreach ($types as $mime => $exts) {
if (in_array($extension, $exts)) {
$mimeType = $mime;
break;
}
}
if ($mimeType === null) {
$mimeType = 'image/jpeg';
}
return $mimeType;
}
}

227
classes/ImageType.php Normal file
View File

@@ -0,0 +1,227 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ImageTypeCore.
*/
class ImageTypeCore extends ObjectModel
{
public $id;
/** @var string Name */
public $name;
/** @var int Width */
public $width;
/** @var int Height */
public $height;
/** @var bool Apply to products */
public $products;
/** @var int Apply to categories */
public $categories;
/** @var int Apply to manufacturers */
public $manufacturers;
/** @var int Apply to suppliers */
public $suppliers;
/** @var int Apply to store */
public $stores;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'image_type',
'primary' => 'id_image_type',
'fields' => array(
'name' => array('type' => self::TYPE_STRING, 'validate' => 'isImageTypeName', 'required' => true, 'size' => 64),
'width' => array('type' => self::TYPE_INT, 'validate' => 'isImageSize', 'required' => true),
'height' => array('type' => self::TYPE_INT, 'validate' => 'isImageSize', 'required' => true),
'categories' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'products' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'manufacturers' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'suppliers' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'stores' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
),
);
/**
* @var array Image types cache
*/
protected static $images_types_cache = array();
protected static $images_types_name_cache = array();
protected $webserviceParameters = array();
/**
* Returns image type definitions.
*
* @param string|null Image type
* @param bool $orderBySize
*
* @return array Image type definitions
*
* @throws PrestaShopDatabaseException
*/
public static function getImagesTypes($type = null, $orderBySize = false)
{
if (!isset(self::$images_types_cache[$type])) {
$where = 'WHERE 1';
if (!empty($type)) {
$where .= ' AND `' . bqSQL($type) . '` = 1 ';
}
if ($orderBySize) {
$query = 'SELECT * FROM `' . _DB_PREFIX_ . 'image_type` ' . $where . ' ORDER BY `width` DESC, `height` DESC, `name`ASC';
} else {
$query = 'SELECT * FROM `' . _DB_PREFIX_ . 'image_type` ' . $where . ' ORDER BY `name` ASC';
}
self::$images_types_cache[$type] = Db::getInstance()->executeS($query);
}
return self::$images_types_cache[$type];
}
/**
* Check if type already is already registered in database.
*
* @param string $typeName Name
*
* @return int Number of results found
*/
public static function typeAlreadyExists($typeName)
{
if (!Validate::isImageTypeName($typeName)) {
die(Tools::displayError());
}
Db::getInstance()->executeS('
SELECT `id_image_type`
FROM `' . _DB_PREFIX_ . 'image_type`
WHERE `name` = \'' . pSQL($typeName) . '\'');
return Db::getInstance()->numRows();
}
/**
* Finds image type definition by name and type.
*
* @param string $name
* @param string $type
*/
public static function getByNameNType($name, $type = null, $order = 0)
{
static $is_passed = false;
if (!isset(self::$images_types_name_cache[$name . '_' . $type . '_' . $order]) && !$is_passed) {
$results = Db::getInstance()->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'image_type`');
$types = array('products', 'categories', 'manufacturers', 'suppliers', 'stores');
$total = count($types);
foreach ($results as $result) {
foreach ($result as $value) {
for ($i = 0; $i < $total; ++$i) {
self::$images_types_name_cache[$result['name'] . '_' . $types[$i] . '_' . $value] = $result;
}
}
}
$is_passed = true;
}
$return = false;
if (isset(self::$images_types_name_cache[$name . '_' . $type . '_' . $order])) {
$return = self::$images_types_name_cache[$name . '_' . $type . '_' . $order];
}
return $return;
}
/**
* Get formatted name.
*
* @deprecated 1.7.0.0 Use ImageType::getFormattedName($name) instead
*
* @param string $name
*
* @return string
*/
public static function getFormatedName($name)
{
Tools::displayAsDeprecated('Please use ImageType::getFormattedName($name) instead');
return self::getFormattedName($name);
}
/**
* Get formatted name.
*
* @param string $name
*
* @return string
*/
public static function getFormattedName($name)
{
$themeName = Context::getContext()->shop->theme_name;
$nameWithoutThemeName = str_replace(array('_' . $themeName, $themeName . '_'), '', $name);
//check if the theme name is already in $name if yes only return $name
if (strstr($name, $themeName) && self::getByNameNType($name)) {
return $name;
} elseif (self::getByNameNType($nameWithoutThemeName . '_' . $themeName)) {
return $nameWithoutThemeName . '_' . $themeName;
} elseif (self::getByNameNType($themeName . '_' . $nameWithoutThemeName)) {
return $themeName . '_' . $nameWithoutThemeName;
} else {
return $nameWithoutThemeName . '_default';
}
}
/**
* Get all image types.
*
* @return array
*/
public static function getAll()
{
$context = Context::getContext();
if (isset($context->shop->theme)) {
$imagesTypes = $context->shop->theme->get('image_types');
return is_array($imagesTypes) ? $imagesTypes : array();
}
return array();
}
}

1499
classes/Language.php Normal file

File diff suppressed because it is too large Load Diff

1534
classes/Link.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,621 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
use PrestaShop\PrestaShop\Core\Addon\Module\ModuleManagerBuilder;
use PrestaShop\PrestaShop\Core\Localization\CLDR\LocaleRepository;
class LocalizationPackCore
{
public $name;
public $version;
protected $iso_code_lang;
protected $iso_currency;
protected $_errors = array();
/**
* Loads localization pack.
*
* @param SimpleXMLElement|string $pack Localization pack as SimpleXMLElement or plain XML string
* @param array $selection Content to import selection
* @param bool $install_mode Whether mode is installation or not
* @param string|null $iso_localization_pack Country Alpha-2 ISO code
*
* @return bool
*/
public function loadLocalisationPack($pack, $selection, $install_mode = false, $iso_localization_pack = null)
{
if ($pack instanceof SimpleXMLElement) {
$xml = $pack;
} elseif (!$xml = @simplexml_load_string($pack)) {
return false;
}
libxml_clear_errors();
$main_attributes = $xml->attributes();
$this->name = (string) $main_attributes['name'];
$this->version = (string) $main_attributes['version'];
if ($iso_localization_pack) {
$id_country = (int) Country::getByIso($iso_localization_pack);
if ($id_country) {
$country = new Country($id_country);
}
if (!$id_country || !Validate::isLoadedObject($country)) {
$this->_errors[] = Context::getContext()->getTranslator()->trans(
'Cannot load country: %d',
array($id_country),
'Admin.International.Notification'
);
return false;
}
if (!$country->active) {
$country->active = 1;
if (!$country->update()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans(
'Cannot enable the associated country: %s',
array($country->name),
'Admin.International.Notification'
);
}
}
}
$res = true;
if (empty($selection)) {
$res &= $this->_installStates($xml);
$res &= $this->_installTaxes($xml);
$res &= $this->_installCurrencies($xml, $install_mode);
$res &= $this->installConfiguration($xml);
$res &= $this->installModules($xml);
$res &= $this->updateDefaultGroupDisplayMethod($xml);
if (($res || $install_mode) && isset($this->iso_code_lang)) {
if (!($id_lang = (int) Language::getIdByIso($this->iso_code_lang, true))) {
$id_lang = 1;
}
if (!$install_mode) {
Configuration::updateValue('PS_LANG_DEFAULT', $id_lang);
}
} elseif (!isset($this->iso_code_lang) && $install_mode) {
$id_lang = 1;
}
if (!Language::isInstalled(Language::getIsoById($id_lang))) {
$res &= $this->_installLanguages($xml, $install_mode);
$res &= $this->_installUnits($xml);
}
if ($install_mode && $res && isset($this->iso_currency)) {
Cache::clean('Currency::getIdByIsoCode_*');
$res &= Configuration::updateValue('PS_CURRENCY_DEFAULT', (int) Currency::getIdByIsoCode($this->iso_currency));
Currency::refreshCurrencies();
}
} else {
foreach ($selection as $selected) {
// No need to specify the install_mode because if the selection mode is used, then it's not the install
$res &= Validate::isLocalizationPackSelection($selected) ? $this->{'_install' . $selected}($xml) : false;
}
}
return $res;
}
/**
* @param SimpleXMLElement $xml
*
* @return bool
*
* @throws PrestaShopException
*/
protected function _installStates($xml)
{
if (isset($xml->states->state)) {
foreach ($xml->states->state as $data) {
/** @var SimpleXMLElement $data */
$attributes = $data->attributes();
$id_country = ($attributes['country']) ? (int) Country::getByIso((string) ($attributes['country'])) : false;
$id_state = ($id_country) ? State::getIdByIso($attributes['iso_code'], $id_country) : State::getIdByName($attributes['name']);
if (!$id_state) {
$state = new State();
$state->name = (string) ($attributes['name']);
$state->iso_code = (string) ($attributes['iso_code']);
$state->id_country = $id_country;
$id_zone = (int) Zone::getIdByName((string) ($attributes['zone']));
if (!$id_zone) {
$zone = new Zone();
$zone->name = (string) $attributes['zone'];
$zone->active = true;
if (!$zone->add()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('Invalid Zone name.', array(), 'Admin.International.Notification');
return false;
}
$id_zone = $zone->id;
}
$state->id_zone = $id_zone;
if (!$state->validateFields()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('Invalid state properties.', array(), 'Admin.International.Notification');
return false;
}
$country = new Country($state->id_country);
if (!$country->contains_states) {
$country->contains_states = 1;
if (!$country->update()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('Cannot update the associated country: %s', array($country->name), 'Admin.International.Notification');
}
}
if (!$state->add()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error occurred while adding the state.', array(), 'Admin.International.Notification');
return false;
}
} else {
$state = new State($id_state);
if (!Validate::isLoadedObject($state)) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error occurred while fetching the state.', array(), 'Admin.International.Notification');
return false;
}
}
}
}
return true;
}
/**
* @param SimpleXMLElement $xml
*
* @return bool
*
* @throws PrestaShopException
*/
protected function _installTaxes($xml)
{
if (isset($xml->taxes->tax)) {
$assoc_taxes = array();
foreach ($xml->taxes->tax as $taxData) {
/** @var SimpleXMLElement $taxData */
$attributes = $taxData->attributes();
if (($id_tax = Tax::getTaxIdByName($attributes['name']))) {
$assoc_taxes[(int) $attributes['id']] = $id_tax;
continue;
}
$tax = new Tax();
$tax->name[(int) Configuration::get('PS_LANG_DEFAULT')] = (string) $attributes['name'];
$tax->rate = (float) $attributes['rate'];
$tax->active = 1;
if (($error = $tax->validateFields(false, true)) !== true || ($error = $tax->validateFieldsLang(false, true)) !== true) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('Invalid tax properties.', array(), 'Admin.International.Notification') . ' ' . $error;
return false;
}
if (!$tax->add()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error occurred while importing the tax: %s', array((string) $attributes['name']), 'Admin.International.Notification');
return false;
}
$assoc_taxes[(int) $attributes['id']] = $tax->id;
}
foreach ($xml->taxes->taxRulesGroup as $group) {
/** @var SimpleXMLElement $group */
$group_attributes = $group->attributes();
if (!Validate::isGenericName($group_attributes['name'])) {
continue;
}
if (TaxRulesGroup::getIdByName($group['name'])) {
continue;
}
$trg = new TaxRulesGroup();
$trg->name = $group['name'];
$trg->active = 1;
if (!$trg->save()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('This tax rule cannot be saved.', array(), 'Admin.International.Notification');
return false;
}
foreach ($group->taxRule as $rule) {
/** @var SimpleXMLElement $rule */
$rule_attributes = $rule->attributes();
// Validation
if (!isset($rule_attributes['iso_code_country'])) {
continue;
}
$id_country = (int) Country::getByIso(strtoupper($rule_attributes['iso_code_country']));
if (!$id_country) {
continue;
}
if (!isset($rule_attributes['id_tax']) || !array_key_exists((string) ($rule_attributes['id_tax']), $assoc_taxes)) {
continue;
}
// Default values
$id_state = (int) isset($rule_attributes['iso_code_state']) ? State::getIdByIso(strtoupper($rule_attributes['iso_code_state'])) : 0;
$id_county = 0;
$zipcode_from = 0;
$zipcode_to = 0;
$behavior = $rule_attributes['behavior'];
if (isset($rule_attributes['zipcode_from'])) {
$zipcode_from = $rule_attributes['zipcode_from'];
if (isset($rule_attributes['zipcode_to'])) {
$zipcode_to = $rule_attributes['zipcode_to'];
}
}
// Creation
$tr = new TaxRule();
$tr->id_tax_rules_group = $trg->id;
$tr->id_country = $id_country;
$tr->id_state = $id_state;
$tr->id_county = $id_county;
$tr->zipcode_from = $zipcode_from;
$tr->zipcode_to = $zipcode_to;
$tr->behavior = $behavior;
$tr->description = '';
$tr->id_tax = $assoc_taxes[(string) ($rule_attributes['id_tax'])];
$tr->save();
}
}
}
return true;
}
/**
* @param SimpleXMLElement $xml
* @param bool $install_mode
*
* @return bool
*
* @throws PrestaShopException
*/
protected function _installCurrencies($xml, $install_mode = false)
{
if (isset($xml->currencies->currency)) {
foreach ($xml->currencies->currency as $data) {
/** @var SimpleXMLElement $data */
$attributes = $data->attributes();
if (Currency::exists($attributes['iso_code'])) {
continue;
}
/** @var Language $defaultLang */
$defaultLang = new Language((int) Configuration::get('PS_LANG_DEFAULT'));
$currency = new Currency();
$currency->name = (string) $attributes['name'];
$currency->iso_code = (string) $attributes['iso_code'];
$currency->iso_code_num = (int) $attributes['iso_code_num'];
$currency->numeric_iso_code = (string) $attributes['iso_code_num'];
$currency->blank = (int) $attributes['blank'];
$currency->conversion_rate = 1; // This value will be updated if the store is online
$currency->format = (int) $attributes['format'];
$currency->decimals = (int) $attributes['decimals'];
$currency->active = true;
if (!$currency->validateFields()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('Invalid currency properties.', array(), 'Admin.International.Notification');
return false;
}
if (!Currency::exists($currency->iso_code)) {
if (!$currency->add()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error occurred while importing the currency: %s', array((string) ($attributes['name'])), 'Admin.International.Notification');
return false;
}
Cache::clear();
// set default data from CLDR
$this->setCurrencyCldrData($currency, $defaultLang);
// Following is required when installing new currency on existing languages:
// we want to properly update the symbol in each language
$localeRepoCLDR = $this->getCldrLocaleRepository();
$currency->refreshLocalizedCurrencyData(Language::getLanguages(), $localeRepoCLDR);
$currency->save();
PaymentModule::addCurrencyPermissions($currency->id);
}
}
$error = Currency::refreshCurrencies();
if (!empty($error)) {
$this->_errors[] = $error;
}
if (!count($this->_errors) && $install_mode && isset($attributes['iso_code']) && count($xml->currencies->currency) == 1) {
$this->iso_currency = $attributes['iso_code'];
}
}
return true;
}
/**
* @return LocaleRepository
*
* @throws Exception
*/
protected function getCldrLocaleRepository()
{
$context = Context::getContext();
$container = isset($context->controller) ? $context->controller->getContainer() : null;
if (null === $container) {
$container = SymfonyContainer::getInstance();
}
/** @var LocaleRepository $localeRepoCLDR */
$localeRepoCLDR = $container->get('prestashop.core.localization.cldr.locale_repository');
return $localeRepoCLDR;
}
/**
* @param Currency $currency
* @param Language $defaultLang
*/
protected function setCurrencyCldrData(Currency $currency, Language $defaultLang)
{
$localeRepoCLDR = $this->getCldrLocaleRepository();
$cldrLocale = $localeRepoCLDR->getLocale($defaultLang->locale);
$cldrCurrency = $cldrLocale->getCurrency($currency->iso_code);
$symbol = (string) $cldrCurrency->getSymbol();
if (empty($symbol)) {
$symbol = $currency->iso_code;
}
$currency->symbol = $symbol;
$currency->sign = $symbol;
$currency->precision = (int) $cldrCurrency->getDecimalDigits();
}
/**
* @param SimpleXMLElement $xml
* @param bool $install_mode
*
* @return bool
*/
protected function _installLanguages($xml, $install_mode = false)
{
$attributes = array();
if (isset($xml->languages->language)) {
foreach ($xml->languages->language as $data) {
/** @var SimpleXMLElement $data */
$attributes = $data->attributes();
// if we are not in an installation context or if the pack is not available in the local directory
if (Language::getIdByIso($attributes['iso_code']) && !$install_mode) {
continue;
}
$freshInstall = empty(Language::getIdByIso($attributes['iso_code']));
$errors = Language::downloadAndInstallLanguagePack($attributes['iso_code'], $attributes['version'], $attributes, $freshInstall);
if ($errors !== true && is_array($errors)) {
$this->_errors = array_merge($this->_errors, $errors);
}
}
}
// change the default language if there is only one language in the localization pack
if (!count($this->_errors) && $install_mode && isset($attributes['iso_code']) && count($xml->languages->language) == 1) {
$this->iso_code_lang = $attributes['iso_code'];
}
// refreshed localized currency data
$this->refreshLocalizedCurrenciesData();
return !count($this->_errors);
}
/**
* This method aims to update localized data in currencies from CLDR reference.
* Eg currency symbol used depends on language, so it has to be updated when adding a new language
* Use-case: adding a new language should trigger all currencies update
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
* @throws \PrestaShop\PrestaShop\Core\Localization\Exception\LocalizationException
*/
protected function refreshLocalizedCurrenciesData()
{
/** @var Currency[] $currencies */
$currencies = Currency::getCurrencies(true, false, true);
$languages = Language::getLanguages();
$localeRepoCLDR = $this->getCldrLocaleRepository();
foreach ($currencies as $currency) {
$currency->refreshLocalizedCurrencyData($languages, $localeRepoCLDR);
$currency->save();
}
}
/**
* @param SimpleXMLElement $xml
*
* @return bool
*/
protected function _installUnits($xml)
{
$varNames = array('weight' => 'PS_WEIGHT_UNIT', 'volume' => 'PS_VOLUME_UNIT', 'short_distance' => 'PS_DIMENSION_UNIT', 'base_distance' => 'PS_BASE_DISTANCE_UNIT', 'long_distance' => 'PS_DISTANCE_UNIT');
if (isset($xml->units->unit)) {
foreach ($xml->units->unit as $data) {
/** @var SimpleXMLElement $data */
$attributes = $data->attributes();
if (!isset($varNames[(string) ($attributes['type'])])) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('Localization pack corrupted: wrong unit type.', array(), 'Admin.International.Notification');
return false;
}
if (!Configuration::updateValue($varNames[(string) ($attributes['type'])], (string) ($attributes['value']))) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error occurred while setting the units.', array(), 'Admin.International.Notification');
return false;
}
}
}
return true;
}
/**
* Install/Uninstall a module from a localization file
* <modules>
* <module name="module_name" [install="0|1"] />.
*
* @param SimpleXMLElement $xml
*
* @return bool
*/
protected function installModules($xml)
{
if (isset($xml->modules)) {
foreach ($xml->modules->module as $data) {
/** @var SimpleXMLElement $data */
$attributes = $data->attributes();
$name = (string) $attributes['name'];
if (isset($name) && $module = Module::getInstanceByName($name)) {
$install = ($attributes['install'] == 1) ? true : false;
$moduleManagerBuilder = ModuleManagerBuilder::getInstance();
$moduleManager = $moduleManagerBuilder->build();
if ($install) {
if (!$moduleManager->isInstalled($name)) {
if (!$module->install()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error occurred while installing the module: %s', array($name), 'Admin.International.Notification');
}
}
} elseif ($moduleManager->isInstalled($name)) {
if (!$module->uninstall()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error occurred while uninstalling the module: %s', array($name), 'Admin.International.Notification');
}
}
unset($module);
} else {
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error has occurred, this module does not exist: %s', array($name), 'Admin.International.Notification');
}
}
}
return true;
}
/**
* Update a configuration variable from a localization file
* <configuration>
* <configuration name="variable_name" value="variable_value" />.
*
* @param SimpleXMLElement $xml
*
* @return bool
*/
protected function installConfiguration($xml)
{
if (isset($xml->configurations)) {
foreach ($xml->configurations->configuration as $data) {
/** @var SimpleXMLElement $data */
$attributes = $data->attributes();
$name = (string) $attributes['name'];
if (isset($name, $attributes['value']) && Configuration::get($name) !== false) {
if (!Configuration::updateValue($name, (string) $attributes['value'])) {
$this->_errors[] = Context::getContext()->getTranslator()->trans(
'An error occurred during the configuration setup: %1$s',
array($name),
'Admin.International.Notification'
);
}
}
}
}
return true;
}
/**
* @param SimpleXMLElement $xml
*
* @return bool
*/
protected function _installGroups($xml)
{
return $this->updateDefaultGroupDisplayMethod($xml);
}
/**
* @param SimpleXMLElement $xml
*
* @return bool
*/
protected function updateDefaultGroupDisplayMethod($xml)
{
if (isset($xml->group_default)) {
$attributes = $xml->group_default->attributes();
if (isset($attributes['price_display_method']) && in_array((int) $attributes['price_display_method'], array(0, 1))) {
Configuration::updateValue('PRICE_DISPLAY_METHOD', (int) $attributes['price_display_method']);
foreach (array((int) Configuration::get('PS_CUSTOMER_GROUP'), (int) Configuration::get('PS_GUEST_GROUP'), (int) Configuration::get('PS_UNIDENTIFIED_GROUP')) as $id_group) {
$group = new Group((int) $id_group);
$group->price_display_method = (int) $attributes['price_display_method'];
if (!$group->save()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error occurred during the default group update', array(), 'Admin.International.Notification');
}
}
} else {
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error has occurred during the default group update', array(), 'Admin.International.Notification');
}
}
return true;
}
public function getErrors()
{
return $this->_errors;
}
}

927
classes/Mail.php Normal file
View File

@@ -0,0 +1,927 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class MailCore.
*/
class MailCore extends ObjectModel
{
public $id;
/** @var string Recipient */
public $recipient;
/** @var string Template */
public $template;
/** @var string Subject */
public $subject;
/** @var int Language ID */
public $id_lang;
/** @var int Timestamp */
public $date_add;
/**
* @see ObjectModel::$definition
*/
public static $definition = [
'table' => 'mail',
'primary' => 'id_mail',
'fields' => [
'recipient' => [
'type' => self::TYPE_STRING,
'validate' => 'isEmail',
'copy_post' => false,
'required' => true,
'size' => 255,
],
'template' => [
'type' => self::TYPE_STRING,
'validate' => 'isTplName',
'copy_post' => false,
'required' => true,
'size' => 62,
],
'subject' => [
'type' => self::TYPE_STRING,
'validate' => 'isMailSubject',
'copy_post' => false,
'required' => true,
'size' => 255,
],
'id_lang' => [
'type' => self::TYPE_INT,
'validate' => 'isUnsignedId',
'copy_post' => false,
'required' => true,
],
'date_add' => [
'type' => self::TYPE_DATE,
'validate' => 'isDate',
'copy_post' => false,
'required' => true,
],
],
];
/**
* Mail content type.
*/
const TYPE_HTML = 1;
const TYPE_TEXT = 2;
const TYPE_BOTH = 3;
/**
* Send mail under SMTP server.
*/
const METHOD_SMTP = 2;
/**
* Disable mail, will return immediately after calling send method.
*/
const METHOD_DISABLE = 3;
/**
* Send Email.
*
* @param int $idLang Language ID of the email (to translate the template)
* @param string $template Template: the name of template not be a var but a string !
* @param string $subject Subject of the email
* @param string $templateVars Template variables for the email
* @param string $to To email
* @param string $toName To name
* @param string $from From email
* @param string $fromName To email
* @param array $fileAttachment array with three parameters (content, mime and name).
* You can use an array of array to attach multiple files
* @param bool $mode_smtp SMTP mode (deprecated)
* @param string $templatePath Template path
* @param bool $die Die after error
* @param int $idShop Shop ID
* @param string $bcc Bcc recipient address. You can use an array of array to send to multiple recipients
* @param string $replyTo Reply-To recipient address
* @param string $replyToName Reply-To recipient name
*
* @return bool|int Whether sending was successful. If not at all, false, otherwise amount of recipients succeeded.
*/
public static function send(
$idLang,
$template,
$subject,
$templateVars,
$to,
$toName = null,
$from = null,
$fromName = null,
$fileAttachment = null,
$mode_smtp = null,
$templatePath = _PS_MAIL_DIR_,
$die = false,
$idShop = null,
$bcc = null,
$replyTo = null,
$replyToName = null
) {
if (!$idShop) {
$idShop = Context::getContext()->shop->id;
}
$hookBeforeEmailResult = Hook::exec(
'actionEmailSendBefore',
[
'idLang' => &$idLang,
'template' => &$template,
'subject' => &$subject,
'templateVars' => &$templateVars,
'to' => &$to,
'toName' => &$toName,
'from' => &$from,
'fromName' => &$fromName,
'fileAttachment' => &$fileAttachment,
'mode_smtp' => &$mode_smtp,
'templatePath' => &$templatePath,
'die' => &$die,
'idShop' => &$idShop,
'bcc' => &$bcc,
'replyTo' => &$replyTo,
],
null,
true
);
if ($hookBeforeEmailResult === null) {
$keepGoing = false;
} else {
$keepGoing = array_reduce(
$hookBeforeEmailResult,
function ($carry, $item) {
return ($item === false) ? false : $carry;
},
true
);
}
if (!$keepGoing) {
return true;
}
if (is_numeric($idShop) && $idShop) {
$shop = new Shop((int) $idShop);
}
$configuration = Configuration::getMultiple(
[
'PS_SHOP_EMAIL',
'PS_MAIL_METHOD',
'PS_MAIL_SERVER',
'PS_MAIL_USER',
'PS_MAIL_PASSWD',
'PS_SHOP_NAME',
'PS_MAIL_SMTP_ENCRYPTION',
'PS_MAIL_SMTP_PORT',
'PS_MAIL_TYPE',
],
null,
null,
$idShop
);
// Returns immediately if emails are deactivated
if ($configuration['PS_MAIL_METHOD'] == self::METHOD_DISABLE) {
return true;
}
// Hook to alter template vars
Hook::exec(
'sendMailAlterTemplateVars',
[
'template' => $template,
'template_vars' => &$templateVars,
]
);
if (!isset($configuration['PS_MAIL_SMTP_ENCRYPTION']) ||
Tools::strtolower($configuration['PS_MAIL_SMTP_ENCRYPTION']) === 'off'
) {
$configuration['PS_MAIL_SMTP_ENCRYPTION'] = false;
}
if (!isset($configuration['PS_MAIL_SMTP_PORT'])) {
$configuration['PS_MAIL_SMTP_PORT'] = 'default';
}
/*
* Sending an e-mail can be of vital importance for the merchant, when his password
* is lost for example, so we must not die but do our best to send the e-mail.
*/
if (!isset($from) || !Validate::isEmail($from)) {
$from = $configuration['PS_SHOP_EMAIL'];
}
if (!Validate::isEmail($from)) {
$from = null;
}
// $from_name is not that important, no need to die if it is not valid
if (!isset($fromName) || !Validate::isMailName($fromName)) {
$fromName = $configuration['PS_SHOP_NAME'];
}
if (!Validate::isMailName($fromName)) {
$fromName = null;
}
/*
* It would be difficult to send an e-mail if the e-mail is not valid,
* so this time we can die if there is a problem.
*/
if (!is_array($to) && !Validate::isEmail($to)) {
self::dieOrLog($die, 'Error: parameter "to" is corrupted');
return false;
}
// if bcc is not null, make sure it's a vaild e-mail
if (null !== $bcc && !is_array($bcc) && !Validate::isEmail($bcc)) {
self::dieOrLog($die, 'Error: parameter "bcc" is corrupted');
$bcc = null;
}
if (!is_array($templateVars)) {
$templateVars = [];
}
// Do not crash for this error, that may be a complicated customer name
if (is_string($toName) && !empty($toName) && !Validate::isMailName($toName)) {
$toName = null;
}
if (!Validate::isTplName($template)) {
self::dieOrLog($die, 'Error: invalid e-mail template');
return false;
}
if (!Validate::isMailSubject($subject)) {
self::dieOrLog($die, 'Error: invalid e-mail subject');
return false;
}
/* Construct multiple recipients list if needed */
$message = \Swift_Message::newInstance();
if (is_array($to) && isset($to)) {
foreach ($to as $key => $addr) {
$addr = trim($addr);
if (!Validate::isEmail($addr)) {
self::dieOrLog($die, 'Error: invalid e-mail address');
return false;
}
if (is_array($toName) && isset($toName[$key])) {
$addrName = $toName[$key];
} else {
$addrName = $toName;
}
$addrName = ($addrName == null || $addrName == $addr || !Validate::isGenericName($addrName)) ?
'' :
self::mimeEncode($addrName);
$message->addTo(self::toPunycode($addr), $addrName);
}
$toPlugin = $to[0];
} else {
/* Simple recipient, one address */
$toPlugin = $to;
$toName = (($toName == null || $toName == $to) ? '' : self::mimeEncode($toName));
$message->addTo(self::toPunycode($to), $toName);
}
if (isset($bcc) && is_array($bcc)) {
foreach ($bcc as $addr) {
$addr = trim($addr);
if (!Validate::isEmail($addr)) {
self::dieOrLog($die, 'Error: invalid e-mail address');
return false;
}
$message->addBcc(self::toPunycode($addr));
}
} elseif (isset($bcc)) {
$message->addBcc(self::toPunycode($bcc));
}
try {
/* Connect with the appropriate configuration */
if ($configuration['PS_MAIL_METHOD'] == self::METHOD_SMTP) {
if (empty($configuration['PS_MAIL_SERVER']) || empty($configuration['PS_MAIL_SMTP_PORT'])) {
self::dieOrLog($die, 'Error: invalid SMTP server or SMTP port');
return false;
}
$connection = \Swift_SmtpTransport::newInstance(
$configuration['PS_MAIL_SERVER'],
$configuration['PS_MAIL_SMTP_PORT'],
$configuration['PS_MAIL_SMTP_ENCRYPTION']
)
->setUsername($configuration['PS_MAIL_USER'])
->setPassword($configuration['PS_MAIL_PASSWD']);
} else {
$connection = \Swift_MailTransport::newInstance();
}
if (!$connection) {
return false;
}
$swift = \Swift_Mailer::newInstance($connection);
/* Get templates content */
$iso = Language::getIsoById((int) $idLang);
$isoDefault = Language::getIsoById((int) Configuration::get('PS_LANG_DEFAULT'));
$isoArray = [];
if ($iso) {
$isoArray[] = $iso;
}
if ($isoDefault && $iso !== $isoDefault) {
$isoArray[] = $isoDefault;
}
if (!in_array('en', $isoArray)) {
$isoArray[] = 'en';
}
$moduleName = false;
// get templatePath
if (preg_match('#' . $shop->physical_uri . 'modules/#', str_replace(DIRECTORY_SEPARATOR, '/', $templatePath)) &&
preg_match('#modules/([a-z0-9_-]+)/#ui', str_replace(DIRECTORY_SEPARATOR, '/', $templatePath), $res)
) {
$moduleName = $res[1];
}
foreach ($isoArray as $isoCode) {
$isoTemplate = $isoCode . '/' . $template;
$templatePath = self::getTemplateBasePath($isoTemplate, $moduleName, $shop->theme);
if (!file_exists($templatePath . $isoTemplate . '.txt') &&
(
$configuration['PS_MAIL_TYPE'] == Mail::TYPE_BOTH ||
$configuration['PS_MAIL_TYPE'] == Mail::TYPE_TEXT
)
) {
PrestaShopLogger::addLog(
Context::getContext()->getTranslator()->trans(
'Error - The following e-mail template is missing: %s',
[$templatePath . $isoTemplate . '.txt'],
'Admin.Advparameters.Notification'
)
);
} elseif (!file_exists($templatePath . $isoTemplate . '.html') &&
(
$configuration['PS_MAIL_TYPE'] == Mail::TYPE_BOTH ||
$configuration['PS_MAIL_TYPE'] == Mail::TYPE_HTML
)
) {
PrestaShopLogger::addLog(
Context::getContext()->getTranslator()->trans(
'Error - The following e-mail template is missing: %s',
[$templatePath . $isoTemplate . '.html'],
'Admin.Advparameters.Notification'
)
);
} else {
$templatePathExists = true;
break;
}
}
if (empty($templatePathExists)) {
self::dieOrLog($die, 'Error - The following e-mail template is missing: %s', [$template]);
return false;
}
$templateHtml = '';
$templateTxt = '';
Hook::exec(
'actionEmailAddBeforeContent',
[
'template' => $template,
'template_html' => &$templateHtml,
'template_txt' => &$templateTxt,
'id_lang' => (int) $idLang,
],
null,
true
);
$templateHtml .= Tools::file_get_contents($templatePath . $isoTemplate . '.html');
$templateTxt .= strip_tags(
html_entity_decode(
Tools::file_get_contents($templatePath . $isoTemplate . '.txt'),
null,
'utf-8'
)
);
Hook::exec(
'actionEmailAddAfterContent',
[
'template' => $template,
'template_html' => &$templateHtml,
'template_txt' => &$templateTxt,
'id_lang' => (int) $idLang,
],
null,
true
);
/* Create mail and attach differents parts */
$subject = '[' . strip_tags($configuration['PS_SHOP_NAME']) . '] ' . $subject;
$message->setSubject($subject);
$message->setCharset('utf-8');
/* Set Message-ID - getmypid() is blocked on some hosting */
$message->setId(Mail::generateId());
if (!($replyTo && Validate::isEmail($replyTo))) {
$replyTo = $from;
}
if (isset($replyTo) && $replyTo) {
$message->setReplyTo($replyTo, ($replyToName !== '' ? $replyToName : null));
}
$templateVars = array_map(['Tools', 'htmlentitiesDecodeUTF8'], $templateVars);
$templateVars = array_map(['Tools', 'stripslashes'], $templateVars);
if (false !== Configuration::get('PS_LOGO_MAIL') &&
file_exists(_PS_IMG_DIR_ . Configuration::get('PS_LOGO_MAIL', null, null, $idShop))
) {
$logo = _PS_IMG_DIR_ . Configuration::get('PS_LOGO_MAIL', null, null, $idShop);
} else {
if (file_exists(_PS_IMG_DIR_ . Configuration::get('PS_LOGO', null, null, $idShop))) {
$logo = _PS_IMG_DIR_ . Configuration::get('PS_LOGO', null, null, $idShop);
} else {
$templateVars['{shop_logo}'] = '';
}
}
ShopUrl::cacheMainDomainForShop((int) $idShop);
/* don't attach the logo as */
if (isset($logo)) {
$templateVars['{shop_logo}'] = $message->embed(\Swift_Image::fromPath($logo));
}
if ((Context::getContext()->link instanceof Link) === false) {
Context::getContext()->link = new Link();
}
$templateVars['{shop_name}'] = Tools::safeOutput($configuration['PS_SHOP_NAME']);
$templateVars['{shop_url}'] = Context::getContext()->link->getPageLink(
'index',
true,
$idLang,
null,
false,
$idShop
);
$templateVars['{my_account_url}'] = Context::getContext()->link->getPageLink(
'my-account',
true,
$idLang,
null,
false,
$idShop
);
$templateVars['{guest_tracking_url}'] = Context::getContext()->link->getPageLink(
'guest-tracking',
true,
$idLang,
null,
false,
$idShop
);
$templateVars['{history_url}'] = Context::getContext()->link->getPageLink(
'history',
true,
$idLang,
null,
false,
$idShop
);
$templateVars['{color}'] = Tools::safeOutput(Configuration::get('PS_MAIL_COLOR', null, null, $idShop));
// Get extra template_vars
$extraTemplateVars = [];
Hook::exec(
'actionGetExtraMailTemplateVars',
[
'template' => $template,
'template_vars' => $templateVars,
'extra_template_vars' => &$extraTemplateVars,
'id_lang' => (int) $idLang,
],
null,
true
);
$templateVars = array_merge($templateVars, $extraTemplateVars);
$swift->registerPlugin(new \Swift_Plugins_DecoratorPlugin(array($toPlugin => $templateVars)));
if ($configuration['PS_MAIL_TYPE'] == Mail::TYPE_BOTH ||
$configuration['PS_MAIL_TYPE'] == Mail::TYPE_TEXT
) {
$message->addPart($templateTxt, 'text/plain', 'utf-8');
}
if ($configuration['PS_MAIL_TYPE'] == Mail::TYPE_BOTH ||
$configuration['PS_MAIL_TYPE'] == Mail::TYPE_HTML
) {
$message->addPart($templateHtml, 'text/html', 'utf-8');
}
if ($fileAttachment && !empty($fileAttachment)) {
// Multiple attachments?
if (!is_array(current($fileAttachment))) {
$fileAttachment = array($fileAttachment);
}
foreach ($fileAttachment as $attachment) {
if (isset($attachment['content'], $attachment['name'], $attachment['mime'])) {
$message->attach(
\Swift_Attachment::newInstance()->setFilename(
$attachment['name']
)->setContentType($attachment['mime'])
->setBody($attachment['content'])
);
}
}
}
/* Send mail */
$message->setFrom(array($from => $fromName));
// Hook to alter Swift Message before sending mail
Hook::exec('actionMailAlterMessageBeforeSend', [
'message' => &$message,
]);
$send = $swift->send($message);
ShopUrl::resetMainDomainCache();
if ($send && Configuration::get('PS_LOG_EMAILS')) {
$mail = new Mail();
$mail->template = Tools::substr($template, 0, 62);
$mail->subject = Tools::substr($subject, 0, 255);
$mail->id_lang = (int) $idLang;
$recipientsTo = $message->getTo();
$recipientsCc = $message->getCc();
$recipientsBcc = $message->getBcc();
if (!is_array($recipientsTo)) {
$recipientsTo = [];
}
if (!is_array($recipientsCc)) {
$recipientsCc = [];
}
if (!is_array($recipientsBcc)) {
$recipientsBcc = [];
}
foreach (array_merge($recipientsTo, $recipientsCc, $recipientsBcc) as $email => $recipient_name) {
/* @var Swift_Address $recipient */
$mail->id = null;
$mail->recipient = Tools::substr($email, 0, 255);
$mail->add();
}
}
return $send;
} catch (\Swift_SwiftException $e) {
PrestaShopLogger::addLog(
'Swift Error: ' . $e->getMessage(),
3,
null,
'Swift_Message'
);
return false;
}
}
protected static function getTemplateBasePath($isoTemplate, $moduleName, $theme)
{
$basePathList = [
_PS_ROOT_DIR_ . '/themes/' . $theme->getName() . '/',
_PS_ROOT_DIR_ . '/themes/' . $theme->get('parent') . '/',
_PS_ROOT_DIR_,
];
if ($moduleName !== false) {
$templateRelativePath = '/modules/' . $moduleName . '/mails/';
} else {
$templateRelativePath = '/mails/';
}
foreach ($basePathList as $base) {
$templatePath = $base . $templateRelativePath;
if (file_exists($templatePath . $isoTemplate . '.txt') || file_exists($templatePath . $isoTemplate . '.html')) {
return $templatePath;
}
}
return '';
}
/**
* @param $idMail Mail ID
*
* @return bool Whether removal succeeded
*/
public static function eraseLog($idMail)
{
return Db::getInstance()->delete('mail', 'id_mail = ' . (int) $idMail);
}
/**
* @return bool
*/
public static function eraseAllLogs()
{
return Db::getInstance()->execute('TRUNCATE TABLE ' . _DB_PREFIX_ . 'mail');
}
/**
* Send a test email.
*
* @param bool $smtpChecked Is SMTP checked?
* @param string $smtp_server SMTP Server hostname
* @param string $content Content of the email
* @param string $subject Subject of the email
* @param bool $type Deprecated
* @param string $to To email address
* @param string $from From email address
* @param string $smtpLogin SMTP login name
* @param string $smtpPassword SMTP password
* @param int $smtpPort SMTP Port
* @param bool|string $smtpEncryption Encryption type. "off" or false disable encryption.
*
* @return bool|string True if succeeded, otherwise the error message
*/
public static function sendMailTest(
$smtpChecked,
$smtp_server,
$content,
$subject,
$type,
$to,
$from,
$smtpLogin,
$smtpPassword,
$smtpPort,
$smtpEncryption
) {
$result = false;
try {
if ($smtpChecked) {
if (Tools::strtolower($smtpEncryption) === 'off') {
$smtpEncryption = false;
}
$smtp = \Swift_SmtpTransport::newInstance($smtp_server, $smtpPort, $smtpEncryption)
->setUsername($smtpLogin)
->setPassword($smtpPassword);
$swift = \Swift_Mailer::newInstance($smtp);
} else {
$swift = \Swift_Mailer::newInstance(\Swift_MailTransport::newInstance());
}
$message = \Swift_Message::newInstance();
$message
->setFrom($from)
->setTo($to)
->setSubject($subject)
->setBody($content);
if ($swift->send($message)) {
$result = true;
}
} catch (\Swift_SwiftException $e) {
$result = $e->getMessage();
}
return $result;
}
/**
* This method is used to get the translation for email Object.
* For an object is forbidden to use htmlentities,
* we have to return a sentence with accents.
*
* @param string $string raw sentence (write directly in file)
*
* @return mixed
*/
public static function l($string, $idLang = null, Context $context = null)
{
global $_LANGMAIL;
if (!$context) {
$context = Context::getContext();
}
if ($idLang === null) {
$idLang = (!isset($context->language) || !is_object($context->language)) ?
(int) Configuration::get('PS_LANG_DEFAULT') :
(int) $context->language->id;
}
$isoCode = Language::getIsoById((int) $idLang);
$file_core = _PS_ROOT_DIR_ . '/mails/' . $isoCode . '/lang.php';
if (Tools::file_exists_cache($file_core) && empty($_LANGMAIL)) {
include $file_core;
}
$fileTheme = _PS_THEME_DIR_ . 'mails/' . $isoCode . '/lang.php';
if (Tools::file_exists_cache($fileTheme)) {
include $fileTheme;
}
if (!is_array($_LANGMAIL)) {
return str_replace('"', '&quot;', $string);
}
$key = str_replace('\'', '\\\'', $string);
return str_replace(
'"',
'&quot;',
Tools::stripslashes(
(array_key_exists($key, $_LANGMAIL) && !empty($_LANGMAIL[$key])) ? $_LANGMAIL[$key] : $string
)
);
}
/* Rewrite of Swift_Message::generateId() without getmypid() */
protected static function generateId($idstring = null)
{
$midparams = [
'utctime' => gmstrftime('%Y%m%d%H%M%S'),
'randint' => mt_rand(),
'customstr' => (preg_match('/^(?<!\\.)[a-z0-9\\.]+(?!\\.)$/iD', $idstring) ? $idstring : 'swift'),
'hostname' => !empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : php_uname('n'),
];
return vsprintf('%s.%d.%s@%s', $midparams);
}
/**
* Check if a multibyte character set is used for the data.
*
* @param string $data Data
*
* @return bool Whether the string uses a multibyte character set
*/
public static function isMultibyte($data)
{
$length = Tools::strlen($data);
for ($i = 0; $i < $length; ++$i) {
if (ord(($data[$i])) > 128) {
return true;
}
}
return false;
}
/**
* MIME encode the string.
*
* @param string $string The string to encode
* @param string $charset The character set to use
* @param string $newline The newline character(s)
*
* @return mixed|string MIME encoded string
*/
public static function mimeEncode($string, $charset = 'UTF-8', $newline = "\r\n")
{
if (!self::isMultibyte($string) && Tools::strlen($string) < 75) {
return $string;
}
$charset = Tools::strtoupper($charset);
$start = '=?' . $charset . '?B?';
$end = '?=';
$sep = $end . $newline . ' ' . $start;
$length = 75 - Tools::strlen($start) - Tools::strlen($end);
$length = $length - ($length % 4);
if ($charset === 'UTF-8') {
$parts = [];
$maxchars = floor(($length * 3) / 4);
$stringLength = Tools::strlen($string);
while ($stringLength > $maxchars) {
$i = (int) $maxchars;
$result = ord($string[$i]);
while ($result >= 128 && $result <= 191) {
$result = ord($string[--$i]);
}
$parts[] = base64_encode(Tools::substr($string, 0, $i));
$string = Tools::substr($string, $i);
$stringLength = Tools::strlen($string);
}
$parts[] = base64_encode($string);
$string = implode($sep, $parts);
} else {
$string = chunk_split(base64_encode($string), $length, $sep);
$string = preg_replace('/' . preg_quote($sep) . '$/', '', $string);
}
return $start . $string . $end;
}
/**
* Automatically convert email to Punycode.
*
* Try to use INTL_IDNA_VARIANT_UTS46 only if defined, else use INTL_IDNA_VARIANT_2003
* See https://wiki.php.net/rfc/deprecate-and-remove-intl_idna_variant_2003
*
* @param string $to Email address
*
* @return string
*/
public static function toPunycode($to)
{
$address = explode('@', $to);
if (empty($address[0]) || empty($address[1])) {
return $to;
}
if (defined('INTL_IDNA_VARIANT_UTS46')) {
return $address[0] . '@' . idn_to_ascii($address[1], 0, INTL_IDNA_VARIANT_UTS46);
}
/*
* INTL_IDNA_VARIANT_2003 const will be removed in PHP 8.
* See https://wiki.php.net/rfc/deprecate-and-remove-intl_idna_variant_2003
*/
if (defined('INTL_IDNA_VARIANT_2003')) {
return $address[0] . '@' . idn_to_ascii($address[1], 0, INTL_IDNA_VARIANT_2003);
}
return $address[0] . '@' . idn_to_ascii($address[1]);
}
/**
* Generic function to dieOrLog with translations.
*
* @param bool $die Should die
* @param string $message Message
* @param array $templates Templates list
* @param string $domain Translation domain
*/
protected static function dieOrLog(
$die,
$message,
$templates = [],
$domain = 'Admin.Advparameters.Notification'
) {
Tools::dieOrLog(
Context::getContext()->getTranslator()->trans(
$message,
$templates,
$domain
),
$die
);
}
}

629
classes/Manufacturer.php Normal file
View File

@@ -0,0 +1,629 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ManufacturerCore.
*/
class ManufacturerCore extends ObjectModel
{
public $id;
/** @var string Name */
public $name;
/** @var string A description */
public $description;
/** @var string A short description */
public $short_description;
/** @var int Address */
public $id_address;
/** @var string Object creation date */
public $date_add;
/** @var string Object last modification date */
public $date_upd;
/** @var string Friendly URL */
public $link_rewrite;
/** @var string Meta title */
public $meta_title;
/** @var string Meta keywords */
public $meta_keywords;
/** @var string Meta description */
public $meta_description;
/** @var bool active */
public $active;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'manufacturer',
'primary' => 'id_manufacturer',
'multilang' => true,
'fields' => array(
'name' => array('type' => self::TYPE_STRING, 'validate' => 'isCatalogName', 'required' => true, 'size' => 64),
'active' => array('type' => self::TYPE_BOOL),
'date_add' => array('type' => self::TYPE_DATE),
'date_upd' => array('type' => self::TYPE_DATE),
/* Lang fields */
'description' => array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml'),
'short_description' => array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml'),
'meta_title' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255),
'meta_description' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 512),
'meta_keywords' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName'),
),
);
protected $webserviceParameters = array(
'fields' => array(
'active' => array(),
'link_rewrite' => array('getter' => 'getLink', 'setter' => false),
),
'associations' => array(
'addresses' => array(
'resource' => 'address',
'setter' => false,
'fields' => array(
'id' => array('xlink_resource' => 'addresses'),
),
),
),
);
/**
* ManufacturerCore constructor.
*
* @param int|null $id
* @param int|null $idLang
*/
public function __construct($id = null, $idLang = null)
{
parent::__construct($id, $idLang);
$this->link_rewrite = $this->getLink();
$this->image_dir = _PS_MANU_IMG_DIR_;
}
/**
* Deletes current Manufacturer from the database.
*
* @return bool `true` if delete was successful
*
* @throws PrestaShopException
*/
public function delete()
{
$address = new Address($this->id_address);
if (Validate::isLoadedObject($address) && !$address->delete()) {
return false;
}
if (parent::delete()) {
CartRule::cleanProductRuleIntegrity('manufacturers', $this->id);
return $this->deleteImage();
}
}
/**
* Delete several objects from database.
*
* return boolean Deletion result
*/
public function deleteSelection($selection)
{
if (!is_array($selection)) {
die(Tools::displayError());
}
$result = true;
foreach ($selection as $id) {
$this->id = (int) $id;
$this->id_address = Manufacturer::getManufacturerAddress();
$result = $result && $this->delete();
}
return $result;
}
/**
* Get Manufacturer Address ID.
*
* @return bool|false|string|null
*/
protected function getManufacturerAddress()
{
if (!(int) $this->id) {
return false;
}
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('SELECT `id_address` FROM ' . _DB_PREFIX_ . 'address WHERE `id_manufacturer` = ' . (int) $this->id);
}
/**
* Return manufacturers.
*
* @param bool $getNbProducts [optional] return products numbers for each
* @param int $idLang Language ID
* @param bool $active
* @param int $p
* @param int $n
* @param bool $allGroup
*
* @return array Manufacturers
*/
public static function getManufacturers($getNbProducts = false, $idLang = 0, $active = true, $p = false, $n = false, $allGroup = false, $group_by = false, $withProduct = false)
{
if (!$idLang) {
$idLang = (int) Configuration::get('PS_LANG_DEFAULT');
}
if (!Group::isFeatureActive()) {
$allGroup = true;
}
$manufacturers = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT m.*, ml.`description`, ml.`short_description`
FROM `' . _DB_PREFIX_ . 'manufacturer` m'
. Shop::addSqlAssociation('manufacturer', 'm') .
'INNER JOIN `' . _DB_PREFIX_ . 'manufacturer_lang` ml ON (m.`id_manufacturer` = ml.`id_manufacturer` AND ml.`id_lang` = ' . (int) $idLang . ')' .
'WHERE 1 ' .
($active ? 'AND m.`active` = 1 ' : '') .
($withProduct ? 'AND m.`id_manufacturer` IN (SELECT `id_manufacturer` FROM `' . _DB_PREFIX_ . 'product`) ' : '') .
($group_by ? ' GROUP BY m.`id_manufacturer`' : '') .
'ORDER BY m.`name` ASC
' . ($p ? ' LIMIT ' . (((int) $p - 1) * (int) $n) . ',' . (int) $n : ''));
if ($manufacturers === false) {
return false;
}
if ($getNbProducts) {
$sqlGroups = '';
if (!$allGroup) {
$groups = FrontController::getCurrentCustomerGroups();
$sqlGroups = (count($groups) ? 'IN (' . implode(',', $groups) . ')' : '=' . (int) Configuration::get('PS_UNIDENTIFIED_GROUP'));
}
$results = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'
SELECT p.`id_manufacturer`, COUNT(DISTINCT p.`id_product`) as nb_products
FROM `' . _DB_PREFIX_ . 'product` p USE INDEX (product_manufacturer)
' . Shop::addSqlAssociation('product', 'p') . '
LEFT JOIN `' . _DB_PREFIX_ . 'manufacturer` as m ON (m.`id_manufacturer`= p.`id_manufacturer`)
WHERE p.`id_manufacturer` != 0 AND product_shop.`visibility` NOT IN ("none")
' . ($active ? ' AND product_shop.`active` = 1 ' : '') . '
' . (Group::isFeatureActive() && $allGroup ? '' : ' AND EXISTS (
SELECT 1
FROM `' . _DB_PREFIX_ . 'category_group` cg
LEFT JOIN `' . _DB_PREFIX_ . 'category_product` cp ON (cp.`id_category` = cg.`id_category`)
WHERE p.`id_product` = cp.`id_product` AND cg.`id_group` ' . $sqlGroups . '
)') . '
GROUP BY p.`id_manufacturer`'
);
$counts = array();
foreach ($results as $result) {
$counts[(int) $result['id_manufacturer']] = (int) $result['nb_products'];
}
foreach ($manufacturers as $key => $manufacturer) {
if (array_key_exists((int) $manufacturer['id_manufacturer'], $counts)) {
$manufacturers[$key]['nb_products'] = $counts[(int) $manufacturer['id_manufacturer']];
} else {
$manufacturers[$key]['nb_products'] = 0;
}
}
}
$totalManufacturers = count($manufacturers);
$rewriteSettings = (int) Configuration::get('PS_REWRITING_SETTINGS');
for ($i = 0; $i < $totalManufacturers; ++$i) {
$manufacturers[$i]['link_rewrite'] = ($rewriteSettings ? Tools::link_rewrite($manufacturers[$i]['name']) : 0);
}
return $manufacturers;
}
/**
* List of manufacturers.
*
* @param int $idLang Specify the id of the language used
*
* @return array Manufacturers lite tree
*/
public static function getLiteManufacturersList($idLang = null, $format = 'default')
{
$idLang = null === $idLang ? Context::getContext()->language->id : (int) $idLang;
$manufacturersList = array();
$manufacturers = Manufacturer::getManufacturers(false, $idLang);
if ($manufacturers && count($manufacturers)) {
foreach ($manufacturers as $manufacturer) {
if ($format === 'sitemap') {
$manufacturersList[] = array(
'id' => 'manufacturer-page-' . (int) $manufacturer['id_manufacturer'],
'label' => $manufacturer['name'],
'url' => Context::getContext()->link->getManufacturerLink($manufacturer['id_manufacturer'], $manufacturer['link_rewrite']),
'children' => array(),
);
} else {
$manufacturersList[] = array(
'id' => (int) $manufacturer['id_manufacturer'],
'link' => Context::getContext()->link->getManufacturerLink($manufacturer['id_manufacturer'], $manufacturer['link_rewrite']),
'name' => $manufacturer['name'],
'desc' => $manufacturer['description'],
'children' => array(),
);
}
}
}
return $manufacturersList;
}
/**
* Return name from id.
*
* @param int $id_manufacturer Manufacturer ID
*
* @return string name
*/
protected static $cacheName = array();
public static function getNameById($idManufacturer)
{
if (!isset(self::$cacheName[$idManufacturer])) {
self::$cacheName[$idManufacturer] = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
'
SELECT `name`
FROM `' . _DB_PREFIX_ . 'manufacturer`
WHERE `id_manufacturer` = ' . (int) $idManufacturer . '
AND `active` = 1'
);
}
return self::$cacheName[$idManufacturer];
}
/**
* Get Manufacturer ID by name.
*
* @param string $name
*
* @return bool|int
*/
public static function getIdByName($name)
{
$result = Db::getInstance()->getRow(
'
SELECT `id_manufacturer`
FROM `' . _DB_PREFIX_ . 'manufacturer`
WHERE `name` = \'' . pSQL($name) . '\''
);
if (isset($result['id_manufacturer'])) {
return (int) $result['id_manufacturer'];
}
return false;
}
/**
* Get link to Manufacturer page.
*
* @return string
*/
public function getLink()
{
return Tools::link_rewrite($this->name);
}
/**
* Get Products by Manufacturer ID.
*
* @param int $idManufacturer
* @param int $idLang
* @param int $p
* @param int $n
* @param null $orderBy
* @param null $orderWay
* @param bool $getTotal
* @param bool $active
* @param bool $activeCategory
* @param Context|null $context
*
* @return array|bool
*/
public static function getProducts(
$idManufacturer,
$idLang,
$p,
$n,
$orderBy = null,
$orderWay = null,
$getTotal = false,
$active = true,
$activeCategory = true,
Context $context = null
) {
if (!$context) {
$context = Context::getContext();
}
$front = true;
if (!in_array($context->controller->controller_type, array('front', 'modulefront'))) {
$front = false;
}
if ($p < 1) {
$p = 1;
}
if (empty($orderBy) || $orderBy == 'position') {
$orderBy = 'name';
}
if (empty($orderWay)) {
$orderWay = 'ASC';
}
if (!Validate::isOrderBy($orderBy) || !Validate::isOrderWay($orderWay)) {
die(Tools::displayError());
}
$groups = FrontController::getCurrentCustomerGroups();
$sqlGroups = count($groups) ? 'IN (' . implode(',', $groups) . ')' : '=' . (int) Configuration::get('PS_UNIDENTIFIED_GROUP');
/* Return only the number of products */
if ($getTotal) {
$sql = '
SELECT p.`id_product`
FROM `' . _DB_PREFIX_ . 'product` p
' . Shop::addSqlAssociation('product', 'p') . '
WHERE p.id_manufacturer = ' . (int) $idManufacturer
. ($active ? ' AND product_shop.`active` = 1' : '') . '
' . ($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '') . '
AND EXISTS (
SELECT 1
FROM `' . _DB_PREFIX_ . 'category_group` cg
LEFT JOIN `' . _DB_PREFIX_ . 'category_product` cp ON (cp.`id_category` = cg.`id_category`)' .
($activeCategory ? ' INNER JOIN `' . _DB_PREFIX_ . 'category` ca ON cp.`id_category` = ca.`id_category` AND ca.`active` = 1' : '') . '
WHERE p.`id_product` = cp.`id_product` AND cg.`id_group` ' . $sqlGroups . '
)';
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
return (int) count($result);
}
if (strpos($orderBy, '.') > 0) {
$orderBy = explode('.', $orderBy);
$orderBy = pSQL($orderBy[0]) . '.`' . pSQL($orderBy[1]) . '`';
}
if ($orderBy == 'price') {
$alias = 'product_shop.';
} elseif ($orderBy == 'name') {
$alias = 'pl.';
} elseif ($orderBy == 'manufacturer_name') {
$orderBy = 'name';
$alias = 'm.';
} elseif ($orderBy == 'quantity') {
$alias = 'stock.';
} else {
$alias = 'p.';
}
$sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity'
. (Combination::isFeatureActive() ? ', product_attribute_shop.minimal_quantity AS product_attribute_minimal_quantity, IFNULL(product_attribute_shop.`id_product_attribute`,0) id_product_attribute' : '') . '
, pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`,
pl.`meta_title`, pl.`name`, pl.`available_now`, pl.`available_later`, image_shop.`id_image` id_image, il.`legend`, m.`name` AS manufacturer_name,
DATEDIFF(
product_shop.`date_add`,
DATE_SUB(
"' . date('Y-m-d') . ' 00:00:00",
INTERVAL ' . (Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20) . ' DAY
)
) > 0 AS new'
. ' FROM `' . _DB_PREFIX_ . 'product` p
' . Shop::addSqlAssociation('product', 'p') .
(Combination::isFeatureActive() ? 'LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_shop` product_attribute_shop
ON (p.`id_product` = product_attribute_shop.`id_product` AND product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop=' . (int) $context->shop->id . ')' : '') . '
LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl
ON (p.`id_product` = pl.`id_product` AND pl.`id_lang` = ' . (int) $idLang . Shop::addSqlRestrictionOnLang('pl') . ')
LEFT JOIN `' . _DB_PREFIX_ . 'image_shop` image_shop
ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop=' . (int) $context->shop->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il
ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $idLang . ')
LEFT JOIN `' . _DB_PREFIX_ . 'manufacturer` m
ON (m.`id_manufacturer` = p.`id_manufacturer`)
' . Product::sqlStock('p', 0);
if (Group::isFeatureActive() || $activeCategory) {
$sql .= 'JOIN `' . _DB_PREFIX_ . 'category_product` cp ON (p.id_product = cp.id_product)';
if (Group::isFeatureActive()) {
$sql .= 'JOIN `' . _DB_PREFIX_ . 'category_group` cg ON (cp.`id_category` = cg.`id_category` AND cg.`id_group` ' . $sqlGroups . ')';
}
if ($activeCategory) {
$sql .= 'JOIN `' . _DB_PREFIX_ . 'category` ca ON cp.`id_category` = ca.`id_category` AND ca.`active` = 1';
}
}
$sql .= '
WHERE p.`id_manufacturer` = ' . (int) $idManufacturer . '
' . ($active ? ' AND product_shop.`active` = 1' : '') . '
' . ($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '') . '
GROUP BY p.id_product
ORDER BY ' . $alias . '`' . bqSQL($orderBy) . '` ' . pSQL($orderWay) . '
LIMIT ' . (((int) $p - 1) * (int) $n) . ',' . (int) $n;
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
if (!$result) {
return false;
}
if ($orderBy == 'price') {
Tools::orderbyPrice($result, $orderWay);
}
return Product::getProductsProperties($idLang, $result);
}
/**
* Get Products by Manufacturer
* (light edition).
*
* @param int $idLang
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public function getProductsLite($idLang)
{
$context = Context::getContext();
$front = true;
if (!in_array($context->controller->controller_type, array('front', 'modulefront'))) {
$front = false;
}
return Db::getInstance()->executeS('
SELECT p.`id_product`, pl.`name`
FROM `' . _DB_PREFIX_ . 'product` p
' . Shop::addSqlAssociation('product', 'p') . '
LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl ON (
p.`id_product` = pl.`id_product`
AND pl.`id_lang` = ' . (int) $idLang . $context->shop->addSqlRestrictionOnLang('pl') . '
)
WHERE p.`id_manufacturer` = ' . (int) $this->id .
($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : ''));
}
/**
* Specify if a manufacturer already in base.
*
* @param int $idManufacturer Manufacturer id
*
* @return bool
*/
public static function manufacturerExists($idManufacturer)
{
$row = Db::getInstance()->getRow(
'
SELECT `id_manufacturer`
FROM ' . _DB_PREFIX_ . 'manufacturer m
WHERE m.`id_manufacturer` = ' . (int) $idManufacturer
);
return isset($row['id_manufacturer']);
}
/**
* Get Manufacturer Addresses.
*
* @param int $idLang
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public function getAddresses($idLang)
{
return Db::getInstance()->executeS(
'
SELECT a.*, cl.name AS `country`, s.name AS `state`
FROM `' . _DB_PREFIX_ . 'address` AS a
LEFT JOIN `' . _DB_PREFIX_ . 'country_lang` AS cl ON (
cl.`id_country` = a.`id_country`
AND cl.`id_lang` = ' . (int) $idLang . '
)
LEFT JOIN `' . _DB_PREFIX_ . 'state` AS s ON (s.`id_state` = a.`id_state`)
WHERE `id_manufacturer` = ' . (int) $this->id . '
AND a.`deleted` = 0'
);
}
/**
* Get Manufacturer Addresses
* (for webservice).
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public function getWsAddresses()
{
return Db::getInstance()->executeS(
'
SELECT a.id_address as id
FROM `' . _DB_PREFIX_ . 'address` AS a
' . Shop::addSqlAssociation('manufacturer', 'a') . '
WHERE a.`id_manufacturer` = ' . (int) $this->id . '
AND a.`deleted` = 0'
);
}
/**
* Set Manufacturer Addresses
* (for webservice).
*
* @param array $idAddresses
*
* @return bool
*/
public function setWsAddresses($idAddresses)
{
$ids = array();
foreach ($idAddresses as $id) {
$ids[] = (int) $id['id'];
}
$result1 = (
Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'address`
SET id_manufacturer = 0
WHERE id_manufacturer = ' . (int) $this->id . '
AND deleted = 0') !== false
);
$result2 = true;
if (count($ids)) {
$result2 = (
Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'address`
SET id_customer = 0, id_supplier = 0, id_manufacturer = ' . (int) $this->id . '
WHERE id_address IN(' . implode(',', $ids) . ')
AND deleted = 0') !== false
);
}
return $result1 && $result2;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ManufacturerAddressCore.
*
* Holds address info of a Manufacturer.
* This class extends AddressCore to be differentiated from other AddressCore objects in DB.
*/
class ManufacturerAddressCore extends AddressCore
{
}

870
classes/Media.php Normal file
View File

@@ -0,0 +1,870 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class MediaCore.
*/
class MediaCore
{
public static $jquery_ui_dependencies = array(
'ui.core' => array('fileName' => 'jquery.ui.core.min.js', 'dependencies' => array(), 'theme' => true),
'ui.widget' => array('fileName' => 'jquery.ui.widget.min.js', 'dependencies' => array(), 'theme' => false),
'ui.mouse' => array('fileName' => 'jquery.ui.mouse.min.js', 'dependencies' => array('ui.core', 'ui.widget'), 'theme' => false),
'ui.position' => array('fileName' => 'jquery.ui.position.min.js', 'dependencies' => array(), 'theme' => false),
'ui.draggable' => array('fileName' => 'jquery.ui.draggable.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.mouse'), 'theme' => false),
'ui.droppable' => array('fileName' => 'jquery.ui.droppable.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.mouse', 'ui.draggable'), 'theme' => false),
'ui.resizable' => array('fileName' => 'jquery.ui.resizable.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.mouse'), 'theme' => true),
'ui.selectable' => array('fileName' => 'jquery.ui.selectable.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.mouse'), 'theme' => true),
'ui.sortable' => array('fileName' => 'jquery.ui.sortable.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.mouse'), 'theme' => true),
'ui.autocomplete' => array('fileName' => 'jquery.ui.autocomplete.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.position', 'ui.menu'), 'theme' => true),
'ui.button' => array('fileName' => 'jquery.ui.button.min.js', 'dependencies' => array('ui.core', 'ui.widget'), 'theme' => true),
'ui.dialog' => array('fileName' => 'jquery.ui.dialog.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.position', 'ui.button'), 'theme' => true),
'ui.menu' => array('fileName' => 'jquery.ui.menu.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.position'), 'theme' => true),
'ui.slider' => array('fileName' => 'jquery.ui.slider.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.mouse'), 'theme' => true),
'ui.spinner' => array('fileName' => 'jquery.ui.spinner.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.button'), 'theme' => true),
'ui.tabs' => array('fileName' => 'jquery.ui.tabs.min.js', 'dependencies' => array('ui.core', 'ui.widget'), 'theme' => true),
'ui.datepicker' => array('fileName' => 'jquery.ui.datepicker.min.js', 'dependencies' => array('ui.core'), 'theme' => true),
'ui.progressbar' => array('fileName' => 'jquery.ui.progressbar.min.js', 'dependencies' => array('ui.core', 'ui.widget'), 'theme' => true),
'ui.tooltip' => array('fileName' => 'jquery.ui.tooltip.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'ui.position', 'effects.core'), 'theme' => true),
'ui.accordion' => array('fileName' => 'jquery.ui.accordion.min.js', 'dependencies' => array('ui.core', 'ui.widget', 'effects.core'), 'theme' => true),
'effects.core' => array('fileName' => 'jquery.effects.core.min.js', 'dependencies' => array(), 'theme' => false),
'effects.blind' => array('fileName' => 'jquery.effects.blind.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
'effects.bounce' => array('fileName' => 'jquery.effects.bounce.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
'effects.clip' => array('fileName' => 'jquery.effects.clip.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
'effects.drop' => array('fileName' => 'jquery.effects.drop.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
'effects.explode' => array('fileName' => 'jquery.effects.explode.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
'effects.fade' => array('fileName' => 'jquery.effects.fade.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
'effects.fold' => array('fileName' => 'jquery.effects.fold.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
'effects.highlight' => array('fileName' => 'jquery.effects.highlight.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
'effects.pulsate' => array('fileName' => 'jquery.effects.pulsate.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
'effects.scale' => array('fileName' => 'jquery.effects.scale.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
'effects.shake' => array('fileName' => 'jquery.effects.shake.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
'effects.slide' => array('fileName' => 'jquery.effects.slide.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
'effects.transfer' => array('fileName' => 'jquery.effects.transfer.min.js', 'dependencies' => array('effects.core'), 'theme' => false),
);
private static $jquery_ui_datepicker_iso_code = array(
'bn' => 'en',
'bz' => 'en',
'dh' => 'de',
'gb' => 'en-GB',
'ag' => 'es',
'cb' => 'es',
'mx' => 'es',
'pe' => 'es',
've' => 'es',
'qc' => 'fr-CA',
'ga' => 'en',
'lo' => 'en',
'br' => 'pt-BR',
'sh' => 'en',
'si' => 'sl',
'ug' => 'en',
'ur' => 'en',
'vn' => 'vi',
'zh' => 'zh-CN',
'tw' => 'zh-TW',
);
/**
* @var array list of javascript definitions
*/
protected static $js_def = array();
/**
* @var array list of javascript inline scripts
*/
protected static $inline_script = array();
/**
* @var array list of javascript external scripts
*/
protected static $inline_script_src = array();
/**
* @var string pattern used in replaceByAbsoluteURL
*/
public static $pattern_callback = '#(url\((?![\'"]?(?:data:|//|https?:))(?:\'|")?)([^\)\'"]*)(?=[\'"]?\))#s';
/**
* @var string used for preg_replace_callback parameter (avoid global)
*/
protected static $current_css_file;
/**
* @var string pattern used in packJSinHTML
*/
public static $pattern_js = '/(<\s*script(?:\s+[^>]*(?:javascript|src)[^>]*)?\s*>)(.*)(<\s*\/script\s*[^>]*>)/Uims';
protected static $pattern_keepinline = 'data-keepinline';
/**
* Minify JS.
*
* @param string $jsContent
*
* @return string
*/
public static function packJS($jsContent)
{
if (!empty($jsContent)) {
try {
$jsContent = JSMin::minify($jsContent);
} catch (Exception $e) {
if (_PS_MODE_DEV_) {
echo $e->getMessage();
}
return ';' . trim($jsContent, ';') . ';';
}
}
return ';' . trim($jsContent, ';') . ';';
}
/**
* Minify CSS.
*
* @param string $cssContent
* @param bool $fileUri
* @param array $importUrl
*
* @return bool|string
*/
public static function minifyCSS($cssContent, $fileUri = false, &$importUrl = array())
{
Media::$current_css_file = $fileUri;
if (strlen($cssContent) > 0) {
$cssContent = Minify_CSSmin::minify($cssContent);
$limit = Media::getBackTrackLimit();
$cssContent = preg_replace_callback(Media::$pattern_callback, array('Media', 'replaceByAbsoluteURL'), $cssContent, $limit);
$cssContent = str_replace('\'images_ie/', '\'images/', $cssContent);
$cssContent = preg_replace_callback('#(AlphaImageLoader\(src=\')([^\']*\',)#s', array('Media', 'replaceByAbsoluteURL'), $cssContent);
// Store all import url
preg_match_all('#@(import|charset) .*?;#i', $cssContent, $m);
for ($i = 0, $total = count($m[0]); $i < $total; ++$i) {
if (isset($m[1][$i]) && $m[1][$i] == 'import') {
$importUrl[] = $m[0][$i];
}
$cssContent = str_replace($m[0][$i], '', $cssContent);
}
return trim($cssContent);
}
return false;
}
/**
* Replace URL by absolute URL.
*
* @param array $matches
*
* @return bool|string
*/
public static function replaceByAbsoluteURL($matches)
{
if (array_key_exists(1, $matches) && array_key_exists(2, $matches)) {
if (!preg_match('/^(?:https?:)?\/\//iUs', $matches[2])) {
$protocolLink = Tools::getCurrentUrlProtocolPrefix();
$sep = '/';
$tmp = $matches[2][0] == $sep ? $matches[2] : dirname(Media::$current_css_file) . $sep . ltrim($matches[2], $sep);
$server = Tools::getMediaServer($tmp);
return $matches[1] . $protocolLink . $server . $tmp;
} else {
return $matches[0];
}
}
return false;
}
/**
* addJS return javascript path.
*
* @param mixed $jsUri
*
* @return string
*/
public static function getJSPath($jsUri)
{
return Media::getMediaPath($jsUri);
}
/**
* addCSS return stylesheet path.
*
* @param mixed $cssUri
* @param string $cssMediaType
* @param bool $needRtl
*
* @return string
*/
public static function getCSSPath($cssUri, $cssMediaType = 'all', $needRtl = true)
{
// RTL Ready: search and load rtl css file if it's not originally rtl
if ($needRtl && Context::getContext()->language->is_rtl) {
$cssUriRtl = preg_replace('/(^[^.].*)(\.css)$/', '$1_rtl.css', $cssUri);
$rtlMedia = Media::getMediaPath($cssUriRtl, $cssMediaType);
if ($rtlMedia != false) {
return $rtlMedia;
}
}
// End RTL
return Media::getMediaPath($cssUri, $cssMediaType);
}
/**
* Get Media path.
*
* @param string $mediaUri
* @param null $cssMediaType
*
* @return array|bool|mixed|string
*/
public static function getMediaPath($mediaUri, $cssMediaType = null)
{
if (is_array($mediaUri) || $mediaUri === null || empty($mediaUri)) {
return false;
}
$urlData = parse_url($mediaUri);
if (!is_array($urlData)) {
return false;
}
if (!array_key_exists('host', $urlData)) {
$mediaUriHostMode = '/' . ltrim(str_replace(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, _PS_CORE_DIR_), __PS_BASE_URI__, $mediaUri), '/\\');
$mediaUri = '/' . ltrim(str_replace(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, _PS_ROOT_DIR_), __PS_BASE_URI__, $mediaUri), '/\\');
// remove PS_BASE_URI on _PS_ROOT_DIR_ for the following
$fileUri = _PS_ROOT_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $mediaUri);
$fileUriHostMode = _PS_CORE_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, Tools::str_replace_once(_PS_CORE_DIR_, '', $mediaUri));
if (!@filemtime($fileUri) || @filesize($fileUri) === 0) {
if (!defined('_PS_HOST_MODE_')) {
return false;
} elseif (!@filemtime($fileUriHostMode) || @filesize($fileUriHostMode) === 0) {
return false;
} else {
$mediaUri = $mediaUriHostMode;
}
}
$mediaUri = str_replace('//', '/', $mediaUri);
}
if ($cssMediaType) {
return array($mediaUri => $cssMediaType);
}
return $mediaUri;
}
/**
* return jquery path.
*
* @param mixed $version
*
* @return string
*/
public static function getJqueryPath($version = null, $folder = null, $minifier = true)
{
$addNoConflict = false;
if ($version === null) {
$version = _PS_JQUERY_VERSION_;
} //set default version
elseif (preg_match('/^([0-9\.]+)$/Ui', $version)) {
$addNoConflict = true;
} else {
return false;
}
if ($folder === null) {
$folder = _PS_JS_DIR_ . 'jquery/';
} //set default folder
//check if file exist
$file = $folder . 'jquery-' . $version . ($minifier ? '.min.js' : '.js');
// remove PS_BASE_URI on _PS_ROOT_DIR_ for the following
$urlData = parse_url($file);
$fileUri = _PS_ROOT_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $urlData['path']);
$fileUriHostMode = _PS_CORE_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $urlData['path']);
// check if js files exists, if not try to load query from ajax.googleapis.com
$return = array();
if (@filemtime($fileUri) || (defined('_PS_HOST_MODE_') && @filemtime($fileUriHostMode))) {
$return[] = Media::getJSPath($file);
} else {
$return[] = Media::getJSPath(Tools::getCurrentUrlProtocolPrefix() . 'ajax.googleapis.com/ajax/libs/jquery/' . $version . '/jquery' . ($minifier ? '.min.js' : '.js'));
}
if ($addNoConflict) {
$return[] = Media::getJSPath(Context::getContext()->shop->getBaseURL(true, false) . _PS_JS_DIR_ . 'jquery/jquery.noConflict.php?version=' . $version);
}
// added jQuery migrate for compatibility with new version of jQuery
// will be removed when using latest version of jQuery
$return[] = Media::getJSPath(_PS_JS_DIR_ . 'jquery/jquery-migrate-1.2.1.min.js');
return $return;
}
/**
* return jqueryUI component path.
*
* @param mixed $component
*
* @return string
*/
public static function getJqueryUIPath($component, $theme, $checkDependencies)
{
$uiPath = array('js' => array(), 'css' => array());
$folder = _PS_JS_DIR_ . 'jquery/ui/';
$file = 'jquery.' . $component . '.min.js';
$urlData = parse_url($folder . $file);
$fileUri = _PS_ROOT_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $urlData['path']);
$fileUriHostMode = _PS_CORE_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $urlData['path']);
$uiTmp = array();
if (isset(Media::$jquery_ui_dependencies[$component]) && Media::$jquery_ui_dependencies[$component]['theme'] && $checkDependencies) {
$themeCss = Media::getCSSPath($folder . 'themes/' . $theme . '/jquery.ui.theme.css');
$compCss = Media::getCSSPath($folder . 'themes/' . $theme . '/jquery.' . $component . '.css');
if (!empty($themeCss) || $themeCss) {
$uiPath['css'] = array_merge($uiPath['css'], $themeCss);
}
if (!empty($compCss) || $compCss) {
$uiPath['css'] = array_merge($uiPath['css'], $compCss);
}
}
if ($checkDependencies && array_key_exists($component, self::$jquery_ui_dependencies)) {
foreach (self::$jquery_ui_dependencies[$component]['dependencies'] as $dependency) {
$uiTmp[] = Media::getJqueryUIPath($dependency, $theme, false);
if (self::$jquery_ui_dependencies[$dependency]['theme']) {
$depCss = Media::getCSSPath($folder . 'themes/' . $theme . '/jquery.' . $dependency . '.css');
}
if (isset($depCss) && (!empty($depCss) || $depCss)) {
$uiPath['css'] = array_merge($uiPath['css'], $depCss);
}
}
}
if (@filemtime($fileUri) || (defined('_PS_HOST_MODE_') && @filemtime($fileUriHostMode))) {
if (!empty($uiTmp)) {
foreach ($uiTmp as $ui) {
if (!empty($ui['js'])) {
$uiPath['js'][] = $ui['js'];
}
if (!empty($ui['css'])) {
$uiPath['css'][] = $ui['css'];
}
}
$uiPath['js'][] = Media::getJSPath($folder . $file);
} else {
$uiPath['js'] = Media::getJSPath($folder . $file);
}
}
//add i18n file for datepicker
if ($component == 'ui.datepicker') {
if (!is_array($uiPath['js'])) {
$uiPath['js'] = array($uiPath['js']);
}
$datePickerIsoCode = Context::getContext()->language->iso_code;
if (array_key_exists($datePickerIsoCode, self::$jquery_ui_datepicker_iso_code)) {
$datePickerIsoCode = self::$jquery_ui_datepicker_iso_code[$datePickerIsoCode];
}
$uiPath['js'][] = Media::getJSPath($folder . 'i18n/jquery.ui.datepicker-' . $datePickerIsoCode . '.js');
}
return $uiPath;
}
/**
* return jquery plugin path.
*
* @param mixed $name
* @param string|null $folder
*
* @return bool|string
*/
public static function getJqueryPluginPath($name, $folder = null)
{
$pluginPath = array('js' => array(), 'css' => array());
if ($folder === null) {
$folder = _PS_JS_DIR_ . 'jquery/plugins/';
} //set default folder
$file = 'jquery.' . $name . '.js';
$urlData = parse_url($folder);
$fileUri = _PS_ROOT_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $urlData['path']);
$fileUriHostMode = _PS_CORE_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $urlData['path']);
if (@file_exists($fileUri . $file) || (defined('_PS_HOST_MODE_') && @file_exists($fileUriHostMode . $file))) {
$pluginPath['js'] = Media::getJSPath($folder . $file);
} elseif (@file_exists($fileUri . $name . '/' . $file) || (defined('_PS_HOST_MODE_') && @file_exists($fileUriHostMode . $name . '/' . $file))) {
$pluginPath['js'] = Media::getJSPath($folder . $name . '/' . $file);
} else {
return false;
}
$pluginPath['css'] = Media::getJqueryPluginCSSPath($name, $folder);
return $pluginPath;
}
/**
* return jquery plugin css path if exist.
*
* @param mixed $name
* @param string|null $folder
*
* @return bool|string
*/
public static function getJqueryPluginCSSPath($name, $folder = null)
{
if ($folder === null) {
$folder = _PS_JS_DIR_ . 'jquery/plugins/';
} //set default folder
$file = 'jquery.' . $name . '.css';
$urlData = parse_url($folder);
$fileUri = _PS_ROOT_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $urlData['path']);
$fileUriHostMode = _PS_CORE_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $urlData['path']);
if (@file_exists($fileUri . $file) || (defined('_PS_HOST_MODE_') && @file_exists($fileUriHostMode . $file))) {
return Media::getCSSPath($folder . $file);
} elseif (@file_exists($fileUri . $name . '/' . $file) || (defined('_PS_HOST_MODE_') && @file_exists($fileUriHostMode . $name . '/' . $file))) {
return Media::getCSSPath($folder . $name . '/' . $file);
} else {
return false;
}
}
/**
* Combine Compress and Cache CSS (ccc) calls.
*
* @param array $cssFiles
*
* @return array processed css_files
*/
public static function cccCss($cssFiles)
{
//inits
$cssFilesByMedia = array();
$externalCssFiles = array();
$compressedCssFiles = array();
$compressedCssFilesNotFound = array();
$compressedCssFilesInfos = array();
$protocolLink = Tools::getCurrentUrlProtocolPrefix();
$cachePath = _PS_THEME_DIR_ . 'cache/';
// group css files by media
foreach ($cssFiles as $filename => $media) {
if (!array_key_exists($media, $cssFilesByMedia)) {
$cssFilesByMedia[$media] = array();
}
$infos = array();
$infos['uri'] = $filename;
$urlData = parse_url($filename);
if (array_key_exists('host', $urlData)) {
$externalCssFiles[$filename] = $media;
continue;
}
$infos['path'] = _PS_ROOT_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, '/', $urlData['path']);
if (!@filemtime($infos['path'])) {
$infos['path'] = _PS_CORE_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, '/', $urlData['path']);
}
$cssFilesByMedia[$media]['files'][] = $infos;
if (!array_key_exists('date', $cssFilesByMedia[$media])) {
$cssFilesByMedia[$media]['date'] = 0;
}
$cssFilesByMedia[$media]['date'] = max(
(int) @filemtime($infos['path']),
$cssFilesByMedia[$media]['date']
);
if (!array_key_exists($media, $compressedCssFilesInfos)) {
$compressedCssFilesInfos[$media] = array('key' => '');
}
$compressedCssFilesInfos[$media]['key'] .= $filename;
}
// get compressed css file infos
$version = (int) Configuration::get('PS_CCCCSS_VERSION');
foreach ($compressedCssFilesInfos as $media => &$info) {
$key = md5($info['key'] . $protocolLink);
$filename = $cachePath . 'v_' . $version . '_' . $key . '_' . $media . '.css';
$info = array(
'key' => $key,
'date' => (int) @filemtime($filename),
);
}
foreach ($cssFilesByMedia as $media => $mediaInfos) {
if ($mediaInfos['date'] > $compressedCssFilesInfos[$media]['date']) {
if ($compressedCssFilesInfos[$media]['date']) {
Configuration::updateValue('PS_CCCCSS_VERSION', ++$version);
break;
}
}
}
// aggregate and compress css files content, write new caches files
$importUrl = array();
foreach ($cssFilesByMedia as $media => $mediaInfos) {
$cacheFilename = $cachePath . 'v_' . $version . '_' . $compressedCssFilesInfos[$media]['key'] . '_' . $media . '.css';
if ($mediaInfos['date'] > $compressedCssFilesInfos[$media]['date']) {
$cacheFilename = $cachePath . 'v_' . $version . '_' . $compressedCssFilesInfos[$media]['key'] . '_' . $media . '.css';
$compressedCssFiles[$media] = '';
foreach ($mediaInfos['files'] as $file_infos) {
if (file_exists($file_infos['path'])) {
$compressedCssFiles[$media] .= Media::minifyCSS(file_get_contents($file_infos['path']), $file_infos['uri'], $importUrl);
} else {
$compressedCssFilesNotFound[] = $file_infos['path'];
}
}
if (!empty($compressedCssFilesNotFound)) {
$content = '/* WARNING ! file(s) not found : "' .
implode(',', $compressedCssFilesNotFound) .
'" */' . "\n" . $compressedCssFiles[$media];
} else {
$content = $compressedCssFiles[$media];
}
$content = '@charset "UTF-8";' . "\n" . $content;
$content = implode('', $importUrl) . $content;
file_put_contents($cacheFilename, $content);
}
$compressedCssFiles[$media] = $cacheFilename;
}
// rebuild the original css_files array
$cssFiles = array();
foreach ($compressedCssFiles as $media => $filename) {
$url = str_replace(_PS_THEME_DIR_, _THEMES_DIR_ . _THEME_NAME_ . '/', $filename);
$cssFiles[$protocolLink . Tools::getMediaServer($url) . $url] = $media;
}
return array_merge($externalCssFiles, $cssFiles);
}
/**
* Get backtrack limit.
*
* @return int|string|null
*/
public static function getBackTrackLimit()
{
static $limit = null;
if ($limit === null) {
$limit = @ini_get('pcre.backtrack_limit');
if (!$limit) {
$limit = -1;
}
}
return $limit;
}
/**
* Combine Compress and Cache (ccc) JS calls.
*
* @param array $jsFiles
*
* @return array processed js_files
*/
public static function cccJS($jsFiles)
{
//inits
$compressedJsFilesNotFound = array();
$jsFilesInfos = array();
$jsFilesDate = 0;
$compressedJsFilename = '';
$jsExternalFiles = array();
$protocolLink = Tools::getCurrentUrlProtocolPrefix();
$cachePath = _PS_THEME_DIR_ . 'cache/';
// get js files infos
foreach ($jsFiles as $filename) {
if (Validate::isAbsoluteUrl($filename)) {
$jsExternalFiles[] = $filename;
} else {
$infos = array();
$infos['uri'] = $filename;
$urlData = parse_url($filename);
$infos['path'] = _PS_ROOT_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, '/', $urlData['path']);
if (!@filemtime($infos['path'])) {
$infos['path'] = _PS_CORE_DIR_ . Tools::str_replace_once(__PS_BASE_URI__, '/', $urlData['path']);
}
$jsFilesInfos[] = $infos;
$jsFilesDate = max(
(int) @filemtime($infos['path']),
$jsFilesDate
);
$compressedJsFilename .= $filename;
}
}
// get compressed js file infos
$compressedJsFilename = md5($compressedJsFilename);
$version = (int) Configuration::get('PS_CCCJS_VERSION');
$compressedJsPath = $cachePath . 'v_' . $version . '_' . $compressedJsFilename . '.js';
$compressedJsFileDate = (int) @filemtime($compressedJsPath);
// aggregate and compress js files content, write new caches files
if ($jsFilesDate > $compressedJsFileDate) {
if ($compressedJsFileDate) {
Configuration::updateValue('PS_CCCJS_VERSION', ++$version);
}
$compressedJsPath = $cachePath . 'v_' . $version . '_' . $compressedJsFilename . '.js';
$content = '';
foreach ($jsFilesInfos as $fileInfos) {
if (file_exists($fileInfos['path'])) {
$tmpContent = file_get_contents($fileInfos['path']);
if (preg_match('@\.(min|pack)\.[^/]+$@', $fileInfos['path'], $matches)) {
$content .= preg_replace('/\/\/@\ssourceMappingURL\=[_a-zA-Z0-9-.]+\.' . $matches[1] . '\.map\s+/', '', $tmpContent);
} else {
$content .= Media::packJS($tmpContent);
}
} else {
$compressedJsFilesNotFound[] = $fileInfos['path'];
}
}
if (!empty($compressedJsFilesNotFound)) {
$content = '/* WARNING ! file(s) not found : "' .
implode(',', $compressedJsFilesNotFound) .
'" */' . "\n" . $content;
}
file_put_contents($compressedJsPath, $content);
}
// rebuild the original js_files array
if (strpos($compressedJsPath, _PS_ROOT_DIR_) !== false) {
$url = str_replace(_PS_ROOT_DIR_ . '/', __PS_BASE_URI__, $compressedJsPath);
}
if (strpos($compressedJsPath, _PS_CORE_DIR_) !== false) {
$url = str_replace(_PS_CORE_DIR_ . '/', __PS_BASE_URI__, $compressedJsPath);
}
return array_merge(array($protocolLink . Tools::getMediaServer($url) . $url), $jsExternalFiles);
}
/**
* Clear theme cache.
*/
public static function clearCache()
{
$files = array_merge(
glob(_PS_THEME_DIR_ . 'assets/cache/*', GLOB_NOSORT),
glob(_PS_THEME_DIR_ . 'cache/*', GLOB_NOSORT)
);
foreach ($files as $file) {
if ('index.php' !== basename($file)) {
Tools::deleteFile($file);
}
}
$version = (int) Configuration::get('PS_CCCJS_VERSION');
Configuration::updateValue('PS_CCCJS_VERSION', ++$version);
$version = (int) Configuration::get('PS_CCCCSS_VERSION');
Configuration::updateValue('PS_CCCCSS_VERSION', ++$version);
}
/**
* Get JS definitions.
*
* @return array JS definitions
*/
public static function getJsDef()
{
ksort(Media::$js_def);
return Media::$js_def;
}
/**
* Get JS inline script.
*
* @return array inline script
*/
public static function getInlineScript()
{
return Media::$inline_script;
}
/**
* Add a new javascript definition at bottom of page.
*
* @param mixed $jsDef
*/
public static function addJsDef($jsDef)
{
if (is_array($jsDef)) {
foreach ($jsDef as $key => $js) {
Media::$js_def[$key] = $js;
}
} elseif ($jsDef) {
Media::$js_def[] = $jsDef;
}
}
/**
* Add a new javascript definition from a capture at bottom of page.
*
* @param mixed $params
* @param string $content
* @param Smarty $smarty
* @param bool $repeat
*/
public static function addJsDefL($params, $content, $smarty = null, &$repeat = false)
{
if (!$repeat && isset($params) && Tools::strlen($content)) {
if (!is_array($params)) {
$params = (array) $params;
}
foreach ($params as $param) {
Media::$js_def[$param] = $content;
}
}
}
public static function deferInlineScripts($output)
{
/* Try to enqueue in js_files inline scripts with src but without conditionnal comments */
$dom = new DOMDocument();
libxml_use_internal_errors(true);
@$dom->loadHTML(($output));
libxml_use_internal_errors(false);
$scripts = $dom->getElementsByTagName('script');
if (is_object($scripts) && $scripts->length) {
foreach ($scripts as $script) {
/** @var DOMElement $script */
if ($src = $script->getAttribute('src')) {
if (substr($src, 0, 2) == '//') {
$src = Tools::getCurrentUrlProtocolPrefix() . substr($src, 2);
}
$patterns = array(
'#code\.jquery\.com/jquery-([0-9\.]+)(\.min)*\.js$#Ui',
'#ajax\.googleapis\.com/ajax/libs/jquery/([0-9\.]+)/jquery(\.min)*\.js$#Ui',
'#ajax\.aspnetcdn\.com/ajax/jquery/jquery-([0-9\.]+)(\.min)*\.js$#Ui',
'#cdnjs\.cloudflare\.com/ajax/libs/jquery/([0-9\.]+)/jquery(\.min)*\.js$#Ui',
'#/jquery-([0-9\.]+)(\.min)*\.js$#Ui',
);
foreach ($patterns as $pattern) {
$matches = array();
if (preg_match($pattern, $src, $matches)) {
$minifier = $version = false;
if (isset($matches[2]) && $matches[2]) {
$minifier = (bool) $matches[2];
}
if (isset($matches[1]) && $matches[1]) {
$version = $matches[1];
}
if ($version) {
if ($version != _PS_JQUERY_VERSION_) {
Context::getContext()->controller->addJquery($version, null, $minifier);
}
Media::$inline_script_src[] = $src;
}
}
}
if (!in_array($src, Media::$inline_script_src) && !$script->getAttribute(Media::$pattern_keepinline)) {
Context::getContext()->controller->addJS($src);
}
}
}
}
$output = preg_replace_callback(Media::$pattern_js, array('Media', 'deferScript'), $output);
return $output;
}
/**
* Get all JS scripts and place it to bottom
* To be used in callback with deferInlineScripts.
*
* @param array $matches
*
* @return bool|string Empty string or original script lines
*/
public static function deferScript($matches)
{
if (!is_array($matches)) {
return false;
}
$inline = '';
if (isset($matches[0])) {
$original = trim($matches[0]);
}
if (isset($matches[2])) {
$inline = trim($matches[2]);
}
/* This is an inline script, add its content to inline scripts stack then remove it from content */
if (!empty($inline) && preg_match(Media::$pattern_js, $original) !== false && !preg_match('/' . Media::$pattern_keepinline . '/', $original) && Media::$inline_script[] = $inline) {
return '';
}
/* This is an external script, if it already belongs to js_files then remove it from content */
preg_match('/src\s*=\s*["\']?([^"\']*)[^>]/ims', $original, $results);
if (array_key_exists(1, $results)) {
if (substr($results[1], 0, 2) == '//') {
$protocolLink = Tools::getCurrentUrlProtocolPrefix();
$results[1] = $protocolLink . ltrim($results[1], '/');
}
if (in_array($results[1], Context::getContext()->controller->js_files) || in_array($results[1], Media::$inline_script_src)) {
return '';
}
}
/* return original string because no match was found */
return "\n" . $original;
}
}

193
classes/Message.php Normal file
View File

@@ -0,0 +1,193 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class MessageCore.
*/
class MessageCore extends ObjectModel
{
public $id;
/** @var string message content */
public $message;
/** @var int Cart ID (if applicable) */
public $id_cart;
/** @var int Order ID (if applicable) */
public $id_order;
/** @var int Customer ID (if applicable) */
public $id_customer;
/** @var int Employee ID (if applicable) */
public $id_employee;
/** @var bool Message is not displayed to the customer */
public $private;
/** @var string Object creation date */
public $date_add;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'message',
'primary' => 'id_message',
'fields' => array(
'message' => array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'required' => true, 'size' => 1600),
'id_cart' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'id_order' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'id_customer' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'id_employee' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'private' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
),
);
protected $webserviceParameters = array(
'fields' => array(
'id_cart' => array(
'xlink_resource' => 'carts',
),
'id_order' => array(
'xlink_resource' => 'orders',
),
'id_customer' => array(
'xlink_resource' => 'customers',
),
'id_employee' => array(
'xlink_resource' => 'employees',
),
),
);
/**
* Return the last message from cart.
*
* @param int $idCart Cart ID
*
* @return array Message
*/
public static function getMessageByCartId($idCart)
{
return Db::getInstance()->getRow(
'
SELECT *
FROM `' . _DB_PREFIX_ . 'message`
WHERE `id_cart` = ' . (int) $idCart
);
}
/**
* Return messages from Order ID.
*
* @param int $idOrder Order ID
* @param bool $private return WITH private messages
*
* @return array Messages
*/
public static function getMessagesByOrderId($idOrder, $private = false, Context $context = null)
{
if (!Validate::isBool($private)) {
die(Tools::displayError());
}
if (!$context) {
$context = Context::getContext();
}
return Db::getInstance()->executeS('
SELECT m.*, c.`firstname` AS cfirstname, c.`lastname` AS clastname, e.`firstname` AS efirstname, e.`lastname` AS elastname,
(COUNT(mr.id_message) = 0 AND m.id_customer != 0) AS is_new_for_me
FROM `' . _DB_PREFIX_ . 'message` m
LEFT JOIN `' . _DB_PREFIX_ . 'customer` c ON m.`id_customer` = c.`id_customer`
LEFT JOIN `' . _DB_PREFIX_ . 'message_readed` mr
ON mr.`id_message` = m.`id_message`
AND mr.`id_employee` = ' . (isset($context->employee) ? (int) $context->employee->id : '\'\'') . '
LEFT OUTER JOIN `' . _DB_PREFIX_ . 'employee` e ON e.`id_employee` = m.`id_employee`
WHERE id_order = ' . (int) $idOrder . '
' . (!$private ? ' AND m.`private` = 0' : '') . '
GROUP BY m.id_message
ORDER BY m.date_add DESC
');
}
/**
* Return messages from Cart ID.
*
* @param int $id_order Order ID
* @param bool $private return WITH private messages
*
* @return array Messages
*/
public static function getMessagesByCartId($idCart, $private = false, Context $context = null)
{
if (!Validate::isBool($private)) {
die(Tools::displayError());
}
if (!$context) {
$context = Context::getContext();
}
return Db::getInstance()->executeS('
SELECT m.*, c.`firstname` AS cfirstname, c.`lastname` AS clastname, e.`firstname` AS efirstname, e.`lastname` AS elastname,
(COUNT(mr.id_message) = 0 AND m.id_customer != 0) AS is_new_for_me
FROM `' . _DB_PREFIX_ . 'message` m
LEFT JOIN `' . _DB_PREFIX_ . 'customer` c ON m.`id_customer` = c.`id_customer`
LEFT JOIN `' . _DB_PREFIX_ . 'message_readed` mr ON (mr.id_message = m.id_message AND mr.id_employee = ' . (int) $context->employee->id . ')
LEFT OUTER JOIN `' . _DB_PREFIX_ . 'employee` e ON e.`id_employee` = m.`id_employee`
WHERE id_cart = ' . (int) $idCart . '
' . (!$private ? ' AND m.`private` = 0' : '') . '
GROUP BY m.id_message
ORDER BY m.date_add DESC
');
}
/**
* Registered a message 'readed'.
*
* @param int $idMessage Message ID
* @param int $id_emplyee Employee ID
*
* @return bool
*/
public static function markAsReaded($idMessage, $idEmployee)
{
if (!Validate::isUnsignedId($idMessage) || !Validate::isUnsignedId($idEmployee)) {
die(Tools::displayError());
}
$result = Db::getInstance()->execute('
INSERT INTO ' . _DB_PREFIX_ . 'message_readed (id_message , id_employee , date_add) VALUES
(' . (int) $idMessage . ', ' . (int) $idEmployee . ', NOW());
');
return $result;
}
}

546
classes/Meta.php Normal file
View File

@@ -0,0 +1,546 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Adapter\Presenter\Object\ObjectPresenter;
/**
* Class MetaCore.
*/
class MetaCore extends ObjectModel
{
public $page;
public $configurable = 1;
public $title;
public $description;
public $keywords;
public $url_rewrite;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'meta',
'primary' => 'id_meta',
'multilang' => true,
'multilang_shop' => true,
'fields' => array(
'page' => array('type' => self::TYPE_STRING, 'validate' => 'isFileName', 'required' => true, 'size' => 64),
'configurable' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
/* Lang fields */
'title' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 128),
'description' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255),
'keywords' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255),
'url_rewrite' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isLinkRewrite', 'size' => 255),
),
);
/**
* Get pages.
*
* @param bool $excludeFilled
* @param bool $addPage
*
* @return array
*/
public static function getPages($excludeFilled = false, $addPage = false)
{
$selectedPages = array();
if (!$files = Tools::scandir(_PS_CORE_DIR_ . DIRECTORY_SEPARATOR . 'controllers' . DIRECTORY_SEPARATOR . 'front' . DIRECTORY_SEPARATOR, 'php', '', true)) {
die(Tools::displayError(Context::getContext()->getTranslator()->trans('Cannot scan root directory', array(), 'Admin.Notifications.Error')));
}
if (!$overrideFiles = Tools::scandir(_PS_CORE_DIR_ . DIRECTORY_SEPARATOR . 'override' . DIRECTORY_SEPARATOR . 'controllers' . DIRECTORY_SEPARATOR . 'front' . DIRECTORY_SEPARATOR, 'php', '', true)) {
die(Tools::displayError(Context::getContext()->getTranslator()->trans('Cannot scan "override" directory', array(), 'Admin.Notifications.Error')));
}
$files = array_values(array_unique(array_merge($files, $overrideFiles)));
// Exclude pages forbidden
$exludePages = array(
'category',
'changecurrency',
'cms',
'footer',
'header',
'pagination',
'product',
'product-sort',
'statistics',
);
foreach ($files as $file) {
if ($file != 'index.php' && !in_array(strtolower(str_replace('Controller.php', '', $file)), $exludePages)) {
$className = str_replace('.php', '', $file);
$reflection = class_exists($className) ? new ReflectionClass(str_replace('.php', '', $file)) : false;
$properties = $reflection ? $reflection->getDefaultProperties() : array();
if (isset($properties['php_self'])) {
$selectedPages[$properties['php_self']] = $properties['php_self'];
} elseif (preg_match('/^[a-z0-9_.-]*\.php$/i', $file)) {
$selectedPages[strtolower(str_replace('Controller.php', '', $file))] = strtolower(str_replace('Controller.php', '', $file));
} elseif (preg_match('/^([a-z0-9_.-]*\/)?[a-z0-9_.-]*\.php$/i', $file)) {
$selectedPages[strtolower(Context::getContext()->getTranslator()->trans('File %2$s (in directory %1$s)', array(dirname($file), str_replace('Controller.php', '', basename($file))), 'Admin.Notifications.Error'))] = strtolower(str_replace('Controller.php', '', basename($file)));
}
}
}
// Add modules controllers to list (this function is cool !)
foreach (glob(_PS_MODULE_DIR_ . '*/controllers/front/*.php') as $file) {
$filename = Tools::strtolower(basename($file, '.php'));
if ($filename == 'index') {
continue;
}
$module = Tools::strtolower(basename(dirname(dirname(dirname($file)))));
$selectedPages[$module . ' - ' . $filename] = 'module-' . $module . '-' . $filename;
}
// Exclude page already filled
if ($excludeFilled) {
$metas = Meta::getMetas();
foreach ($metas as $meta) {
if (in_array($meta['page'], $selectedPages)) {
unset($selectedPages[array_search($meta['page'], $selectedPages)]);
}
}
}
// Add selected page
if ($addPage) {
$name = $addPage;
if (preg_match('#module-([a-z0-9_-]+)-([a-z0-9]+)$#i', $addPage, $m)) {
$addPage = $m[1] . ' - ' . $m[2];
}
$selectedPages[$addPage] = $name;
asort($selectedPages);
}
return $selectedPages;
}
/**
* Get all Metas.
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public static function getMetas()
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT * FROM ' . _DB_PREFIX_ . 'meta ORDER BY page ASC');
}
/**
* Get all metas, but filter by Language.
*
* @param int $idLang Language ID
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public static function getMetasByIdLang($idLang)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT *
FROM `' . _DB_PREFIX_ . 'meta` m
LEFT JOIN `' . _DB_PREFIX_ . 'meta_lang` ml ON m.`id_meta` = ml.`id_meta`
WHERE ml.`id_lang` = ' . (int) $idLang
. Shop::addSqlRestrictionOnLang('ml') .
'ORDER BY page ASC');
}
/**
* Get metas by page.
*
* @param string $page
* @param int $idLang Language ID
*
* @return array|bool|object|null
*/
public static function getMetaByPage($page, $idLang)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
SELECT *
FROM ' . _DB_PREFIX_ . 'meta m
LEFT JOIN ' . _DB_PREFIX_ . 'meta_lang ml ON m.id_meta = ml.id_meta
WHERE (
m.page = "' . pSQL($page) . '"
OR m.page = "' . pSQL(str_replace('-', '', strtolower($page))) . '"
)
AND ml.id_lang = ' . (int) $idLang . '
' . Shop::addSqlRestrictionOnLang('ml'));
}
/**
* Get all metas.
*
* @param int $idLang
*
* @return array|false|mysqli_result|PDOStatement|resource|null
*/
public static function getAllMeta($idLang)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT *
FROM ' . _DB_PREFIX_ . 'meta m
LEFT JOIN ' . _DB_PREFIX_ . 'meta_lang ml ON m.id_meta = ml.id_meta
AND ml.id_lang = ' . (int) $idLang . '
' . Shop::addSqlRestrictionOnLang('ml'));
}
/**
* Updates the current Meta in the database.
*
* @param bool $nullValues Whether we want to use NULL values instead of empty quotes values
*
* @return bool Indicates whether the Meta has been successfully updated
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function update($nullValues = false)
{
if (!parent::update($nullValues)) {
return false;
}
return Tools::generateHtaccess();
}
/**
* Deletes current Meta from the database.
*
* @return bool `true` if delete was successful
*
* @throws PrestaShopException
*/
public function delete()
{
if (!parent::delete()) {
return false;
}
return Tools::generateHtaccess();
}
/**
* Delete selection.
*
* @param array $selection
*
* @return bool
*/
public function deleteSelection($selection)
{
if (!is_array($selection)) {
die(Tools::displayError());
}
$result = true;
foreach ($selection as $id) {
$this->id = (int) $id;
$result = $result && $this->delete();
}
return $result && Tools::generateHtaccess();
}
/**
* Get equivalent URL rewrite.
*
* @param int $newIdLang
* @param int $idLang
* @param string $urlRewrite
*
* @return false|string|null
*/
public static function getEquivalentUrlRewrite($newIdLang, $idLang, $urlRewrite)
{
return Db::getInstance()->getValue('
SELECT url_rewrite
FROM `' . _DB_PREFIX_ . 'meta_lang`
WHERE id_meta = (
SELECT id_meta
FROM `' . _DB_PREFIX_ . 'meta_lang`
WHERE url_rewrite = \'' . pSQL($urlRewrite) . '\' AND id_lang = ' . (int) $idLang . '
AND id_shop = ' . Context::getContext()->shop->id . '
)
AND id_lang = ' . (int) $newIdLang . '
AND id_shop = ' . Context::getContext()->shop->id);
}
/**
* Get meta tags.
*
* @since 1.5.0
*/
public static function getMetaTags($idLang, $pageName, $title = '')
{
if (Configuration::get('PS_SHOP_ENABLE')
|| in_array(Tools::getRemoteAddr(), explode(',', Configuration::get('PS_MAINTENANCE_IP')))) {
if ($pageName == 'product' && ($idProduct = Tools::getValue('id_product'))) {
return Meta::getProductMetas($idProduct, $idLang, $pageName);
} elseif ($pageName == 'category' && ($idCategory = Tools::getValue('id_category'))) {
return Meta::getCategoryMetas($idCategory, $idLang, $pageName, $title);
} elseif ($pageName == 'manufacturer' && ($idManufacturer = Tools::getValue('id_manufacturer'))) {
return Meta::getManufacturerMetas($idManufacturer, $idLang, $pageName);
} elseif ($pageName == 'supplier' && ($idSupplier = Tools::getValue('id_supplier'))) {
return Meta::getSupplierMetas($idSupplier, $idLang, $pageName);
} elseif ($pageName == 'cms' && ($idCms = Tools::getValue('id_cms'))) {
return Meta::getCmsMetas($idCms, $idLang, $pageName);
} elseif ($pageName == 'cms' && ($idCmsCategory = Tools::getValue('id_cms_category'))) {
return Meta::getCmsCategoryMetas($idCmsCategory, $idLang, $pageName);
}
}
return Meta::getHomeMetas($idLang, $pageName);
}
/**
* Get meta tags for a given page.
*
* @param int $idLang Language ID
* @param string $pageName Page name
*
* @return array Meta tags
*
* @since 1.5.0
*/
public static function getHomeMetas($idLang, $pageName)
{
$metas = Meta::getMetaByPage($pageName, $idLang);
$ret['meta_title'] = (isset($metas['title']) && $metas['title']) ? $metas['title'] : Configuration::get('PS_SHOP_NAME');
$ret['meta_description'] = (isset($metas['description']) && $metas['description']) ? $metas['description'] : '';
$ret['meta_keywords'] = (isset($metas['keywords']) && $metas['keywords']) ? $metas['keywords'] : '';
return $ret;
}
/**
* Get product meta tags.
*
* @param int $idProduct
* @param int $idLang
* @param string $pageName
*
* @return array
*
* @since 1.5.0
*/
public static function getProductMetas($idProduct, $idLang, $pageName)
{
$product = new Product($idProduct, false, $idLang);
if (Validate::isLoadedObject($product) && $product->active) {
$row = Meta::getPresentedObject($product);
if (empty($row['meta_description'])) {
$row['meta_description'] = strip_tags($row['description_short']);
}
return Meta::completeMetaTags($row, $row['name']);
}
return Meta::getHomeMetas($idLang, $pageName);
}
/**
* Get category meta tags.
*
* @param int $idCategory
* @param int $idLang
* @param string $pageName
*
* @return array
*
* @since 1.5.0
*/
public static function getCategoryMetas($idCategory, $idLang, $pageName, $title = '')
{
if (!empty($title)) {
$title = ' - ' . $title;
}
$pageNumber = (int) Tools::getValue('page');
$category = new Category($idCategory, $idLang);
$cacheId = 'Meta::getCategoryMetas' . (int) $idCategory . '-' . (int) $idLang;
if (!Cache::isStored($cacheId)) {
if (Validate::isLoadedObject($category)) {
$row = Meta::getPresentedObject($category);
if (empty($row['meta_description'])) {
$row['meta_description'] = strip_tags($row['description']);
}
// Paginate title
if (!empty($row['meta_title'])) {
$row['meta_title'] = $title . $row['meta_title'] . (!empty($pageNumber) ? ' (' . $pageNumber . ')' : '');
} else {
$row['meta_title'] = $row['name'] . (!empty($pageNumber) ? ' (' . $pageNumber . ')' : '');
}
if (!empty($title)) {
$row['meta_title'] = $title . (!empty($pageNumber) ? ' (' . $pageNumber . ')' : '');
}
$result = Meta::completeMetaTags($row, $row['name']);
} else {
$result = Meta::getHomeMetas($idLang, $pageName);
}
Cache::store($cacheId, $result);
return $result;
}
return Cache::retrieve($cacheId);
}
/**
* Get manufacturer meta tags.
*
*
* @param int $idManufacturer
* @param int $idLang
* @param string $pageName
*
* @return array
*
* @since 1.5.0
*/
public static function getManufacturerMetas($idManufacturer, $idLang, $pageName)
{
$pageNumber = (int) Tools::getValue('page');
$manufacturer = new Manufacturer($idManufacturer, $idLang);
if (Validate::isLoadedObject($manufacturer)) {
$row = Meta::getPresentedObject($manufacturer);
if (!empty($row['meta_description'])) {
$row['meta_description'] = strip_tags($row['meta_description']);
}
$row['meta_title'] = ($row['meta_title'] ? $row['meta_title'] : $row['name']) . (!empty($pageNumber) ? ' (' . $pageNumber . ')' : '');
$row['meta_title'];
return Meta::completeMetaTags($row, $row['meta_title']);
}
return Meta::getHomeMetas($idLang, $pageName);
}
/**
* Get supplier meta tags.
*
*
* @param int $idSupplier
* @param int $idLang
* @param string $pageName
*
* @return array
*
* @since 1.5.0
*/
public static function getSupplierMetas($idSupplier, $idLang, $pageName)
{
$supplier = new Supplier($idSupplier, $idLang);
if (Validate::isLoadedObject($supplier)) {
$row = Meta::getPresentedObject($supplier);
if (!empty($row['meta_description'])) {
$row['meta_description'] = strip_tags($row['meta_description']);
}
return Meta::completeMetaTags($row, $row['name']);
}
return Meta::getHomeMetas($idLang, $pageName);
}
/**
* Get CMS meta tags.
*
* @param int $idCms
* @param int $idLang
* @param string $pageName
*
* @return array
*
* @since 1.5.0
*/
public static function getCmsMetas($idCms, $idLang, $pageName)
{
$cms = new CMS($idCms, $idLang);
if (Validate::isLoadedObject($cms)) {
$row = Meta::getPresentedObject($cms);
$row['meta_title'] = !empty($row['head_seo_title']) ? $row['head_seo_title'] : $row['meta_title'];
return Meta::completeMetaTags($row, $row['meta_title']);
}
return Meta::getHomeMetas($idLang, $pageName);
}
/**
* Get CMS category meta tags.
*
* @param int $idCmsCategory
* @param int $idLang
* @param string $pageName
*
* @return array
*
* @since 1.5.0
*/
public static function getCmsCategoryMetas($idCmsCategory, $idLang, $pageName)
{
$cmsCategory = new CMSCategory($idCmsCategory, $idLang);
if (Validate::isLoadedObject($cmsCategory)) {
$row = Meta::getPresentedObject($cmsCategory);
$row['meta_title'] = empty($row['meta_title']) ? $row['name'] : $row['meta_title'];
return Meta::completeMetaTags($row, $row['meta_title']);
}
return Meta::getHomeMetas($idLang, $pageName);
}
/**
* @since 1.5.0
*/
public static function completeMetaTags($metaTags, $defaultValue, Context $context = null)
{
if (!$context) {
$context = Context::getContext();
}
if (empty($metaTags['meta_title'])) {
$metaTags['meta_title'] = $defaultValue;
}
return $metaTags;
}
/**
* Get presented version of an object.
*
* @param ObjectModel $object
*
* @return array
*/
protected static function getPresentedObject($object)
{
$objectPresenter = new ObjectPresenter();
return $objectPresenter->present($object);
}
}

177
classes/Notification.php Normal file
View File

@@ -0,0 +1,177 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class NotificationCore.
*/
class NotificationCore
{
public $types;
/**
* NotificationCore constructor.
*/
public function __construct()
{
$this->types = array('order', 'customer_message', 'customer');
}
/**
* getLastElements return all the notifications (new order, new customer registration, and new customer message)
* Get all the notifications.
*
* @return array containing the notifications
*/
public function getLastElements()
{
$notifications = array();
$employeeInfos = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
SELECT id_last_order, id_last_customer_message, id_last_customer
FROM `' . _DB_PREFIX_ . 'employee`
WHERE `id_employee` = ' . (int) Context::getContext()->employee->id);
foreach ($this->types as $type) {
$notifications[$type] = Notification::getLastElementsIdsByType($type, $employeeInfos['id_last_' . $type]);
}
return $notifications;
}
/**
* getLastElementsIdsByType return all the element ids to show (order, customer registration, and customer message)
* Get all the element ids.
*
* @param string $type contains the field name of the Employee table
* @param int $idLastElement contains the id of the last seen element
*
* @return array containing the notifications
*/
public static function getLastElementsIdsByType($type, $idLastElement)
{
global $cookie;
switch ($type) {
case 'order':
$sql = '
SELECT SQL_CALC_FOUND_ROWS o.`id_order`, o.`id_customer`, o.`total_paid`, o.`id_currency`, o.`date_upd`, c.`firstname`, c.`lastname`, ca.`name`, co.`iso_code`
FROM `' . _DB_PREFIX_ . 'orders` as o
LEFT JOIN `' . _DB_PREFIX_ . 'customer` as c ON (c.`id_customer` = o.`id_customer`)
LEFT JOIN `' . _DB_PREFIX_ . 'carrier` as ca ON (ca.`id_carrier` = o.`id_carrier`)
LEFT JOIN `' . _DB_PREFIX_ . 'address` as a ON (a.`id_address` = o.`id_address_delivery`)
LEFT JOIN `' . _DB_PREFIX_ . 'country` as co ON (co.`id_country` = a.`id_country`)
WHERE `id_order` > ' . (int) $idLastElement .
Shop::addSqlRestriction(false, 'o') . '
ORDER BY `id_order` DESC
LIMIT 5';
break;
case 'customer_message':
$sql = '
SELECT SQL_CALC_FOUND_ROWS c.`id_customer_message`, ct.`id_customer`, ct.`id_customer_thread`, ct.`email`, ct.`status`, c.`date_add`, cu.`firstname`, cu.`lastname`
FROM `' . _DB_PREFIX_ . 'customer_message` as c
LEFT JOIN `' . _DB_PREFIX_ . 'customer_thread` as ct ON (c.`id_customer_thread` = ct.`id_customer_thread`)
LEFT JOIN `' . _DB_PREFIX_ . 'customer` as cu ON (cu.`id_customer` = ct.`id_customer`)
WHERE c.`id_customer_message` > ' . (int) $idLastElement . '
AND c.`id_employee` = 0
AND ct.id_shop IN (' . implode(', ', Shop::getContextListShopID()) . ')
ORDER BY c.`id_customer_message` DESC
LIMIT 5';
break;
default:
$sql = '
SELECT SQL_CALC_FOUND_ROWS t.`id_' . bqSQL($type) . '`, t.*
FROM `' . _DB_PREFIX_ . bqSQL($type) . '` t
WHERE t.`deleted` = 0 AND t.`id_' . bqSQL($type) . '` > ' . (int) $idLastElement .
Shop::addSqlRestriction(false, 't') . '
ORDER BY t.`id_' . bqSQL($type) . '` DESC
LIMIT 5';
break;
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql, true, false);
$total = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('SELECT FOUND_ROWS()', false);
$json = array('total' => $total, 'results' => array());
foreach ($result as $value) {
$customerName = '';
if (isset($value['firstname'], $value['lastname'])) {
$customerName = Tools::safeOutput($value['firstname'] . ' ' . $value['lastname']);
} elseif (isset($value['email'])) {
$customerName = Tools::safeOutput($value['email']);
}
$json['results'][] = array(
'id_order' => ((!empty($value['id_order'])) ? (int) $value['id_order'] : 0),
'id_customer' => ((!empty($value['id_customer'])) ? (int) $value['id_customer'] : 0),
'id_customer_message' => ((!empty($value['id_customer_message'])) ? (int) $value['id_customer_message'] : 0),
'id_customer_thread' => ((!empty($value['id_customer_thread'])) ? (int) $value['id_customer_thread'] : 0),
'total_paid' => ((!empty($value['total_paid'])) ? Tools::displayPrice((float) $value['total_paid'], (int) $value['id_currency'], false) : 0),
'carrier' => ((!empty($value['name'])) ? Tools::safeOutput($value['name']) : ''),
'iso_code' => ((!empty($value['iso_code'])) ? Tools::safeOutput($value['iso_code']) : ''),
'company' => ((!empty($value['company'])) ? Tools::safeOutput($value['company']) : ''),
'status' => ((!empty($value['status'])) ? Tools::safeOutput($value['status']) : ''),
'customer_name' => $customerName,
'date_add' => isset($value['date_add']) ? Tools::displayDate($value['date_add']) : 0,
'customer_view_url' => Context::getContext()->link->getAdminLink(
'AdminCustomers',
true,
[
'customerId' => $value['id_customer'],
'viewcustomer' => true,
]
),
);
}
return $json;
}
/**
* updateEmployeeLastElement return 0 if the field doesn't exists in Employee table.
* Updates the last seen element by the employee.
*
* @param string $type contains the field name of the Employee table
*
* @return bool if type exists or not
*/
public function updateEmployeeLastElement($type)
{
if (in_array($type, $this->types)) {
// We update the last item viewed
return Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'employee`
SET `id_last_' . bqSQL($type) . '` = (
SELECT IFNULL(MAX(`id_' . bqSQL($type) . '`), 0)
FROM `' . _DB_PREFIX_ . (($type == 'order') ? bqSQL($type) . 's' : bqSQL($type)) . '`
)
WHERE `id_employee` = ' . (int) Context::getContext()->employee->id);
}
return false;
}
}

2180
classes/ObjectModel.php Normal file

File diff suppressed because it is too large Load Diff

593
classes/Pack.php Normal file
View File

@@ -0,0 +1,593 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class PackCore extends Product
{
/**
* Only decrement pack quantity.
*
* @var string
*/
const STOCK_TYPE_PACK_ONLY = 0;
/**
* Only decrement pack products quantities.
*
* @var string
*/
const STOCK_TYPE_PRODUCTS_ONLY = 1;
/**
* Decrement pack quantity and pack products quantities.
*
* @var string
*/
const STOCK_TYPE_PACK_BOTH = 2;
/**
* Use pack quantity default setting.
*
* @var string
*/
const STOCK_TYPE_DEFAULT = 3;
protected static $cachePackItems = array();
protected static $cacheIsPack = array();
protected static $cacheIsPacked = array();
public static function resetStaticCache()
{
self::$cachePackItems = array();
self::$cacheIsPack = array();
self::$cacheIsPacked = array();
}
/**
* Is product a pack?
*
* @param $id_product
*
* @return bool
*/
public static function isPack($id_product)
{
if (!Pack::isFeatureActive()) {
return false;
}
if (!$id_product) {
return false;
}
if (!array_key_exists($id_product, self::$cacheIsPack)) {
$result = Db::getInstance()->getValue('SELECT COUNT(*) FROM `' . _DB_PREFIX_ . 'pack` WHERE id_product_pack = ' . (int) $id_product);
self::$cacheIsPack[$id_product] = ($result > 0);
}
return self::$cacheIsPack[$id_product];
}
/**
* Is product in a pack?
* If $id_product_attribute specified, then will restrict search on the given combination,
* else this method will match a product if at least one of all its combination is in a pack.
*
* @param $id_product
* @param $id_product_attribute Optional combination of the product
*
* @return bool
*/
public static function isPacked($id_product, $id_product_attribute = false)
{
if (!Pack::isFeatureActive()) {
return false;
}
if ($id_product_attribute === false) {
$cache_key = $id_product . '-0';
if (!array_key_exists($cache_key, self::$cacheIsPacked)) {
$result = Db::getInstance()->getValue('SELECT COUNT(*) FROM `' . _DB_PREFIX_ . 'pack` WHERE id_product_item = ' . (int) $id_product);
self::$cacheIsPacked[$cache_key] = ($result > 0);
}
return self::$cacheIsPacked[$cache_key];
} else {
$cache_key = $id_product . '-' . $id_product_attribute;
if (!array_key_exists($cache_key, self::$cacheIsPacked)) {
$result = Db::getInstance()->getValue('SELECT COUNT(*) FROM `' . _DB_PREFIX_ . 'pack` WHERE id_product_item = ' . ((int) $id_product) . ' AND
id_product_attribute_item = ' . ((int) $id_product_attribute));
self::$cacheIsPacked[$cache_key] = ($result > 0);
}
return self::$cacheIsPacked[$cache_key];
}
}
public static function noPackPrice($id_product)
{
$sum = 0;
$price_display_method = !self::$_taxCalculationMethod;
$items = Pack::getItems($id_product, Configuration::get('PS_LANG_DEFAULT'));
foreach ($items as $item) {
/* @var Product $item */
$sum += $item->getPrice($price_display_method, ($item->id_pack_product_attribute ? $item->id_pack_product_attribute : null)) * $item->pack_quantity;
}
return $sum;
}
public static function noPackWholesalePrice($id_product)
{
$sum = 0;
$items = Pack::getItems($id_product, Configuration::get('PS_LANG_DEFAULT'));
foreach ($items as $item) {
$sum += $item->wholesale_price * $item->pack_quantity;
}
return $sum;
}
public static function getItems($id_product, $id_lang)
{
if (!Pack::isFeatureActive()) {
return array();
}
if (array_key_exists($id_product, self::$cachePackItems)) {
return self::$cachePackItems[$id_product];
}
$result = Db::getInstance()->executeS('SELECT id_product_item, id_product_attribute_item, quantity FROM `' . _DB_PREFIX_ . 'pack` where id_product_pack = ' . (int) $id_product);
$array_result = array();
foreach ($result as $row) {
$p = new Product($row['id_product_item'], false, $id_lang);
$p->loadStockData();
$p->pack_quantity = $row['quantity'];
$p->id_pack_product_attribute = (isset($row['id_product_attribute_item']) && $row['id_product_attribute_item'] ? $row['id_product_attribute_item'] : 0);
if (isset($row['id_product_attribute_item']) && $row['id_product_attribute_item']) {
$sql = 'SELECT agl.`name` AS group_name, al.`name` AS attribute_name
FROM `' . _DB_PREFIX_ . 'product_attribute` pa
' . Shop::addSqlAssociation('product_attribute', 'pa') . '
LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_combination` pac ON pac.`id_product_attribute` = pa.`id_product_attribute`
LEFT JOIN `' . _DB_PREFIX_ . 'attribute` a ON a.`id_attribute` = pac.`id_attribute`
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_group` ag ON ag.`id_attribute_group` = a.`id_attribute_group`
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = ' . (int) Context::getContext()->language->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_group_lang` agl ON (ag.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = ' . (int) Context::getContext()->language->id . ')
WHERE pa.`id_product_attribute` = ' . $row['id_product_attribute_item'] . '
GROUP BY pa.`id_product_attribute`, ag.`id_attribute_group`
ORDER BY pa.`id_product_attribute`';
$combinations = Db::getInstance()->executeS($sql);
foreach ($combinations as $k => $combination) {
$p->name .= ' ' . $combination['group_name'] . '-' . $combination['attribute_name'];
}
}
$array_result[] = $p;
}
self::$cachePackItems[$id_product] = $array_result;
return self::$cachePackItems[$id_product];
}
/**
* Indicates if a pack and its associated products are available for orders in the desired quantity.
*
* @todo This method returns true even if the pack feature is not active.
* Should throw an exception instead.
* Developers should first test if product is a pack
* and then if it's in stock.
*
* @param int $idProduct
* @param int $wantedQuantity
* @param Cart|null $cart
*
* @return bool
*
* @throws PrestaShopException
*/
public static function isInStock($idProduct, $wantedQuantity = 1, Cart $cart = null)
{
if (!Pack::isFeatureActive()) {
return true;
}
$idProduct = (int) $idProduct;
$wantedQuantity = (int) $wantedQuantity;
$product = new Product($idProduct, false);
$packQuantity = self::getQuantity($idProduct, null, null, $cart);
if ($product->isAvailableWhenOutOfStock($product->out_of_stock)) {
return true;
} elseif ($wantedQuantity > $packQuantity) {
return false;
}
return true;
}
/**
* Returns the available quantity of a given pack.
*
* @param int $id_product Product id
* @param int $id_product_attribute Product attribute id (optional)
* @param bool|null $cacheIsPack
* @param Cart $cart
* @param int $idCustomization Product customization id (optional)
*
* @return int
*
* @throws PrestaShopException
*/
public static function getQuantity(
$idProduct,
$idProductAttribute = null,
$cacheIsPack = null,
Cart $cart = null,
$idCustomization = null
) {
$idProduct = (int) $idProduct;
$idProductAttribute = (int) $idProductAttribute;
$cacheIsPack = (bool) $cacheIsPack;
if (!self::isPack($idProduct)) {
throw new PrestaShopException("Product with id $idProduct is not a pack");
}
// Initialize
$product = new Product($idProduct, false);
$packQuantity = 0;
$packQuantityInStock = StockAvailable::getQuantityAvailableByProduct(
$idProduct,
$idProductAttribute
);
$packStockType = $product->pack_stock_type;
$allPackStockType = array(
self::STOCK_TYPE_PACK_ONLY,
self::STOCK_TYPE_PRODUCTS_ONLY,
self::STOCK_TYPE_PACK_BOTH,
self::STOCK_TYPE_DEFAULT,
);
if (!in_array($packStockType, $allPackStockType)) {
throw new PrestaShopException('Unknown pack stock type');
}
// If no pack stock or shop default, set it
if (empty($packStockType)
|| $packStockType == self::STOCK_TYPE_DEFAULT
) {
$packStockType = Configuration::get('PS_PACK_STOCK_TYPE');
}
// Initialize with pack quantity if not only products
if (in_array($packStockType, array(self::STOCK_TYPE_PACK_ONLY, self::STOCK_TYPE_PACK_BOTH))) {
$packQuantity = $packQuantityInStock;
}
// Set pack quantity to the minimum quantity of pack, or
// product pack
if (in_array($packStockType, array(self::STOCK_TYPE_PACK_BOTH, self::STOCK_TYPE_PRODUCTS_ONLY))) {
$items = array_values(Pack::getItems($idProduct, Configuration::get('PS_LANG_DEFAULT')));
foreach ($items as $index => $item) {
$itemQuantity = Product::getQuantity($item->id, null, null, $cart, $idCustomization);
$nbPackAvailableForItem = floor($itemQuantity / $item->pack_quantity);
// Initialize packQuantity with the first product quantity
// if pack decrement stock type is products only
if ($index === 0
&& $packStockType == self::STOCK_TYPE_PRODUCTS_ONLY
) {
$packQuantity = $nbPackAvailableForItem;
continue;
}
if ($nbPackAvailableForItem < $packQuantity) {
$packQuantity = $nbPackAvailableForItem;
}
}
} elseif (!empty($cart)) {
$cartProduct = $cart->getProductQuantity($idProduct, $idProductAttribute, $idCustomization);
if (!empty($cartProduct['deep_quantity'])) {
$packQuantity -= $cartProduct['deep_quantity'];
}
}
return $packQuantity;
}
public static function getItemTable($id_product, $id_lang, $full = false)
{
if (!Pack::isFeatureActive()) {
return array();
}
$context = Context::getContext();
$sql = 'SELECT p.*, product_shop.*, pl.*, image_shop.`id_image` id_image, il.`legend`, cl.`name` AS category_default, a.quantity AS pack_quantity, product_shop.`id_category_default`, a.id_product_pack, a.id_product_attribute_item
FROM `' . _DB_PREFIX_ . 'pack` a
LEFT JOIN `' . _DB_PREFIX_ . 'product` p ON p.id_product = a.id_product_item
LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl
ON p.id_product = pl.id_product
AND pl.`id_lang` = ' . (int) $id_lang . Shop::addSqlRestrictionOnLang('pl') . '
LEFT JOIN `' . _DB_PREFIX_ . 'image_shop` image_shop
ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop=' . (int) $context->shop->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $id_lang . ')
' . Shop::addSqlAssociation('product', 'p') . '
LEFT JOIN `' . _DB_PREFIX_ . 'category_lang` cl
ON product_shop.`id_category_default` = cl.`id_category`
AND cl.`id_lang` = ' . (int) $id_lang . Shop::addSqlRestrictionOnLang('cl') . '
WHERE product_shop.`id_shop` = ' . (int) $context->shop->id . '
AND a.`id_product_pack` = ' . (int) $id_product . '
GROUP BY a.`id_product_item`, a.`id_product_attribute_item`';
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
foreach ($result as &$line) {
if (Combination::isFeatureActive() && isset($line['id_product_attribute_item']) && $line['id_product_attribute_item']) {
$line['cache_default_attribute'] = $line['id_product_attribute'] = $line['id_product_attribute_item'];
$sql = 'SELECT agl.`name` AS group_name, al.`name` AS attribute_name, pai.`id_image` AS id_product_attribute_image
FROM `' . _DB_PREFIX_ . 'product_attribute` pa
' . Shop::addSqlAssociation('product_attribute', 'pa') . '
LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_combination` pac ON pac.`id_product_attribute` = ' . $line['id_product_attribute_item'] . '
LEFT JOIN `' . _DB_PREFIX_ . 'attribute` a ON a.`id_attribute` = pac.`id_attribute`
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_group` ag ON ag.`id_attribute_group` = a.`id_attribute_group`
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = ' . (int) Context::getContext()->language->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'attribute_group_lang` agl ON (ag.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = ' . (int) Context::getContext()->language->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_image` pai ON (' . $line['id_product_attribute_item'] . ' = pai.`id_product_attribute`)
WHERE pa.`id_product` = ' . (int) $line['id_product'] . ' AND pa.`id_product_attribute` = ' . $line['id_product_attribute_item'] . '
GROUP BY pa.`id_product_attribute`, ag.`id_attribute_group`
ORDER BY pa.`id_product_attribute`';
$attr_name = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
if (isset($attr_name[0]['id_product_attribute_image']) && $attr_name[0]['id_product_attribute_image']) {
$line['id_image'] = $attr_name[0]['id_product_attribute_image'];
}
$line['name'] .= "\n";
foreach ($attr_name as $value) {
$line['name'] .= ' ' . $value['group_name'] . '-' . $value['attribute_name'];
}
}
$line = Product::getTaxesInformations($line);
}
if (!$full) {
return $result;
}
$array_result = array();
foreach ($result as $prow) {
if (!Pack::isPack($prow['id_product'])) {
$prow['id_product_attribute'] = (int) $prow['id_product_attribute_item'];
$array_result[] = Product::getProductProperties($id_lang, $prow);
}
}
return $array_result;
}
public static function getPacksTable($id_product, $id_lang, $full = false, $limit = null)
{
if (!Pack::isFeatureActive()) {
return array();
}
$packs = Db::getInstance()->getValue('
SELECT GROUP_CONCAT(a.`id_product_pack`)
FROM `' . _DB_PREFIX_ . 'pack` a
WHERE a.`id_product_item` = ' . (int) $id_product);
if (!(int) $packs) {
return array();
}
$context = Context::getContext();
$sql = '
SELECT p.*, product_shop.*, pl.*, image_shop.`id_image` id_image, il.`legend`, IFNULL(product_attribute_shop.id_product_attribute, 0) id_product_attribute
FROM `' . _DB_PREFIX_ . 'product` p
NATURAL LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl
' . Shop::addSqlAssociation('product', 'p') . '
LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_shop` product_attribute_shop
ON (p.`id_product` = product_attribute_shop.`id_product` AND product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop=' . (int) $context->shop->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'image_shop` image_shop
ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop=' . (int) $context->shop->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $id_lang . ')
WHERE pl.`id_lang` = ' . (int) $id_lang . '
' . Shop::addSqlRestrictionOnLang('pl') . '
AND p.`id_product` IN (' . $packs . ')
GROUP BY p.id_product';
if ($limit) {
$sql .= ' LIMIT ' . (int) $limit;
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
if (!$full) {
return $result;
}
$array_result = array();
foreach ($result as $row) {
if (!Pack::isPacked($row['id_product'])) {
$array_result[] = Product::getProductProperties($id_lang, $row);
}
}
return $array_result;
}
public static function deleteItems($id_product)
{
return Db::getInstance()->update('product', array('cache_is_pack' => 0), 'id_product = ' . (int) $id_product) &&
Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'pack` WHERE `id_product_pack` = ' . (int) $id_product) &&
Configuration::updateGlobalValue('PS_PACK_FEATURE_ACTIVE', Pack::isCurrentlyUsed());
}
/**
* Add an item to the pack.
*
* @param int $id_product
* @param int $id_item
* @param int $qty
* @param int $id_attribute_item
*
* @return bool true if everything was fine
*
* @throws PrestaShopDatabaseException
*/
public static function addItem($id_product, $id_item, $qty, $id_attribute_item = 0)
{
$id_attribute_item = (int) $id_attribute_item ? (int) $id_attribute_item : Product::getDefaultAttribute((int) $id_item);
return Db::getInstance()->update('product', array('cache_is_pack' => 1), 'id_product = ' . (int) $id_product) &&
Db::getInstance()->insert('pack', array(
'id_product_pack' => (int) $id_product,
'id_product_item' => (int) $id_item,
'id_product_attribute_item' => (int) $id_attribute_item,
'quantity' => (int) $qty,
))
&& Configuration::updateGlobalValue('PS_PACK_FEATURE_ACTIVE', '1');
}
public static function duplicate($id_product_old, $id_product_new)
{
Db::getInstance()->execute('INSERT INTO `' . _DB_PREFIX_ . 'pack` (`id_product_pack`, `id_product_item`, `id_product_attribute_item`, `quantity`)
(SELECT ' . (int) $id_product_new . ', `id_product_item`, `id_product_attribute_item`, `quantity` FROM `' . _DB_PREFIX_ . 'pack` WHERE `id_product_pack` = ' . (int) $id_product_old . ')');
// If return query result, a non-pack product will return false
return true;
}
/**
* This method is allow to know if a feature is used or active.
*
* @since 1.5.0.1
*
* @return bool
*/
public static function isFeatureActive()
{
return Configuration::get('PS_PACK_FEATURE_ACTIVE');
}
/**
* This method is allow to know if a Pack entity is currently used.
*
* @since 1.5.0
*
* @param $table
* @param $has_active_column
*
* @return bool
*/
public static function isCurrentlyUsed($table = null, $has_active_column = false)
{
// We dont't use the parent method because the identifier isn't id_pack
return (bool) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
SELECT `id_product_pack`
FROM `' . _DB_PREFIX_ . 'pack`
');
}
/**
* For a given pack, tells if it has at least one product using the advanced stock management.
*
* @param int $id_product id_pack
*
* @return bool
*/
public static function usesAdvancedStockManagement($id_product)
{
if (!Pack::isPack($id_product)) {
return false;
}
$products = Pack::getItems($id_product, Configuration::get('PS_LANG_DEFAULT'));
foreach ($products as $product) {
// if one product uses the advanced stock management
if ($product->advanced_stock_management == 1) {
return true;
}
}
// not used
return false;
}
/**
* For a given pack, tells if all products using the advanced stock management.
*
* @param int $id_product id_pack
*
* @return bool
*/
public static function allUsesAdvancedStockManagement($id_product)
{
if (!Pack::isPack($id_product)) {
return false;
}
$products = Pack::getItems($id_product, Configuration::get('PS_LANG_DEFAULT'));
foreach ($products as $product) {
// if one product uses the advanced stock management
if ($product->advanced_stock_management == 0) {
return false;
}
}
// not used
return true;
}
/**
* Returns Packs that conatins the given product in the right declinaison.
*
* @param int $id_item Product item id that could be contained in a|many pack(s)
* @param int $id_attribute_item The declinaison of the product
* @param int $id_lang
*
* @return array[Product] Packs that contains the given product
*/
public static function getPacksContainingItem($id_item, $id_attribute_item, $id_lang)
{
if (!Pack::isFeatureActive() || !$id_item) {
return array();
}
$query = 'SELECT `id_product_pack`, `quantity` FROM `' . _DB_PREFIX_ . 'pack`
WHERE `id_product_item` = ' . ((int) $id_item);
if (Combination::isFeatureActive()) {
$query .= ' AND `id_product_attribute_item` = ' . ((int) $id_attribute_item);
}
$result = Db::getInstance()->executeS($query);
$array_result = array();
foreach ($result as $row) {
$p = new Product($row['id_product_pack'], true, $id_lang);
$p->loadStockData();
$p->pack_item_quantity = $row['quantity']; // Specific need from StockAvailable::updateQuantity()
$array_result[] = $p;
}
return $array_result;
}
}

147
classes/Page.php Normal file
View File

@@ -0,0 +1,147 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class PageCore.
*/
class PageCore extends ObjectModel
{
public $id_page_type;
public $id_object;
public $name;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'page',
'primary' => 'id_page',
'fields' => array(
'id_page_type' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_object' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
),
);
/**
* @return int Current page ID
*/
public static function getCurrentId()
{
$controller = Dispatcher::getInstance()->getController();
$pageTypeId = Page::getPageTypeByName($controller);
/**
* Some pages must be distinguished in order to record exactly what is being seen
*
* @todo dispatcher module
*/
$specialArray = array(
'product' => 'id_product',
'category' => 'id_category',
'order' => 'step',
'manufacturer' => 'id_manufacturer',
);
$where = '';
$insertData = array(
'id_page_type' => $pageTypeId,
);
if (array_key_exists($controller, $specialArray)) {
$objectId = Tools::getValue($specialArray[$controller], null);
$where = ' AND `id_object` = ' . (int) $objectId;
$insertData['id_object'] = (int) $objectId;
}
$sql = 'SELECT `id_page`
FROM `' . _DB_PREFIX_ . 'page`
WHERE `id_page_type` = ' . (int) $pageTypeId . $where;
$result = Db::getInstance()->getRow($sql);
if ($result['id_page']) {
return $result['id_page'];
}
Db::getInstance()->insert('page', $insertData, true);
return Db::getInstance()->Insert_ID();
}
/**
* Return page type ID from page name.
*
* @param string $name Page name (E.g. product.php)
*/
public static function getPageTypeByName($name)
{
if ($value = Db::getInstance()->getValue(
'
SELECT id_page_type
FROM ' . _DB_PREFIX_ . 'page_type
WHERE name = \'' . pSQL($name) . '\''
)
) {
return $value;
}
Db::getInstance()->insert('page_type', array('name' => pSQL($name)));
return Db::getInstance()->Insert_ID();
}
/**
* Increase page viewed number by one.
*
* @param int $idPage Page ID
*/
public static function setPageViewed($idPage)
{
$idDateRange = DateRange::getCurrentRange();
$context = Context::getContext();
// Try to increment the visits counter
$sql = 'UPDATE `' . _DB_PREFIX_ . 'page_viewed`
SET `counter` = `counter` + 1
WHERE `id_date_range` = ' . (int) $idDateRange . '
AND `id_page` = ' . (int) $idPage . '
AND `id_shop` = ' . (int) $context->shop->id;
Db::getInstance()->execute($sql);
// If no one has seen the page in this date range, it is added
if (Db::getInstance()->Affected_Rows() == 0) {
Db::getInstance()->insert(
'page_viewed',
array(
'id_date_range' => (int) $idDateRange,
'id_page' => (int) $idPage,
'counter' => 1,
'id_shop' => (int) $context->shop->id,
'id_shop_group' => (int) $context->shop->id_shop_group,
)
);
}
}
}

36
classes/PaymentFree.php Normal file
View File

@@ -0,0 +1,36 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class PaymentFree
* Simple class to allow free order.
*/
class PaymentFree extends PaymentModule
{
public $active = 1;
public $name = 'free_order';
public $displayName = 'Free order';
}

1193
classes/PaymentModule.php Normal file

File diff suppressed because it is too large Load Diff

122
classes/PhpEncryption.php Normal file
View File

@@ -0,0 +1,122 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
/**
* Class PhpEncryptionCore for openSSL 1.0.1+.
*/
class PhpEncryptionCore
{
const ENGINE = 'PhpEncryptionEngine';
const LEGACY_ENGINE = 'PhpEncryptionLegacyEngine';
private static $engine;
/**
* PhpEncryptionCore constructor.
*
* @param string $hexString A string that only contains hexadecimal characters
* Bother upper and lower case are allowed
*/
public function __construct($hexString)
{
$engineClass = self::resolveEngineToUse();
self::$engine = new $engineClass($hexString);
}
/**
* Encrypt the plaintext.
*
* @param string $plaintext Plaintext
*
* @return string Cipher text
*/
public function encrypt($plaintext)
{
return self::$engine->encrypt($plaintext);
}
/**
* Decrypt the cipher text.
*
* @param string $cipherText Cipher text
*
* @return bool|string Plaintext
* `false` if unable to decrypt
*
* @throws Exception
*/
public function decrypt($cipherText)
{
return self::$engine->decrypt($cipherText);
}
/**
* @param $header
* @param $bytes
*
* @return string
*
* @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
*/
public static function saveBytesToChecksummedAsciiSafeString($header, $bytes)
{
$engine = self::resolveEngineToUse();
return $engine::saveBytesToChecksummedAsciiSafeString($header, $bytes);
}
/**
* @return string
*
* @throws Exception
*/
public static function createNewRandomKey()
{
$engine = self::resolveEngineToUse();
try {
$randomKey = $engine::createNewRandomKey();
} catch (EnvironmentIsBrokenException $exception) {
$buf = $engine::randomCompat();
$randomKey = $engine::saveToAsciiSafeString($buf);
}
return $randomKey;
}
/**
* Choose which engine use regarding the OpenSSL cipher methods available.
*/
public static function resolveEngineToUse()
{
if (false === in_array(\Defuse\Crypto\Core::CIPHER_METHOD, openssl_get_cipher_methods())) {
return self::LEGACY_ENGINE;
}
return self::ENGINE;
}
}

View File

@@ -0,0 +1,168 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use Defuse\Crypto\Crypto;
use Defuse\Crypto\Encoding;
use Defuse\Crypto\Key;
/**
* Class PhpEncryption engine for openSSL 1.0.1+.
*/
class PhpEncryptionEngineCore
{
protected $key;
/**
* PhpEncryptionCore constructor.
*
* @param string $hexString A string that only contains hexadecimal characters
* Bother upper and lower case are allowed
*/
public function __construct($hexString)
{
$this->key = self::loadFromAsciiSafeString($hexString);
}
/**
* Encrypt the plaintext.
*
* @param string $plaintext Plaintext
*
* @return string Cipher text
*/
public function encrypt($plaintext)
{
return Crypto::encrypt($plaintext, $this->key);
}
/**
* Decrypt the cipher text.
*
* @param string $cipherText Cipher text
*
* @return bool|string Plaintext
* `false` if unable to decrypt
*
* @throws Exception
*/
public function decrypt($cipherText)
{
try {
$plaintext = Crypto::decrypt($cipherText, $this->key);
} catch (Exception $e) {
if ($e instanceof \Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException) {
return false;
}
throw $e;
}
return $plaintext;
}
/**
* @param $header
* @param $bytes
*
* @return string
*
* @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
*/
public static function saveBytesToChecksummedAsciiSafeString($header, $bytes)
{
return Encoding::saveBytesToChecksummedAsciiSafeString($header, $bytes);
}
/**
* @return string
*/
public static function createNewRandomKey()
{
$key = Key::createNewRandomKey();
return $key->saveToAsciiSafeString();
}
/**
* @param $hexString
*
* @return Key
*/
public static function loadFromAsciiSafeString($hexString)
{
return Key::loadFromAsciiSafeString($hexString);
}
/**
* @return string
*
* @throws Exception
*
* @see https://github.com/paragonie/random_compat/blob/v1.4.1/lib/random_bytes_openssl.php
* @see https://github.com/paragonie/random_compat/blob/v1.4.1/lib/random_bytes_mcrypt.php
*/
public static function randomCompat()
{
$bytes = Key::KEY_BYTE_SIZE;
$secure = true;
$buf = openssl_random_pseudo_bytes($bytes, $secure);
if (
$buf !== false
&&
$secure
&&
RandomCompat_strlen($buf) === $bytes
) {
return $buf;
}
$buf = @mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);
if (
$buf !== false
&&
RandomCompat_strlen($buf) === $bytes
) {
return $buf;
}
throw new Exception(
'Could not gather sufficient random data'
);
}
/**
* @param $buf
*
* @return string
*/
public static function saveToAsciiSafeString($buf)
{
return Encoding::saveBytesToChecksummedAsciiSafeString(
Key::KEY_CURRENT_VERSION,
$buf
);
}
}

View File

@@ -0,0 +1,168 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class PhpEncryption engine for openSSL < 0.9.8.
*
* @doc http://php.net/manual/fr/function.mcrypt-encrypt.php#refsect1-function.mcrypt-encrypt-examples
*
* This class will be deprecated when web hosting providers will update their version of OpenSSL.
*/
class PhpEncryptionLegacyEngineCore extends PhpEncryptionEngine
{
protected $key;
protected $hmacIv;
protected $iv;
protected $ivSize;
protected $mode = MCRYPT_MODE_CBC;
protected $cipher = MCRYPT_RIJNDAEL_128;
/**
* PhpEncryptionCore constructor.
*
* @param string $hexString A string that only contains hexadecimal characters
* Bother upper and lower case are allowed
*/
public function __construct($hexString)
{
$this->key = substr($hexString, 0, 32);
$this->ivSize = mcrypt_get_iv_size($this->cipher, $this->mode);
$this->iv = mcrypt_create_iv($this->ivSize, MCRYPT_RAND);
$this->hmacIv = substr(sha1(_COOKIE_KEY_), 0, $this->ivSize);
}
/**
* Encrypt the plaintext.
*
* @param string $plaintext Plaintext
*
* @return string Cipher text
*/
public function encrypt($plaintext)
{
$blockSize = mcrypt_get_block_size($this->cipher, $this->mode);
$pad = $blockSize - (strlen($plaintext) % $blockSize);
$cipherText = mcrypt_encrypt(
$this->cipher,
$this->key,
$plaintext . str_repeat(chr($pad), $pad),
$this->mode,
$this->iv
);
$cipherText = $this->iv . $cipherText;
return $this->generateHmac($cipherText) . ':' . base64_encode($cipherText);
}
/**
* Decrypt the cipher text.
*
* @param string $cipherText Cipher text
*
* @return bool|string Plaintext
* `false` if unable to decrypt
*
* @throws Exception
*/
public function decrypt($cipherText)
{
$data = explode(':', $cipherText);
if (count($data) != 2) {
return false;
}
list($hmac, $encrypted) = $data;
$encrypted = base64_decode($encrypted);
$newHmac = $this->generateHmac($encrypted);
if ($hmac !== $newHmac) {
return false;
}
$ivDec = substr($encrypted, 0, $this->ivSize);
$cipherText = substr($encrypted, $this->ivSize);
$data = mcrypt_decrypt(
$this->cipher,
$this->key,
$cipherText,
$this->mode,
$ivDec
);
$pad = ord($data[strlen($data) - 1]);
return substr($data, 0, -$pad);
}
/**
* Generate Hmac.
*
* @param string $encrypted
*
* @return string
*/
protected function generateHmac($encrypted)
{
$macKey = $this->generateKeygenS2k('sha256', $this->key, $this->hmacIv, 32);
return hash_hmac(
'sha256',
$this->hmacIv . $this->cipher . $encrypted,
$macKey
);
}
/**
* Alternative to mhash_keygen_s2k for security reason
* and php compatibilities.
*
* @param string $hash
* @param string $password
* @param string $salt
* @param int $bytes
*
* @return string
*/
protected function generateKeygenS2k($hash, $password, $salt, $bytes)
{
$result = '';
foreach (range(0, ceil($bytes / strlen(hash($hash, null, true))) - 1) as $i) {
$result .= hash(
$hash,
str_repeat("\0", $i) . str_pad(substr($salt, 0, 8), 8, "\0", STR_PAD_RIGHT) . $password,
true
);
}
return substr(
$result,
0,
(int) $bytes
);
}
}

View File

@@ -0,0 +1,348 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class PrestaShopAutoload.
*
* @since 1.5
*/
class PrestaShopAutoload
{
/**
* @var PrestaShopAutoload
*/
protected static $instance;
/**
* @var string Root directory
*/
protected $root_dir;
/**
* @var array array('classname' => 'path/to/override', 'classnamecore' => 'path/to/class/core')
*/
public $index = array();
public $_include_override_path = true;
protected static $class_aliases = array(
'Collection' => 'PrestaShopCollection',
'Autoload' => 'PrestaShopAutoload',
'Backup' => 'PrestaShopBackup',
'Logger' => 'PrestaShopLogger',
);
protected function __construct()
{
$this->root_dir = _PS_CORE_DIR_ . '/';
$file = static::getCacheFileIndex();
$stubFile = static::getStubFileIndex();
if (@filemtime($file) && is_readable($file) && @filemtime($stubFile) && is_readable($stubFile)) {
$this->index = include $file;
} else {
$this->generateIndex();
}
}
/**
* Get instance of autoload (singleton).
*
* @return PrestaShopAutoload
*/
public static function getInstance()
{
if (!static::$instance) {
static::$instance = new static();
}
return static::$instance;
}
/**
* Get Class index cache file.
*
* @return string
*/
public static function getCacheFileIndex()
{
return _PS_ROOT_DIR_ . DIRECTORY_SEPARATOR . 'var' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . (_PS_MODE_DEV_ ? 'dev' : 'prod') . DIRECTORY_SEPARATOR . 'class_index.php';
}
/**
* Get Namespaced class stub file.
*
* @return string
*/
public static function getNamespacedStubFileIndex()
{
return _PS_ROOT_DIR_ . DIRECTORY_SEPARATOR . 'var' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . (_PS_MODE_DEV_ ? 'dev' : 'prod') . DIRECTORY_SEPARATOR . 'namespaced_class_stub.php';
}
/**
* Get Class stub file.
*
* @return string
*/
public static function getStubFileIndex()
{
return _PS_ROOT_DIR_ . DIRECTORY_SEPARATOR . 'var' . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . (_PS_MODE_DEV_ ? 'dev' : 'prod') . DIRECTORY_SEPARATOR . 'class_stub.php';
}
/**
* Retrieve informations about a class in classes index and load it.
*
* @param string $className
*/
public function load($className)
{
// Retrocompatibility
if (isset(static::$class_aliases[$className]) && !interface_exists($className, false) && !class_exists($className, false)) {
return eval('class ' . $className . ' extends ' . static::$class_aliases[$className] . ' {}');
}
// regenerate the class index if the requested file doesn't exists
if ((isset($this->index[$className]) && $this->index[$className]['path'] && !is_file($this->root_dir . $this->index[$className]['path']))
|| (isset($this->index[$className . 'Core']) && $this->index[$className . 'Core']['path'] && !is_file($this->root_dir . $this->index[$className . 'Core']['path']))
|| !file_exists(static::getNamespacedStubFileIndex())) {
$this->generateIndex();
}
// If $classname has not core suffix (E.g. Shop, Product)
if (substr($className, -4) != 'Core' && !class_exists($className, false)) {
$classDir = (isset($this->index[$className]['override'])
&& $this->index[$className]['override'] === true) ? $this->normalizeDirectory(_PS_ROOT_DIR_) : $this->root_dir;
// If requested class does not exist, load associated core class
if (isset($this->index[$className]) && !$this->index[$className]['path']) {
require_once $classDir . $this->index[$className . 'Core']['path'];
if ($this->index[$className . 'Core']['type'] != 'interface') {
eval($this->index[$className . 'Core']['type'] . ' ' . $className . ' extends ' . $className . 'Core {}');
}
} else {
// request a non Core Class load the associated Core class if exists
if (isset($this->index[$className . 'Core'])) {
require_once $this->root_dir . $this->index[$className . 'Core']['path'];
}
if (isset($this->index[$className])) {
require_once $classDir . $this->index[$className]['path'];
}
}
} elseif (isset($this->index[$className]['path']) && $this->index[$className]['path']) {
// Call directly ProductCore, ShopCore class
require_once $this->root_dir . $this->index[$className]['path'];
}
if (strpos($className, 'PrestaShop\PrestaShop\Adapter\Entity') !== false) {
require_once static::getNamespacedStubFileIndex();
}
}
/**
* Generate classes index.
*/
public function generateIndex()
{
if (class_exists('Configuration') && defined('_PS_CREATION_DATE_')) {
$creationDate = _PS_CREATION_DATE_;
if (!empty($creationDate) && Configuration::get('PS_DISABLE_OVERRIDES')) {
$this->_include_override_path = false;
} else {
$this->_include_override_path = true;
}
}
$coreClasses = $this->getClassesFromDir('classes/');
$classes = array_merge(
$coreClasses,
$this->getClassesFromDir('controllers/')
);
$contentNamespacedStub = '<?php ' . "\n" . 'namespace PrestaShop\\PrestaShop\\Adapter\\Entity;' . "\n\n";
foreach ($coreClasses as $coreClassName => $coreClass) {
if (substr($coreClassName, -4) == 'Core') {
$coreClassName = substr($coreClassName, 0, -4);
if ($coreClass['type'] != 'interface') {
$contentNamespacedStub .= $coreClass['type'] . ' ' . $coreClassName . ' extends \\' . $coreClassName . ' {};' . "\n";
}
}
}
if ($this->_include_override_path) {
$coreOverrideClasses = $this->getClassesFromDir('override/classes/', defined('_PS_HOST_MODE_'));
$coreClassesWOOverrides = array_diff_key($coreClasses, $coreOverrideClasses);
$classes = array_merge(
$classes,
$coreOverrideClasses,
$this->getClassesFromDir('override/controllers/', defined('_PS_HOST_MODE_'))
);
} else {
$coreClassesWOOverrides = $coreClasses;
}
$contentStub = '<?php' . "\n\n";
foreach ($coreClassesWOOverrides as $coreClassName => $coreClass) {
if (substr($coreClassName, -4) == 'Core') {
$coreClassNameNoCore = substr($coreClassName, 0, -4);
if ($coreClass['type'] != 'interface') {
$contentStub .= $coreClass['type'] . ' ' . $coreClassNameNoCore . ' extends ' . $coreClassName . ' {};' . "\n";
}
}
}
ksort($classes);
$content = '<?php return ' . var_export($classes, true) . '; ?>';
// Write classes index on disc to cache it
$filename = static::getCacheFileIndex();
@mkdir(_PS_CACHE_DIR_, 0777, true);
if (!$this->dumpFile($filename, $content)) {
Tools::error_log('Cannot write temporary file ' . $filename);
}
$stubFilename = static::getStubFileIndex();
if (!$this->dumpFile($stubFilename, $contentStub)) {
Tools::error_log('Cannot write temporary file ' . $stubFilename);
}
$namespacedStubFilename = static::getNamespacedStubFileIndex();
if (!$this->dumpFile($namespacedStubFilename, $contentNamespacedStub)) {
Tools::error_log('Cannot write temporary file ' . $namespacedStubFilename);
}
$this->index = $classes;
}
/**
* @param string $filename
* @param string $content
*
* @return bool
*
* @see http://api.symfony.com/3.0/Symfony/Component/Filesystem/Filesystem.html#method_dumpFile
*/
public function dumpFile($filename, $content)
{
$dir = dirname($filename);
// Will create a temp file with 0600 access rights
// when the filesystem supports chmod.
$tmpFile = tempnam($dir, basename($filename));
if (false === @file_put_contents($tmpFile, $content)) {
return false;
}
// Ignore for filesystems that do not support umask
@chmod($tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask());
rename($tmpFile, $filename);
return true;
}
/**
* Retrieve recursively all classes in a directory and its subdirectories.
*
* @param string $path Relative path from root to the directory
* @param bool $hostMode Since 1.7, deprecated.
*
* @return array
*/
protected function getClassesFromDir($path, $hostMode = false)
{
$classes = array();
$rootDir = $hostMode ? $this->normalizeDirectory(_PS_ROOT_DIR_) : $this->root_dir;
foreach (scandir($rootDir . $path, SCANDIR_SORT_NONE) as $file) {
if ($file[0] != '.') {
if (is_dir($rootDir . $path . $file)) {
$classes = array_merge($classes, $this->getClassesFromDir($path . $file . '/', $hostMode));
} elseif (substr($file, -4) == '.php') {
$content = file_get_contents($rootDir . $path . $file);
$namespacePattern = '[\\a-z0-9_]*[\\]';
$pattern = '#\W((abstract\s+)?class|interface)\s+(?P<classname>' . basename($file, '.php') . '(?:Core)?)'
. '(?:\s+extends\s+' . $namespacePattern . '[a-z][a-z0-9_]*)?(?:\s+implements\s+' . $namespacePattern . '[a-z][\\a-z0-9_]*(?:\s*,\s*' . $namespacePattern . '[a-z][\\a-z0-9_]*)*)?\s*\{#i';
//DONT LOAD CLASS WITH NAMESPACE - PSR4 autoloaded from composer
$usesNamespace = false;
foreach (token_get_all($content) as $token) {
if ($token[0] === T_NAMESPACE) {
$usesNamespace = true;
break;
}
}
if (!$usesNamespace && preg_match($pattern, $content, $m)) {
$classes[$m['classname']] = array(
'path' => $path . $file,
'type' => trim($m[1]),
'override' => $hostMode,
);
if (substr($m['classname'], -4) == 'Core') {
$classes[substr($m['classname'], 0, -4)] = array(
'path' => '',
'type' => $classes[$m['classname']]['type'],
'override' => $hostMode,
);
}
}
}
}
}
return $classes;
}
/**
* Get Class path.
*
* @param string $classname
*/
public function getClassPath($classname)
{
return (isset($this->index[$classname]['path'])) ? $this->index[$classname]['path'] : null;
}
/**
* Normalize directory.
*
* @param string $directory
*
* @return string
*/
private function normalizeDirectory($directory)
{
return rtrim($directory, '/\\') . DIRECTORY_SEPARATOR;
}
}
spl_autoload_register(array(PrestaShopAutoload::getInstance(), 'load'));

View File

@@ -0,0 +1,350 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class PrestaShopBackupCore.
*/
class PrestaShopBackupCore
{
/** @var int Object id */
public $id;
/** @var string Last error messages */
public $error;
/** @var string default backup directory. */
public static $backupDir = '/backups/';
/** @var string custom backup directory. */
public $customBackupDir = null;
public $psBackupAll = true;
public $psBackupDropTable = true;
/**
* Creates a new backup object.
*
* @param string $filename Filename of the backup file
*/
public function __construct($filename = null)
{
if ($filename) {
$this->id = $this->getRealBackupPath($filename);
}
$psBackupAll = Configuration::get('PS_BACKUP_ALL');
$psBackupDropTable = Configuration::get('PS_BACKUP_DROP_TABLE');
$this->psBackupAll = $psBackupAll !== false ? $psBackupAll : true;
$this->psBackupDropTable = $psBackupDropTable !== false ? $psBackupDropTable : true;
}
/**
* you can set a different path with that function.
*
* @TODO include the prefix name
*
* @param string $dir
*
* @return bool bo
*/
public function setCustomBackupPath($dir)
{
$customDir = DIRECTORY_SEPARATOR . trim($dir, '/') . DIRECTORY_SEPARATOR;
if (is_dir((defined('_PS_HOST_MODE_') ? _PS_ROOT_DIR_ : _PS_ADMIN_DIR_) . $customDir)) {
$this->customBackupDir = $customDir;
} else {
return false;
}
return true;
}
/**
* get the path to use for backup (customBackupDir if specified, or default).
*
* @param string $filename filename to use
*
* @return string full path
*/
public function getRealBackupPath($filename = null)
{
$backupDir = PrestaShopBackup::getBackupPath($filename);
if (!empty($this->customBackupDir)) {
$backupDir = str_replace(
(defined('_PS_HOST_MODE_') ? _PS_ROOT_DIR_ : _PS_ADMIN_DIR_) . self::$backupDir,
(defined('_PS_HOST_MODE_') ? _PS_ROOT_DIR_ : _PS_ADMIN_DIR_) . $this->customBackupDir,
$backupDir
);
if (strrpos($backupDir, DIRECTORY_SEPARATOR)) {
$backupDir .= DIRECTORY_SEPARATOR;
}
}
return $backupDir;
}
/**
* Get the full path of the backup file.
*
* @param string $filename prefix of the backup file (datetime will be the second part)
*
* @return string The full path of the backup file, or false if the backup file does not exists
*/
public static function getBackupPath($filename = '')
{
$backupdir = realpath((defined('_PS_HOST_MODE_') ? _PS_ROOT_DIR_ : _PS_ADMIN_DIR_) . self::$backupDir);
if ($backupdir === false) {
die(Tools::displayError(Context::getContext()->getTranslator()->trans('"Backup" directory does not exist.', array(), 'Admin.Advparameters.Notification')));
}
// Check the realpath so we can validate the backup file is under the backup directory
if (!empty($filename)) {
$backupfile = realpath($backupdir . DIRECTORY_SEPARATOR . $filename);
} else {
$backupfile = $backupdir . DIRECTORY_SEPARATOR;
}
if ($backupfile === false || strncmp($backupdir, $backupfile, strlen($backupdir)) != 0) {
die(Tools::displayError());
}
return $backupfile;
}
/**
* Check if a backup file exist.
*
* @param string $filename prefix of the backup file (datetime will be the second part)
*
* @return bool true if backup file exist
*/
public static function backupExist($filename)
{
$backupdir = realpath((defined('_PS_HOST_MODE_') ? _PS_ROOT_DIR_ : _PS_ADMIN_DIR_) . self::$backupDir);
if ($backupdir === false) {
die(Tools::displayError(Context::getContext()->getTranslator()->trans('"Backup" directory does not exist.', array(), 'Admin.Advparameters.Notification')));
}
return @filemtime($backupdir . DIRECTORY_SEPARATOR . $filename);
}
/**
* Get the URL used to retrieve this backup file.
*
* @return string The url used to request the backup file
*
* @deprecated As the call has been duplicated in the new Controller. Get the URL from the router instead.
*/
public function getBackupURL()
{
// Additionnal parameters (action, filename, ajax) are kept for backward compatibility, in case we disable the new controller
return Context::getContext()->link->getAdminLink(
'AdminBackup',
true,
[
'route' => 'admin_backup_download',
'downloadFileName' => basename($this->id),
],
[
'action' => 'backupContent',
'ajax' => 1,
'filename' => basename($this->id),
]
);
}
/**
* Delete the current backup file.
*
* @return bool Deletion result, true on success
*/
public function delete()
{
if (!$this->id || !unlink($this->id)) {
$this->error = Context::getContext()->getTranslator()->trans('Error deleting', array(), 'Admin.Advparameters.Notification') . ' ' . ($this->id ? '"' . $this->id . '"' :
Context::getContext()->getTranslator()->trans('Invalid ID', array(), 'Admin.Advparameters.Notification'));
return false;
}
return true;
}
/**
* Deletes a range of backup files.
*
* @return bool True on success
*/
public function deleteSelection($list)
{
foreach ($list as $file) {
$backup = new PrestaShopBackup($file);
if (!$backup->delete()) {
$this->error = $backup->error;
return false;
}
}
return true;
}
/**
* Creates a new backup file.
*
* @return bool true on successful backup
*/
public function add()
{
if (!$this->psBackupAll) {
$ignoreInsertTable = array(_DB_PREFIX_ . 'connections', _DB_PREFIX_ . 'connections_page', _DB_PREFIX_
. 'connections_source', _DB_PREFIX_ . 'guest', _DB_PREFIX_ . 'statssearch',
);
} else {
$ignoreInsertTable = array();
}
// Generate some random number, to make it extra hard to guess backup file names
$rand = dechex(mt_rand(0, min(0xffffffff, mt_getrandmax())));
$date = time();
$backupfile = $this->getRealBackupPath() . $date . '-' . $rand . '.sql';
// Figure out what compression is available and open the file
if (function_exists('bzopen')) {
$backupfile .= '.bz2';
$fp = @bzopen($backupfile, 'w');
} elseif (function_exists('gzopen')) {
$backupfile .= '.gz';
$fp = @gzopen($backupfile, 'w');
} else {
$fp = @fopen($backupfile, 'wb');
}
if ($fp === false) {
echo Context::getContext()->getTranslator()->trans('Unable to create backup file', array(), 'Admin.Advparameters.Notification') . ' "' . addslashes($backupfile) . '"';
return false;
}
$this->id = realpath($backupfile);
fwrite($fp, '/* Backup for ' . Tools::getHttpHost(false, false) . __PS_BASE_URI__ . "\n * at " . date($date) . "\n */\n");
fwrite($fp, "\n" . 'SET NAMES \'utf8\';');
fwrite($fp, "\n" . 'SET FOREIGN_KEY_CHECKS = 0;');
fwrite($fp, "\n" . 'SET SESSION sql_mode = \'\';' . "\n\n");
// Find all tables
$tables = Db::getInstance()->executeS('SHOW TABLES');
$found = 0;
foreach ($tables as $table) {
$table = current($table);
// Skip tables which do not start with _DB_PREFIX_
if (strlen($table) < strlen(_DB_PREFIX_) || strncmp($table, _DB_PREFIX_, strlen(_DB_PREFIX_)) != 0) {
continue;
}
// Export the table schema
$schema = Db::getInstance()->executeS('SHOW CREATE TABLE `' . $table . '`');
if (count($schema) != 1 || !isset($schema[0]['Table']) || !isset($schema[0]['Create Table'])) {
fclose($fp);
$this->delete();
echo Context::getContext()->getTranslator()->trans('An error occurred while backing up. Unable to obtain the schema of %s', array($table), 'Admin.Advparameters.Notification');
return false;
}
fwrite($fp, '/* Scheme for table ' . $schema[0]['Table'] . " */\n");
if ($this->psBackupDropTable) {
fwrite($fp, 'DROP TABLE IF EXISTS `' . $schema[0]['Table'] . '`;' . "\n");
}
fwrite($fp, $schema[0]['Create Table'] . ";\n\n");
if (!in_array($schema[0]['Table'], $ignoreInsertTable)) {
$data = Db::getInstance()->query('SELECT * FROM `' . $schema[0]['Table'] . '`', false);
$sizeof = Db::getInstance()->numRows();
$lines = explode("\n", $schema[0]['Create Table']);
if ($data && $sizeof > 0) {
// Export the table data
fwrite($fp, 'INSERT INTO `' . $schema[0]['Table'] . "` VALUES\n");
$i = 1;
while ($row = Db::getInstance()->nextRow($data)) {
$s = '(';
foreach ($row as $field => $value) {
$tmp = "'" . pSQL($value, true) . "',";
if ($tmp != "'',") {
$s .= $tmp;
} else {
foreach ($lines as $line) {
if (strpos($line, '`' . $field . '`') !== false) {
if (preg_match('/(.*NOT NULL.*)/Ui', $line)) {
$s .= "'',";
} else {
$s .= 'NULL,';
}
break;
}
}
}
}
$s = rtrim($s, ',');
if ($i % 200 == 0 && $i < $sizeof) {
$s .= ");\nINSERT INTO `" . $schema[0]['Table'] . "` VALUES\n";
} elseif ($i < $sizeof) {
$s .= "),\n";
} else {
$s .= ");\n";
}
fwrite($fp, $s);
++$i;
}
}
}
++$found;
}
fclose($fp);
if ($found == 0) {
$this->delete();
echo Context::getContext()->getTranslator()->trans('No valid tables were found to backup.', array(), 'Admin.Advparameters.Notification');
return false;
}
return true;
}
}

View File

@@ -0,0 +1,782 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Create a collection of ObjectModel objects.
*
* @since 1.5.0
*/
class PrestaShopCollectionCore implements Iterator, ArrayAccess, Countable
{
const LEFT_JOIN = 1;
const INNER_JOIN = 2;
const LEFT_OUTER_JOIN = 3;
/**
* @var string Object class name
*/
protected $classname;
/**
* @var int
*/
protected $id_lang;
/**
* @var array Object definition
*/
protected $definition = array();
/**
* @var DbQuery
*/
protected $query;
/**
* @var array Collection of objects in an array
*/
protected $results = array();
/**
* @var bool Is current collection already hydrated
*/
protected $is_hydrated = false;
/**
* @var int Collection iterator
*/
protected $iterator = 0;
/**
* @var int Total of elements for iteration
*/
protected $total;
/**
* @var int Page number
*/
protected $page_number = 0;
/**
* @var int Size of a page
*/
protected $page_size = 0;
protected $fields = array();
protected $alias = array();
protected $alias_iterator = 0;
protected $join_list = array();
protected $association_definition = array();
const LANG_ALIAS = 'l';
/**
* @param string $classname
* @param int $id_lang
*/
public function __construct($classname, $id_lang = null)
{
$this->classname = $classname;
$this->id_lang = $id_lang;
$this->definition = ObjectModel::getDefinition($this->classname);
if (!isset($this->definition['table'])) {
throw new PrestaShopException('Miss table in definition for class ' . $this->classname);
} elseif (!isset($this->definition['primary'])) {
throw new PrestaShopException('Miss primary in definition for class ' . $this->classname);
}
$this->query = new DbQuery();
}
/**
* Join current entity to an associated entity.
*
* @param string $association Association name
* @param string $on
* @param int $type
*
* @return PrestaShopCollection
*/
public function join($association, $on = '', $type = null)
{
if (!$association) {
return;
}
if (!isset($this->join_list[$association])) {
$definition = $this->getDefinition($association);
$on = '{' . $definition['asso']['complete_field'] . '} = {' . $definition['asso']['complete_foreign_field'] . '}';
$type = self::LEFT_JOIN;
$this->join_list[$association] = array(
'table' => ($definition['is_lang']) ? $definition['table'] . '_lang' : $definition['table'],
'alias' => $this->generateAlias($association),
'on' => array(),
);
}
if ($on) {
$this->join_list[$association]['on'][] = $this->parseFields($on);
}
if ($type) {
$this->join_list[$association]['type'] = $type;
}
return $this;
}
/**
* Add WHERE restriction on query.
*
* @param string $field Field name
* @param string $operator List of operators : =, !=, <>, <, <=, >, >=, like, notlike, regexp, notregexp
* @param mixed $value
* @param string $type where|having
*
* @return PrestaShopCollection
*/
public function where($field, $operator, $value, $method = 'where')
{
if ($method != 'where' && $method != 'having') {
throw new PrestaShopException('Bad method argument for where() method (should be "where" or "having")');
}
// Create WHERE clause with an array value (IN, NOT IN)
if (is_array($value)) {
switch (strtolower($operator)) {
case '=':
case 'in':
$this->query->$method($this->parseField($field) . ' IN(' . implode(', ', $this->formatValue($value, $field)) . ')');
break;
case '!=':
case '<>':
case 'notin':
$this->query->$method($this->parseField($field) . ' NOT IN(' . implode(', ', $this->formatValue($value, $field)) . ')');
break;
default:
throw new PrestaShopException('Operator not supported for array value');
}
} else {
// Create WHERE clause
switch (strtolower($operator)) {
case '=':
case '!=':
case '<>':
case '>':
case '>=':
case '<':
case '<=':
case 'like':
case 'regexp':
$this->query->$method($this->parseField($field) . ' ' . $operator . ' ' . $this->formatValue($value, $field));
break;
case 'notlike':
$this->query->$method($this->parseField($field) . ' NOT LIKE ' . $this->formatValue($value, $field));
break;
case 'notregexp':
$this->query->$method($this->parseField($field) . ' NOT REGEXP ' . $this->formatValue($value, $field));
break;
default:
throw new PrestaShopException('Operator not supported');
}
}
return $this;
}
/**
* Add WHERE restriction on query using real SQL syntax.
*
* @param string $sql
*
* @return PrestaShopCollection
*/
public function sqlWhere($sql)
{
$this->query->where($this->parseFields($sql));
return $this;
}
/**
* Add HAVING restriction on query.
*
* @param string $field Field name
* @param string $operator List of operators : =, !=, <>, <, <=, >, >=, like, notlike, regexp, notregexp
* @param mixed $value
*
* @return PrestaShopCollection
*/
public function having($field, $operator, $value)
{
return $this->where($field, $operator, $value, 'having');
}
/**
* Add HAVING restriction on query using real SQL syntax.
*
* @param string $sql
*
* @return PrestaShopCollection
*/
public function sqlHaving($sql)
{
$this->query->having($this->parseFields($sql));
return $this;
}
/**
* Add ORDER BY restriction on query.
*
* @param string $field Field name
* @param string $order asc|desc
*
* @return PrestaShopCollection
*/
public function orderBy($field, $order = 'asc')
{
$order = strtolower($order);
if ($order != 'asc' && $order != 'desc') {
throw new PrestaShopException('Order must be asc or desc');
}
$this->query->orderBy($this->parseField($field) . ' ' . $order);
return $this;
}
/**
* Add ORDER BY restriction on query using real SQL syntax.
*
* @param string $sql
*
* @return PrestaShopCollection
*/
public function sqlOrderBy($sql)
{
$this->query->orderBy($this->parseFields($sql));
return $this;
}
/**
* Add GROUP BY restriction on query.
*
* @param string $field Field name
*
* @return PrestaShopCollection
*/
public function groupBy($field)
{
$this->query->groupBy($this->parseField($field));
return $this;
}
/**
* Add GROUP BY restriction on query using real SQL syntax.
*
* @param string $sql
*
* @return PrestaShopCollection
*/
public function sqlGroupBy($sql)
{
$this->query->groupBy($this->parseFields($sql));
return $this;
}
/**
* Launch sql query to create collection of objects.
*
* @param bool $display_query If true, query will be displayed (for debug purpose)
*
* @return PrestaShopCollection
*/
public function getAll($display_query = false)
{
if ($this->is_hydrated) {
return $this;
}
$this->is_hydrated = true;
$alias = $this->generateAlias();
//$this->query->select($alias.'.*');
$this->query->from($this->definition['table'], $alias);
// If multilang, create association to lang table
if (!empty($this->definition['multilang'])) {
$this->join(self::LANG_ALIAS);
if ($this->id_lang) {
$this->where(self::LANG_ALIAS . '.id_lang', '=', $this->id_lang);
}
}
// Add join clause
foreach ($this->join_list as $data) {
$on = '(' . implode(') AND (', $data['on']) . ')';
switch ($data['type']) {
case self::LEFT_JOIN:
$this->query->leftJoin($data['table'], $data['alias'], $on);
break;
case self::INNER_JOIN:
$this->query->innerJoin($data['table'], $data['alias'], $on);
break;
case self::LEFT_OUTER_JOIN:
$this->query->leftOuterJoin($data['table'], $data['alias'], $on);
break;
}
}
// All limit clause
if ($this->page_size) {
$this->query->limit($this->page_size, $this->page_number * $this->page_size);
}
// Shall we display query for debug ?
if ($display_query) {
echo $this->query . '<br />';
}
$this->results = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($this->query);
if ($this->results && is_array($this->results)) {
$this->results = ObjectModel::hydrateCollection($this->classname, $this->results, $this->id_lang);
}
return $this;
}
/**
* Retrieve the first result.
*
* @return ObjectModel
*/
public function getFirst()
{
$this->getAll();
if (!count($this)) {
return false;
}
return $this[0];
}
/**
* Get results array.
*
* @return array
*/
public function getResults()
{
$this->getAll();
return $this->results;
}
/**
* This method is called when a foreach begin.
*
* @see Iterator::rewind()
*/
public function rewind()
{
$this->getAll();
$this->results = array_merge($this->results);
$this->iterator = 0;
$this->total = count($this->results);
}
/**
* Get current result.
*
* @see Iterator::current()
*
* @return ObjectModel
*/
public function current()
{
return isset($this->results[$this->iterator]) ? $this->results[$this->iterator] : null;
}
/**
* Check if there is a current result.
*
* @see Iterator::valid()
*
* @return bool
*/
public function valid()
{
return $this->iterator < $this->total;
}
/**
* Get current result index.
*
* @see Iterator::key()
*
* @return int
*/
public function key()
{
return $this->iterator;
}
/**
* Go to next result.
*
* @see Iterator::next()
*/
public function next()
{
++$this->iterator;
}
/**
* Get total of results.
*
* @see Countable::count()
*
* @return int
*/
public function count()
{
$this->getAll();
return count($this->results);
}
/**
* Check if a result exist.
*
* @see ArrayAccess::offsetExists()
*
* @param $offset
*
* @return bool
*/
public function offsetExists($offset)
{
$this->getAll();
return isset($this->results[$offset]);
}
/**
* Get a result by offset.
*
* @see ArrayAccess::offsetGet()
*
* @param $offset
*
* @return ObjectModel
*/
public function offsetGet($offset)
{
$this->getAll();
if (!isset($this->results[$offset])) {
throw new PrestaShopException('Unknown offset ' . $offset . ' for collection ' . $this->classname);
}
return $this->results[$offset];
}
/**
* Add an element in the collection.
*
* @see ArrayAccess::offsetSet()
*
* @param $offset
* @param $value
*/
public function offsetSet($offset, $value)
{
if (!$value instanceof $this->classname) {
throw new PrestaShopException('You cannot add an element which is not an instance of ' . $this->classname);
}
$this->getAll();
if (null === $offset) {
$this->results[] = $value;
} else {
$this->results[$offset] = $value;
}
}
/**
* Delete an element from the collection.
*
* @see ArrayAccess::offsetUnset()
*
* @param $offset
*/
public function offsetUnset($offset)
{
$this->getAll();
unset($this->results[$offset]);
}
/**
* Get definition of an association.
*
* @param string $association
*
* @return array
*/
protected function getDefinition($association)
{
if (!$association) {
return $this->definition;
}
if (!isset($this->association_definition[$association])) {
$definition = $this->definition;
$split = explode('.', $association);
$is_lang = false;
for ($i = 0, $total_association = count($split); $i < $total_association; ++$i) {
$asso = $split[$i];
// Check is current association exists in current definition
if (!isset($definition['associations'][$asso])) {
throw new PrestaShopException('Association ' . $asso . ' not found for class ' . $this->definition['classname']);
}
$current_def = $definition['associations'][$asso];
// Special case for lang alias
if ($asso == self::LANG_ALIAS) {
$is_lang = true;
break;
}
$classname = (isset($current_def['object'])) ? $current_def['object'] : Tools::toCamelCase($asso, true);
$definition = ObjectModel::getDefinition($classname);
}
// Get definition of associated entity and add information on current association
$current_def['name'] = $asso;
if (!isset($current_def['object'])) {
$current_def['object'] = Tools::toCamelCase($asso, true);
}
if (!isset($current_def['field'])) {
$current_def['field'] = 'id_' . $asso;
}
if (!isset($current_def['foreign_field'])) {
$current_def['foreign_field'] = 'id_' . $asso;
}
if ($total_association > 1) {
unset($split[$total_association - 1]);
$current_def['complete_field'] = implode('.', $split) . '.' . $current_def['field'];
} else {
$current_def['complete_field'] = $current_def['field'];
}
$current_def['complete_foreign_field'] = $association . '.' . $current_def['foreign_field'];
$definition['is_lang'] = $is_lang;
$definition['asso'] = $current_def;
$this->association_definition[$association] = $definition;
} else {
$definition = $this->association_definition[$association];
}
return $definition;
}
/**
* Parse all fields with {field} syntax in a string.
*
* @param string $str
*
* @return string
*/
protected function parseFields($str)
{
preg_match_all('#\{(([a-z0-9_]+\.)*[a-z0-9_]+)\}#i', $str, $m);
for ($i = 0, $total = count($m[0]); $i < $total; ++$i) {
$str = str_replace($m[0][$i], $this->parseField($m[1][$i]), $str);
}
return $str;
}
/**
* Replace a field with its SQL version (E.g. manufacturer.name with a2.name).
*
* @param string $field Field name
*
* @return string
*/
protected function parseField($field)
{
$info = $this->getFieldInfo($field);
return $info['alias'] . '.`' . $info['name'] . '`';
}
/**
* Format a value with the type of the given field.
*
* @param mixed $value
* @param string $field Field name
*
* @return mixed
*/
protected function formatValue($value, $field)
{
$info = $this->getFieldInfo($field);
if (is_array($value)) {
$results = array();
foreach ($value as $item) {
$results[] = ObjectModel::formatValue($item, $info['type'], true);
}
return $results;
}
return ObjectModel::formatValue($value, $info['type'], true);
}
/**
* Obtain some information on a field (alias, name, type, etc.).
*
* @param string $field Field name
*
* @return array
*/
protected function getFieldInfo($field)
{
if (!isset($this->fields[$field])) {
$split = explode('.', $field);
$total = count($split);
if ($total > 1) {
$fieldname = $split[$total - 1];
unset($split[$total - 1]);
$association = implode('.', $split);
} else {
$fieldname = $field;
$association = '';
}
$definition = $this->getDefinition($association);
if ($association && !isset($this->join_list[$association])) {
$this->join($association);
}
if ($fieldname == $definition['primary'] || (!empty($definition['is_lang']) && $fieldname == 'id_lang')) {
$type = ObjectModel::TYPE_INT;
} else {
// Test if field exists
if (!isset($definition['fields'][$fieldname])) {
throw new PrestaShopException('Field ' . $fieldname . ' not found in class ' . $definition['classname']);
}
// Test field validity for language fields
if (empty($definition['is_lang']) && !empty($definition['fields'][$fieldname]['lang'])) {
throw new PrestaShopException('Field ' . $fieldname . ' is declared as lang field but is used in non multilang context');
} elseif (!empty($definition['is_lang']) && empty($definition['fields'][$fieldname]['lang'])) {
throw new PrestaShopException('Field ' . $fieldname . ' is not declared as lang field but is used in multilang context');
}
$type = $definition['fields'][$fieldname]['type'];
}
$this->fields[$field] = array(
'name' => $fieldname,
'association' => $association,
'alias' => $this->generateAlias($association),
'type' => $type,
);
}
return $this->fields[$field];
}
/**
* Set the page number.
*
* @param int $page_number
*
* @return PrestaShopCollection
*/
public function setPageNumber($page_number)
{
$page_number = (int) $page_number;
if ($page_number > 0) {
--$page_number;
}
$this->page_number = $page_number;
return $this;
}
/**
* Set the nuber of item per page.
*
* @param int $page_size
*
* @return PrestaShopCollection
*/
public function setPageSize($page_size)
{
$this->page_size = (int) $page_size;
return $this;
}
/**
* Generate uniq alias from association name.
*
* @param string $association Use empty association for alias on current table
*
* @return string
*/
protected function generateAlias($association = '')
{
if (!isset($this->alias[$association])) {
$this->alias[$association] = 'a' . $this->alias_iterator++;
}
return $this->alias[$association];
}
}

View File

@@ -0,0 +1,204 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class PrestaShopLoggerCore.
*/
class PrestaShopLoggerCore extends ObjectModel
{
/** @var int Log id */
public $id_log;
/** @var int Log severity */
public $severity;
/** @var int Error code */
public $error_code;
/** @var string Message */
public $message;
/** @var string Object type (eg. Order, Customer...) */
public $object_type;
/** @var int Object ID */
public $object_id;
/** @var int Object ID */
public $id_employee;
/** @var string Object creation date */
public $date_add;
/** @var string Object last modification date */
public $date_upd;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'log',
'primary' => 'id_log',
'fields' => array(
'severity' => array('type' => self::TYPE_INT, 'validate' => 'isInt', 'required' => true),
'error_code' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
'message' => array('type' => self::TYPE_STRING, 'validate' => 'isString', 'required' => true),
'object_id' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
'id_employee' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
'object_type' => array('type' => self::TYPE_STRING, 'validate' => 'isName'),
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
),
);
protected static $is_present = array();
/**
* Send e-mail to the shop owner only if the minimal severity level has been reached.
*
* @param Logger
* @param PrestaShopLogger $log
*/
public static function sendByMail($log)
{
if ((int) Configuration::get('PS_LOGS_BY_EMAIL') <= (int) $log->severity) {
$language = new Language((int) Configuration::get('PS_LANG_DEFAULT'));
Mail::Send(
(int) Configuration::get('PS_LANG_DEFAULT'),
'log_alert',
Context::getContext()->getTranslator()->trans(
'Log: You have a new alert from your shop',
array(),
'Emails.Subject',
$language->locale
),
array(),
Configuration::get('PS_SHOP_EMAIL')
);
}
}
/**
* add a log item to the database and send a mail if configured for this $severity.
*
* @param string $message the log message
* @param int $severity
* @param int $errorCode
* @param string $objectType
* @param int $objectId
* @param bool $allowDuplicate if set to true, can log several time the same information (not recommended)
*
* @return bool true if succeed
*/
public static function addLog($message, $severity = 1, $errorCode = null, $objectType = null, $objectId = null, $allowDuplicate = false, $idEmployee = null)
{
$log = new PrestaShopLogger();
$log->severity = (int) $severity;
$log->error_code = (int) $errorCode;
$log->message = pSQL($message);
$log->date_add = date('Y-m-d H:i:s');
$log->date_upd = date('Y-m-d H:i:s');
if ($idEmployee === null && isset(Context::getContext()->employee) && Validate::isLoadedObject(Context::getContext()->employee)) {
$idEmployee = Context::getContext()->employee->id;
}
if ($idEmployee !== null) {
$log->id_employee = (int) $idEmployee;
}
if (!empty($objectType) && !empty($objectId)) {
$log->object_type = pSQL($objectType);
$log->object_id = (int) $objectId;
}
if ($objectType != 'Swift_Message') {
PrestaShopLogger::sendByMail($log);
}
if ($allowDuplicate || !$log->_isPresent()) {
$res = $log->add();
if ($res) {
self::$is_present[$log->getHash()] = isset(self::$is_present[$log->getHash()]) ? self::$is_present[$log->getHash()] + 1 : 1;
return true;
}
}
return false;
}
/**
* this function md5($this->message.$this->severity.$this->error_code.$this->object_type.$this->object_id).
*
* @return string hash
*/
public function getHash()
{
if (empty($this->hash)) {
$this->hash = md5($this->message . $this->severity . $this->error_code . $this->object_type . $this->object_id);
}
return $this->hash;
}
public static function eraseAllLogs()
{
return Db::getInstance()->execute('TRUNCATE TABLE ' . _DB_PREFIX_ . 'log');
}
/**
* @deprecated 1.7.0
*/
protected function _isPresent()
{
return $this->isPresent();
}
/**
* check if this log message already exists in database.
*
* @return true if exists
*
* @since 1.7.0
*/
protected function isPresent()
{
if (!isset(self::$is_present[md5($this->message)])) {
self::$is_present[$this->getHash()] = Db::getInstance()->getValue('SELECT COUNT(*)
FROM `' . _DB_PREFIX_ . 'log`
WHERE
`message` = \'' . $this->message . '\'
AND `severity` = \'' . $this->severity . '\'
AND `error_code` = \'' . $this->error_code . '\'
AND `object_type` = \'' . $this->object_type . '\'
AND `object_id` = \'' . $this->object_id . '\'
');
}
return self::$is_present[$this->getHash()];
}
}

7069
classes/Product.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,115 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchContext;
/**
* Class ProductAssemblerCore.
*/
class ProductAssemblerCore
{
private $context;
private $searchContext;
/**
* ProductAssemblerCore constructor.
*
* @param \Context $context
*/
public function __construct(Context $context)
{
$this->context = $context;
$this->searchContext = new ProductSearchContext($context);
}
/**
* Add missing product fields.
*
* @param array $rawProduct
*
* @return array
*/
private function addMissingProductFields(array $rawProduct)
{
$idShop = (int) $this->searchContext->getIdShop();
$idLang = (int) $this->searchContext->getIdLang();
$idProduct = (int) $rawProduct['id_product'];
$prefix = _DB_PREFIX_;
$nbDaysNewProduct = (int) Configuration::get('PS_NB_DAYS_NEW_PRODUCT');
if (!Validate::isUnsignedInt($nbDaysNewProduct)) {
$nbDaysNewProduct = 20;
}
$now = date('Y-m-d') . ' 00:00:00';
$sql = "SELECT
p.*,
pl.*,
sa.out_of_stock,
IFNULL(sa.quantity, 0) as quantity,
(DATEDIFF(
p.`date_add`,
DATE_SUB(
'$now',
INTERVAL $nbDaysNewProduct DAY
)
) > 0) as new
FROM {$prefix}product p
LEFT JOIN {$prefix}product_lang pl
ON pl.id_product = p.id_product
AND pl.id_shop = $idShop
AND pl.id_lang = $idLang
LEFT JOIN {$prefix}stock_available sa
ON sa.id_product = p.id_product
AND sa.id_shop = $idShop
WHERE p.id_product = $idProduct";
$rows = Db::getInstance()->executeS($sql);
if ($rows === false) {
return $rawProduct;
}
return array_merge($rows[0], $rawProduct);
}
/**
* Assemble Product.
*
* @param array $rawProduct
*
* @return mixed
*/
public function assembleProduct(array $rawProduct)
{
$enrichedProduct = $this->addMissingProductFields($rawProduct);
return Product::getProductProperties(
$this->searchContext->getIdLang(),
$enrichedProduct,
$this->context
);
}
}

335
classes/ProductDownload.php Normal file
View File

@@ -0,0 +1,335 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ProductDownloadCore.
*/
class ProductDownloadCore extends ObjectModel
{
/** @var int Product id which download belongs */
public $id_product;
/** @var string DisplayFilename the name which appear */
public $display_filename;
/** @var string PhysicallyFilename the name of the file on hard disk */
public $filename;
/** @var string DateDeposit when the file is upload */
public $date_add;
/** @var string DateExpiration deadline of the file */
public $date_expiration;
/** @var string NbDaysAccessible how many days the customer can access to file */
public $nb_days_accessible;
/** @var string NbDownloadable how many time the customer can download the file */
public $nb_downloadable;
/** @var bool Active if file is accessible or not */
public $active = 1;
/** @var bool is_shareable indicates whether the product can be shared */
public $is_shareable = 0;
protected static $_productIds = array();
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'product_download',
'primary' => 'id_product_download',
'fields' => array(
'id_product' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'display_filename' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 255),
'filename' => array('type' => self::TYPE_STRING, 'validate' => 'isSha1', 'size' => 255),
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
'date_expiration' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
'nb_days_accessible' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'size' => 10),
'nb_downloadable' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'size' => 10),
'active' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
'is_shareable' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
),
);
/**
* Build a virtual product.
*
* @param int $idProductDownload Existing productDownload id in order to load object (optional)
*/
public function __construct($idProductDownload = null)
{
parent::__construct($idProductDownload);
// @TODO check if the file is present on hard drive
}
/**
* @see ObjectModel::getFields()
*
* @return array
*/
public function getFields()
{
$fields = parent::getFields();
if (!$fields['date_expiration']) {
$fields['date_expiration'] = '0000-00-00 00:00:00';
}
return $fields;
}
public function add($autoDate = true, $nullValues = false)
{
return (bool) parent::add($autoDate, $nullValues);
}
public function update($nullValues = false)
{
if (parent::update($nullValues)) {
// Refresh cache of feature detachable because the row can be deactive
//Configuration::updateGlobalValue('PS_VIRTUAL_PROD_FEATURE_ACTIVE', ProductDownload::isCurrentlyUsed($this->def['table'], true));
return true;
}
return false;
}
public function delete($deleteFile = false)
{
$result = parent::delete();
if ($result && $deleteFile) {
return $this->deleteFile();
}
return $result;
}
/**
* Delete the file.
*
* @param int $idProductDownload : if we need to delete a specific product attribute file
*
* @return bool
*/
public function deleteFile($idProductDownload = null)
{
if (!$this->checkFile()) {
return false;
}
return unlink(_PS_DOWNLOAD_DIR_ . $this->filename)
&& Db::getInstance()->delete('product_download', 'id_product_download = ' . (int) $idProductDownload);
}
/**
* Check if file exists.
*
* @return bool
*/
public function checkFile()
{
if (!$this->filename) {
return false;
}
return file_exists(_PS_DOWNLOAD_DIR_ . $this->filename);
}
/**
* Check if download repository is writable.
*
* @return bool
*/
public static function checkWritableDir()
{
return is_writable(_PS_DOWNLOAD_DIR_);
}
/**
* Return the id_product_download from an id_product.
*
* @param int $idProduct Product the id
*
* @return int Product the id for this virtual product
*/
public static function getIdFromIdProduct($idProduct, $active = true)
{
if (!ProductDownload::isFeatureActive()) {
return false;
}
self::$_productIds[$idProduct] = (int) Db::getInstance()->getValue('
SELECT `id_product_download`
FROM `' . _DB_PREFIX_ . 'product_download`
WHERE `id_product` = ' . (int) $idProduct . '
' . ($active ? ' AND `active` = 1' : '') . '
ORDER BY `id_product_download` DESC');
return self::$_productIds[$idProduct];
}
/**
* Return the display filename from a physical filename.
*
* @param string $filename Filename physically
*
* @return int Product the id for this virtual product
*
* @since 1.5.0.1
*/
public static function getIdFromFilename($filename)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
SELECT `id_product_download`
FROM `' . _DB_PREFIX_ . 'product_download`
WHERE `filename` = \'' . pSQL($filename) . '\'');
}
/**
* Return the filename from a Product ID.
*
* @param int $idProduct Product ID
*
* @return string Filename the filename for this virtual product
*/
public static function getFilenameFromIdProduct($idProduct)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
SELECT `filename`
FROM `' . _DB_PREFIX_ . 'product_download`
WHERE `id_product` = ' . (int) $idProduct . '
AND `active` = 1
');
}
/**
* Return the display filename from a physical filename.
*
* @param string $filename Filename physically
*
* @return string Filename the display filename for this virtual product
*/
public static function getFilenameFromFilename($filename)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
SELECT `display_filename`
FROM `' . _DB_PREFIX_ . 'product_download`
WHERE `filename` = \'' . pSQL($filename) . '\'');
}
/**
* Return text link.
*
* @param bool $admin specific to backend (optionnal)
* @param string $hash hash code in table order detail (optionnal)
*
* @return string Html all the code for print a link to the file
*/
public function getTextLink($admin = true, $hash = false)
{
if ($admin) {
return 'get-file-admin.php?file=' . $this->filename;
}
$key = $this->filename . '-' . ($hash ? $hash : 'orderdetail');
return Context::getContext()->link->getPageLink('get-file&key=' . $key);
}
/**
* Return html link.
*
* @param string $class CSS selector
* @param bool $admin specific to backend
* @param bool $hash hash code in table order detail
*
* @return string Html all the code for print a link to the file
*/
public function getHtmlLink($class = false, $admin = true, $hash = false)
{
$link = $this->getTextLink($admin, $hash);
$html = '<a href="' . $link . '" title=""';
if ($class) {
$html .= ' class="' . $class . '"';
}
$html .= '>' . $this->display_filename . '</a>';
return $html;
}
/**
* Return a deadline.
*
* @return string Datetime in SQL format
*/
public function getDeadline()
{
if (!(int) $this->nb_days_accessible) {
return '0000-00-00 00:00:00';
}
$timestamp = strtotime('+' . (int) $this->nb_days_accessible . ' day');
return date('Y-m-d H:i:s', $timestamp);
}
/**
* Return a hash for control download access.
*
* @return string Hash ready to insert in database
*/
public function getHash()
{
// TODO check if this hash not already in database
return sha1(microtime() . $this->id);
}
/**
* Return a sha1 filename.
*
* @return string Sha1 unique filename
*/
public static function getNewFilename()
{
do {
$filename = sha1(microtime());
} while (file_exists(_PS_DOWNLOAD_DIR_ . $filename));
return $filename;
}
/**
* This method is allow to know if a feature is used or active.
*
* @return bool
*
* @since 1.5.0.1
*/
public static function isFeatureActive()
{
return Configuration::get('PS_VIRTUAL_PROD_FEATURE_ACTIVE');
}
}

View File

@@ -0,0 +1,102 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
use PrestaShop\PrestaShop\Adapter\Image\ImageRetriever;
use PrestaShop\PrestaShop\Adapter\Presenter\Product\ProductListingPresenter;
use PrestaShop\PrestaShop\Adapter\Presenter\Product\ProductPresenter;
use PrestaShop\PrestaShop\Adapter\Product\PriceFormatter;
use PrestaShop\PrestaShop\Adapter\Product\ProductColorsRetriever;
use PrestaShop\PrestaShop\Core\Product\ProductPresentationSettings;
/**
* Class ProductPresenterFactoryCore.
*/
class ProductPresenterFactoryCore
{
private $context;
private $taxConfiguration;
/**
* ProductPresenterFactoryCore constructor.
*
* @param Context $context
* @param \TaxConfiguration|null $taxConfiguration
*/
public function __construct(Context $context, TaxConfiguration $taxConfiguration = null)
{
$this->context = $context;
$this->taxConfiguration = (null === $taxConfiguration) ? new TaxConfiguration() : $taxConfiguration;
}
/**
* Get presentation settings.
*
* @return ProductPresentationSettings
*/
public function getPresentationSettings()
{
$settings = new ProductPresentationSettings();
$settings->catalog_mode = Configuration::isCatalogMode();
$settings->catalog_mode_with_prices = (int) Configuration::get('PS_CATALOG_MODE_WITH_PRICES');
$settings->include_taxes = $this->taxConfiguration->includeTaxes();
$settings->allow_add_variant_to_cart_from_listing = (int) Configuration::get('PS_ATTRIBUTE_CATEGORY_DISPLAY');
$settings->stock_management_enabled = Configuration::get('PS_STOCK_MANAGEMENT');
$settings->showPrices = Configuration::showPrices();
$settings->lastRemainingItems = Configuration::get('PS_LAST_QTIES');
return $settings;
}
/**
* Get presenter.
*
* @return ProductListingPresenter|ProductPresenter
*/
public function getPresenter()
{
$imageRetriever = new ImageRetriever(
$this->context->link
);
if (is_a($this->context->controller, 'ProductListingFrontControllerCore')) {
return new ProductListingPresenter(
$imageRetriever,
$this->context->link,
new PriceFormatter(),
new ProductColorsRetriever(),
$this->context->getTranslator()
);
}
return new ProductPresenter(
$imageRetriever,
$this->context->link,
new PriceFormatter(),
new ProductColorsRetriever(),
$this->context->getTranslator()
);
}
}

291
classes/ProductSale.php Normal file
View File

@@ -0,0 +1,291 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ProductSaleCore.
*/
class ProductSaleCore
{
/**
* Fill the `product_sale` SQL table with data from `order_detail`.
*
* @return bool True on success
*/
public static function fillProductSales()
{
$sql = 'REPLACE INTO ' . _DB_PREFIX_ . 'product_sale
(`id_product`, `quantity`, `sale_nbr`, `date_upd`)
SELECT od.product_id, SUM(od.product_quantity), COUNT(od.product_id), NOW()
FROM ' . _DB_PREFIX_ . 'order_detail od GROUP BY od.product_id';
return Db::getInstance()->execute($sql);
}
/**
* Get number of actives products sold.
*
* @return int number of actives products listed in product_sales
*/
public static function getNbSales()
{
$sql = 'SELECT COUNT(ps.`id_product`) AS nb
FROM `' . _DB_PREFIX_ . 'product_sale` ps
LEFT JOIN `' . _DB_PREFIX_ . 'product` p ON p.`id_product` = ps.`id_product`
' . Shop::addSqlAssociation('product', 'p', false) . '
WHERE product_shop.`active` = 1';
return (int) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql);
}
/**
* Get required informations on best sales products.
*
* @param int $idLang Language id
* @param int $pageNumber Start from (optional)
* @param int $nbProducts Number of products to return (optional)
*
* @return array|bool from Product::getProductProperties
* `false` if failure
*/
public static function getBestSales($idLang, $pageNumber = 0, $nbProducts = 10, $orderBy = null, $orderWay = null)
{
$context = Context::getContext();
if ($pageNumber < 1) {
$pageNumber = 1;
}
if ($nbProducts < 1) {
$nbProducts = 10;
}
$finalOrderBy = $orderBy;
$orderTable = '';
$invalidOrderBy = !Validate::isOrderBy($orderBy);
if ($invalidOrderBy || null === $orderBy) {
$orderBy = 'quantity';
$orderTable = 'ps';
}
if ($orderBy == 'date_add' || $orderBy == 'date_upd') {
$orderTable = 'product_shop';
}
$invalidOrderWay = !Validate::isOrderWay($orderWay);
if ($invalidOrderWay || null === $orderWay || $orderBy == 'sales') {
$orderWay = 'DESC';
}
$interval = Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20;
// no group by needed : there's only one attribute with default_on=1 for a given id_product + shop
// same for image with cover=1
$sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity,
' . (Combination::isFeatureActive() ? 'product_attribute_shop.minimal_quantity AS product_attribute_minimal_quantity,IFNULL(product_attribute_shop.id_product_attribute,0) id_product_attribute,' : '') . '
pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`meta_description`,
pl.`meta_keywords`, pl.`meta_title`, pl.`name`, pl.`available_now`, pl.`available_later`,
m.`name` AS manufacturer_name, p.`id_manufacturer` as id_manufacturer,
image_shop.`id_image` id_image, il.`legend`,
ps.`quantity` AS sales, t.`rate`, pl.`meta_keywords`, pl.`meta_title`, pl.`meta_description`,
DATEDIFF(p.`date_add`, DATE_SUB("' . date('Y-m-d') . ' 00:00:00",
INTERVAL ' . (int) $interval . ' DAY)) > 0 AS new'
. ' FROM `' . _DB_PREFIX_ . 'product_sale` ps
LEFT JOIN `' . _DB_PREFIX_ . 'product` p ON ps.`id_product` = p.`id_product`
' . Shop::addSqlAssociation('product', 'p', false);
if (Combination::isFeatureActive()) {
$sql .= ' LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_shop` product_attribute_shop
ON (p.`id_product` = product_attribute_shop.`id_product` AND product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop=' . (int) $context->shop->id . ')';
}
$sql .= ' LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl
ON p.`id_product` = pl.`id_product`
AND pl.`id_lang` = ' . (int) $idLang . Shop::addSqlRestrictionOnLang('pl') . '
LEFT JOIN `' . _DB_PREFIX_ . 'image_shop` image_shop
ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop=' . (int) $context->shop->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $idLang . ')
LEFT JOIN `' . _DB_PREFIX_ . 'manufacturer` m ON (m.`id_manufacturer` = p.`id_manufacturer`)
LEFT JOIN `' . _DB_PREFIX_ . 'tax_rule` tr ON (product_shop.`id_tax_rules_group` = tr.`id_tax_rules_group`)
AND tr.`id_country` = ' . (int) $context->country->id . '
AND tr.`id_state` = 0
LEFT JOIN `' . _DB_PREFIX_ . 'tax` t ON (t.`id_tax` = tr.`id_tax`)
' . Product::sqlStock('p', 0);
$sql .= '
WHERE product_shop.`active` = 1
AND product_shop.`visibility` != \'none\'';
if (Group::isFeatureActive()) {
$groups = FrontController::getCurrentCustomerGroups();
$sql .= ' AND EXISTS(SELECT 1 FROM `' . _DB_PREFIX_ . 'category_product` cp
JOIN `' . _DB_PREFIX_ . 'category_group` cg ON (cp.id_category = cg.id_category AND cg.`id_group` ' . (count($groups) ? 'IN (' . implode(',', $groups) . ')' : '=' . (int) Configuration::get('PS_UNIDENTIFIED_GROUP')) . ')
WHERE cp.`id_product` = p.`id_product`)';
}
if ($finalOrderBy != 'price') {
$sql .= '
ORDER BY ' . (!empty($orderTable) ? '`' . pSQL($orderTable) . '`.' : '') . '`' . pSQL($orderBy) . '` ' . pSQL($orderWay) . '
LIMIT ' . (int) (($pageNumber - 1) * $nbProducts) . ', ' . (int) $nbProducts;
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
if ($finalOrderBy == 'price') {
Tools::orderbyPrice($result, $orderWay);
$result = array_slice($result, (int) (($pageNumber - 1) * $nbProducts), (int) $nbProducts);
}
if (!$result) {
return false;
}
return Product::getProductsProperties($idLang, $result);
}
/**
* Get required informations on best sales products.
*
* @param int $idLang Language id
* @param int $pageNumber Start from (optional)
* @param int $nbProducts Number of products to return (optional)
*
* @return array keys : id_product, link_rewrite, name, id_image, legend, sales, ean13, upc, link
*/
public static function getBestSalesLight($idLang, $pageNumber = 0, $nbProducts = 10, Context $context = null)
{
if (!$context) {
$context = Context::getContext();
}
if ($pageNumber < 0) {
$pageNumber = 0;
}
if ($nbProducts < 1) {
$nbProducts = 10;
}
// no group by needed : there's only one attribute with default_on=1 for a given id_product + shop
// same for image with cover=1
$sql = '
SELECT
p.id_product, IFNULL(product_attribute_shop.id_product_attribute,0) id_product_attribute, pl.`link_rewrite`, pl.`name`, pl.`description_short`, product_shop.`id_category_default`,
image_shop.`id_image` id_image, il.`legend`,
ps.`quantity` AS sales, p.`ean13`, p.`upc`, cl.`link_rewrite` AS category, p.show_price, p.available_for_order, IFNULL(stock.quantity, 0) as quantity, p.customizable,
IFNULL(pa.minimal_quantity, p.minimal_quantity) as minimal_quantity, stock.out_of_stock,
product_shop.`date_add` > "' . date('Y-m-d', strtotime('-' . (Configuration::get('PS_NB_DAYS_NEW_PRODUCT') ? (int) Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20) . ' DAY')) . '" as new,
product_shop.`on_sale`, product_attribute_shop.minimal_quantity AS product_attribute_minimal_quantity
FROM `' . _DB_PREFIX_ . 'product_sale` ps
LEFT JOIN `' . _DB_PREFIX_ . 'product` p ON ps.`id_product` = p.`id_product`
' . Shop::addSqlAssociation('product', 'p') . '
LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_shop` product_attribute_shop
ON (p.`id_product` = product_attribute_shop.`id_product` AND product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop=' . (int) $context->shop->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute` pa ON (product_attribute_shop.id_product_attribute=pa.id_product_attribute)
LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl
ON p.`id_product` = pl.`id_product`
AND pl.`id_lang` = ' . (int) $idLang . Shop::addSqlRestrictionOnLang('pl') . '
LEFT JOIN `' . _DB_PREFIX_ . 'image_shop` image_shop
ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop=' . (int) $context->shop->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $idLang . ')
LEFT JOIN `' . _DB_PREFIX_ . 'category_lang` cl
ON cl.`id_category` = product_shop.`id_category_default`
AND cl.`id_lang` = ' . (int) $idLang . Shop::addSqlRestrictionOnLang('cl') . Product::sqlStock('p', 0);
$sql .= '
WHERE product_shop.`active` = 1
AND p.`visibility` != \'none\'';
if (Group::isFeatureActive()) {
$groups = FrontController::getCurrentCustomerGroups();
$sql .= ' AND EXISTS(SELECT 1 FROM `' . _DB_PREFIX_ . 'category_product` cp
JOIN `' . _DB_PREFIX_ . 'category_group` cg ON (cp.id_category = cg.id_category AND cg.`id_group` ' . (count($groups) ? 'IN (' . implode(',', $groups) . ')' : '=' . (int) Configuration::get('PS_UNIDENTIFIED_GROUP')) . ')
WHERE cp.`id_product` = p.`id_product`)';
}
$sql .= '
ORDER BY ps.quantity DESC
LIMIT ' . (int) ($pageNumber * $nbProducts) . ', ' . (int) $nbProducts;
if (!$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql)) {
return false;
}
return Product::getProductsProperties($idLang, $result);
}
/**
* Add Product sale.
*
* @param int $productId Product ID
* @param int $qty Quantity
*
* @return bool Indicates whether the sale was successfully added
*/
public static function addProductSale($productId, $qty = 1)
{
return Db::getInstance()->execute('
INSERT INTO ' . _DB_PREFIX_ . 'product_sale
(`id_product`, `quantity`, `sale_nbr`, `date_upd`)
VALUES (' . (int) $productId . ', ' . (int) $qty . ', 1, NOW())
ON DUPLICATE KEY UPDATE `quantity` = `quantity` + ' . (int) $qty . ', `sale_nbr` = `sale_nbr` + 1, `date_upd` = NOW()');
}
/**
* Get number of sales.
*
* @param int $idProduct Product ID
*
* @return int Number of sales for the given Product
*/
public static function getNbrSales($idProduct)
{
$result = Db::getInstance()->getRow('SELECT `sale_nbr` FROM ' . _DB_PREFIX_ . 'product_sale WHERE `id_product` = ' . (int) $idProduct);
if (!$result || empty($result) || !array_key_exists('sale_nbr', $result)) {
return -1;
}
return (int) $result['sale_nbr'];
}
/**
* Remove a Product sale.
*
* @param int $idProduct Product ID
* @param int $qty Quantity
*
* @return bool Indicates whether the product sale has been successfully removed
*/
public static function removeProductSale($idProduct, $qty = 1)
{
$totalSales = ProductSale::getNbrSales($idProduct);
if ($totalSales > 1) {
return Db::getInstance()->execute(
'
UPDATE ' . _DB_PREFIX_ . 'product_sale
SET `quantity` = CAST(`quantity` AS SIGNED) - ' . (int) $qty . ', `sale_nbr` = CAST(`sale_nbr` AS SIGNED) - 1, `date_upd` = NOW()
WHERE `id_product` = ' . (int) $idProduct
);
} elseif ($totalSales == 1) {
return Db::getInstance()->delete('product_sale', 'id_product = ' . (int) $idProduct);
}
return true;
}
}

276
classes/ProductSupplier.php Normal file
View File

@@ -0,0 +1,276 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* ProductSupplierCore class.
*
* @since 1.5.0
*/
class ProductSupplierCore extends ObjectModel
{
/**
* @var int product ID
* */
public $id_product;
/**
* @var int product attribute ID
* */
public $id_product_attribute;
/**
* @var int the supplier ID
* */
public $id_supplier;
/**
* @var string The supplier reference of the product
* */
public $product_supplier_reference;
/**
* @var int the currency ID for unit price tax excluded
* */
public $id_currency;
/**
* @var string The unit price tax excluded of the product
* */
public $product_supplier_price_te;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'product_supplier',
'primary' => 'id_product_supplier',
'fields' => array(
'product_supplier_reference' => array('type' => self::TYPE_STRING, 'validate' => 'isReference', 'size' => 64),
'id_product' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_product_attribute' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_supplier' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'product_supplier_price_te' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'),
'id_currency' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
),
);
/**
* @see ObjectModel::$webserviceParameters
*/
protected $webserviceParameters = array(
'objectsNodeName' => 'product_suppliers',
'objectNodeName' => 'product_supplier',
'fields' => array(
'id_product' => array('xlink_resource' => 'products'),
'id_product_attribute' => array('xlink_resource' => 'combinations'),
'id_supplier' => array('xlink_resource' => 'suppliers'),
'id_currency' => array('xlink_resource' => 'currencies'),
),
);
/**
* @see ObjectModel::delete()
*/
public function delete()
{
$res = parent::delete();
if ($res && $this->id_product_attribute == 0) {
$items = ProductSupplier::getSupplierCollection($this->id_product, false);
foreach ($items as $item) {
/** @var ProductSupplier $item */
if ($item->id_product_attribute > 0) {
$item->delete();
}
}
}
return $res;
}
/**
* For a given product and supplier, gets the product supplier reference.
*
* @param int $idProduct Product ID
* @param int $idProductAttribute Product Attribute ID
* @param int $idSupplier Supplier ID
*
* @return string Product Supplier reference
*/
public static function getProductSupplierReference($idProduct, $idProductAttribute, $idSupplier)
{
// build query
$query = new DbQuery();
$query->select('ps.product_supplier_reference');
$query->from('product_supplier', 'ps');
$query->where(
'ps.id_product = ' . (int) $idProduct . '
AND ps.id_product_attribute = ' . (int) $idProductAttribute . '
AND ps.id_supplier = ' . (int) $idSupplier
);
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query);
}
/**
* For a given product and supplier, gets the product supplier unit price.
*
* @param int $idProduct Product ID
* @param int $idProductAttribute Product Attribute ID
* @param int $idSupplier Supplier ID
* @param bool $withCurrency Optional With currency
*
* @return array
*/
public static function getProductSupplierPrice($idProduct, $idProductAttribute, $idSupplier, $withCurrency = false)
{
// build query
$query = new DbQuery();
$query->select('ps.product_supplier_price_te');
if ($withCurrency) {
$query->select('ps.id_currency');
}
$query->from('product_supplier', 'ps');
$query->where(
'ps.id_product = ' . (int) $idProduct . '
AND ps.id_product_attribute = ' . (int) $idProductAttribute . '
AND ps.id_supplier = ' . (int) $idSupplier
);
if (!$withCurrency) {
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query);
}
$res = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($query);
if (isset($res[0])) {
return $res[0];
}
return $res;
}
/**
* For a given product and supplier, gets corresponding ProductSupplier ID.
*
* @param int $idProduct
* @param int $idProductAttribute
* @param int $idSupplier
*
* @return array
*/
public static function getIdByProductAndSupplier($idProduct, $idProductAttribute, $idSupplier)
{
// build query
$query = new DbQuery();
$query->select('ps.id_product_supplier');
$query->from('product_supplier', 'ps');
$query->where(
'ps.id_product = ' . (int) $idProduct . '
AND ps.id_product_attribute = ' . (int) $idProductAttribute . '
AND ps.id_supplier = ' . (int) $idSupplier
);
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query);
}
/**
* For a given product, retrieves its suppliers.
*
* @param int $idProduct
* @param int $groupBySupplier
*
* @return PrestaShopCollection Collection of ProductSupplier
*/
public static function getSupplierCollection($idProduct, $groupBySupplier = true)
{
$suppliers = new PrestaShopCollection('ProductSupplier');
$suppliers->where('id_product', '=', (int) $idProduct);
if ($groupBySupplier) {
$suppliers->groupBy('id_supplier');
}
return $suppliers;
}
/**
* For a given Supplier, Product, returns the purchased price.
*
* @param int $idProduct
* @param int $idProductAttribute Optional
* @param bool $convertedPrice Optional
*
* @return array keys: price_te, id_currency
*/
public static function getProductPrice($idSupplier, $idProduct, $idProductAttribute = 0, $convertedPrice = false)
{
if (null === $idSupplier || null === $idProduct) {
return;
}
$query = new DbQuery();
$query->select('product_supplier_price_te as price_te, id_currency');
$query->from('product_supplier');
$query->where('id_product = ' . (int) $idProduct . ' AND id_product_attribute = ' . (int) $idProductAttribute);
$query->where('id_supplier = ' . (int) $idSupplier);
$row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($query);
if ($convertedPrice) {
return Tools::convertPrice($row['price_te'], $row['id_currency']);
}
return $row['price_te'];
}
/**
* For a given product and supplier, gets the product supplier datas.
*
* @param int $idProduct Product ID
* @param int $idProductAttribute Product Attribute ID
* @param int $idSupplier Supplier ID
*
* @return array
*/
public static function getProductSupplierData($idProduct, $idProductAttribute, $idSupplier)
{
// build query
$query = new DbQuery();
$query->select('ps.product_supplier_reference, ps.product_supplier_price_te as price, ps.id_currency');
$query->from('product_supplier', 'ps');
$query->where(
'ps.id_product = ' . (int) $idProduct . '
AND ps.id_product_attribute = ' . (int) $idProductAttribute . '
AND ps.id_supplier = ' . (int) $idSupplier
);
$res = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($query);
if (isset($res[0])) {
return $res[0];
}
return $res;
}
}

241
classes/Profile.php Normal file
View File

@@ -0,0 +1,241 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ProfileCore.
*/
class ProfileCore extends ObjectModel
{
const ALLOWED_PROFILE_TYPE_CHECK = [
'id_tab',
'class_name',
];
/** @var string Name */
public $name;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'profile',
'primary' => 'id_profile',
'multilang' => true,
'fields' => array(
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 32),
),
);
protected static $_cache_accesses = array();
/**
* Get all available profiles.
*
* @return array Profiles
*/
public static function getProfiles($idLang)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT p.`id_profile`, `name`
FROM `' . _DB_PREFIX_ . 'profile` p
LEFT JOIN `' . _DB_PREFIX_ . 'profile_lang` pl ON (p.`id_profile` = pl.`id_profile` AND `id_lang` = ' . (int) $idLang . ')
ORDER BY `id_profile` ASC');
}
/**
* Get the current profile name.
*
* @param int $idProfile Profile ID
* @param null $idLang Language ID
*
* @return string Profile
*/
public static function getProfile($idProfile, $idLang = null)
{
if (!$idLang) {
$idLang = Configuration::get('PS_LANG_DEFAULT');
}
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(
'
SELECT `name`
FROM `' . _DB_PREFIX_ . 'profile` p
LEFT JOIN `' . _DB_PREFIX_ . 'profile_lang` pl ON (p.`id_profile` = pl.`id_profile`)
WHERE p.`id_profile` = ' . (int) $idProfile . '
AND pl.`id_lang` = ' . (int) $idLang
);
}
public function add($autodate = true, $null_values = false)
{
return parent::add($autodate, true);
}
public function delete()
{
if (parent::delete()) {
return
Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'access` WHERE `id_profile` = ' . (int) $this->id)
&& Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'module_access` WHERE `id_profile` = ' . (int) $this->id);
}
return false;
}
/**
* Get access profile.
*
* @param int $idProfile Profile ID
* @param int $idTab Tab ID
*
* @return bool
*/
public static function getProfileAccess($idProfile, $idTab)
{
// getProfileAccesses is cached so there is no performance leak
$accesses = Profile::getProfileAccesses($idProfile);
return isset($accesses[$idTab]) ? $accesses[$idTab] : false;
}
/**
* Get access profiles.
*
* @param int $idProfile Profile ID
* @param string $type Type
*
* @return bool
*/
public static function getProfileAccesses($idProfile, $type = 'id_tab')
{
if (!in_array($type, self::ALLOWED_PROFILE_TYPE_CHECK)) {
return false;
}
if (!isset(self::$_cache_accesses[$idProfile])) {
self::$_cache_accesses[$idProfile] = array();
}
if (!isset(self::$_cache_accesses[$idProfile][$type])) {
self::$_cache_accesses[$idProfile][$type] = array();
// Super admin profile has full auth
if ($idProfile == _PS_ADMIN_PROFILE_) {
$defaultPermission = [
'id_profile' => _PS_ADMIN_PROFILE_,
'view' => '1',
'add' => '1',
'edit' => '1',
'delete' => '1',
];
$roles = [];
} else {
$defaultPermission = [
'id_profile' => $idProfile,
'view' => '0',
'add' => '0',
'edit' => '0',
'delete' => '0',
];
$roles = self::generateAccessesArrayFromPermissions(
Db::getInstance()->executeS('
SELECT `slug`,
`slug` LIKE "%CREATE" as "add",
`slug` LIKE "%READ" as "view",
`slug` LIKE "%UPDATE" as "edit",
`slug` LIKE "%DELETE" as "delete"
FROM `' . _DB_PREFIX_ . 'authorization_role` a
LEFT JOIN `' . _DB_PREFIX_ . 'access` j ON j.id_authorization_role = a.id_authorization_role
WHERE j.`id_profile` = ' . (int) $idProfile)
);
}
self::fillCacheAccesses(
$idProfile,
$defaultPermission,
$roles
);
}
return self::$_cache_accesses[$idProfile][$type];
}
public static function resetCacheAccesses()
{
self::$_cache_accesses = array();
}
/**
* @param int $idProfile Profile ID
* @param array $defaultData Cached data
* @param array $accesses Data loaded from the database
*/
private static function fillCacheAccesses($idProfile, $defaultData = [], $accesses = [])
{
foreach (Tab::getTabs(Context::getContext()->language->id) as $tab) {
$accessData = [];
if (isset($accesses[strtoupper($tab['class_name'])])) {
$accessData = $accesses[strtoupper($tab['class_name'])];
}
foreach (self::ALLOWED_PROFILE_TYPE_CHECK as $type) {
self::$_cache_accesses[$idProfile][$type][$tab[$type]] = array_merge(
array(
'id_tab' => $tab['id_tab'],
'class_name' => $tab['class_name'],
),
$defaultData,
$accessData
);
}
}
}
/**
* Creates the array of accesses [role => add / view / edit / delete] from a given list of roles
*
* @param array $rolesGiven
*
* @return array
*/
private static function generateAccessesArrayFromPermissions($rolesGiven)
{
// Modify array to merge the class names together.
$accessPerTab = [];
foreach ($rolesGiven as $role) {
preg_match(
'/ROLE_MOD_[A-Z]+_(?P<classname>[A-Z][A-Z0-9]*)_[A-Z]+/',
$role['slug'],
$matches
);
if (empty($matches['classname'])) {
continue;
}
$accessPerTab[$matches['classname']][array_search('1', $role)] = '1';
}
return $accessPerTab;
}
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class QqUploadedFileFormCore
{
/**
* Save the file to the specified path.
*
* @return bool TRUE on success
*/
public function save()
{
$product = new Product($_GET['id_product']);
if (!Validate::isLoadedObject($product)) {
return array('error' => Context::getContext()->getTranslator()->trans('Cannot add image because product creation failed.', array(), 'Admin.Catalog.Notification'));
} else {
$image = new Image();
$image->id_product = (int) $product->id;
$image->position = Image::getHighestPosition($product->id) + 1;
$legends = Tools::getValue('legend');
if (is_array($legends)) {
foreach ($legends as $key => $legend) {
if (Validate::isGenericName($legend)) {
$image->legend[(int) $key] = $legend;
} else {
return array('error' => Context::getContext()->getTranslator()->trans('Error on image caption: "%1s" is not a valid caption.', array(Tools::safeOutput($legend)), 'Admin.Catalog.Notification'));
}
}
}
if (!Image::getCover($image->id_product)) {
$image->cover = 1;
} else {
$image->cover = 0;
}
if (($validate = $image->validateFieldsLang(false, true)) !== true) {
return array('error' => $validate);
}
if (!$image->add()) {
return array('error' => Context::getContext()->getTranslator()->trans('Error while creating additional image', array(), 'Admin.Catalog.Notification'));
} else {
return $this->copyImage($product->id, $image->id);
}
}
}
public function copyImage($id_product, $id_image, $method = 'auto')
{
$image = new Image($id_image);
if (!$new_path = $image->getPathForCreation()) {
return array('error' => Context::getContext()->getTranslator()->trans('An error occurred while attempting to create a new folder.', array(), 'Admin.Notifications.Error'));
}
if (!($tmpName = tempnam(_PS_TMP_IMG_DIR_, 'PS')) || !move_uploaded_file($_FILES['qqfile']['tmp_name'], $tmpName)) {
return array('error' => Context::getContext()->getTranslator()->trans('An error occurred while uploading the image.', array(), 'Admin.Notifications.Error'));
} elseif (!ImageManager::resize($tmpName, $new_path . '.' . $image->image_format)) {
return array('error' => Context::getContext()->getTranslator()->trans('An error occurred while copying the image.', array(), 'Admin.Notifications.Error'));
} elseif ($method == 'auto') {
$imagesTypes = ImageType::getImagesTypes('products');
foreach ($imagesTypes as $imageType) {
if (!ImageManager::resize($tmpName, $new_path . '-' . stripslashes($imageType['name']) . '.' . $image->image_format, $imageType['width'], $imageType['height'], $image->image_format)) {
return array('error' => Context::getContext()->getTranslator()->trans('An error occurred while copying this image: %s', array(stripslashes($imageType['name'])), 'Admin.Notifications.Error'));
}
}
}
unlink($tmpName);
Hook::exec('actionWatermark', array('id_image' => $id_image, 'id_product' => $id_product));
if (!$image->update()) {
return array('error' => Context::getContext()->getTranslator()->trans('Error while updating the status.', array(), 'Admin.Notifications.Error'));
}
$img = array('id_image' => $image->id, 'position' => $image->position, 'cover' => $image->cover, 'name' => $this->getName(), 'legend' => $image->legend);
return array('success' => $img);
}
public function getName()
{
return $_FILES['qqfile']['name'];
}
public function getSize()
{
return $_FILES['qqfile']['size'];
}
}

View File

@@ -0,0 +1,135 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Handle file uploads via XMLHttpRequest.
*/
class QqUploadedFileXhrCore
{
/**
* Save the file to the specified path.
*
* @return bool TRUE on success
*/
public function upload($path)
{
$input = fopen('php://input', 'rb');
$target = fopen($path, 'wb');
$realSize = stream_copy_to_stream($input, $target);
if ($realSize != $this->getSize()) {
return false;
}
fclose($input);
fclose($target);
return true;
}
public function save()
{
$product = new Product($_GET['id_product']);
if (!Validate::isLoadedObject($product)) {
return array('error' => Context::getContext()->getTranslator()->trans('Cannot add image because product creation failed.', array(), 'Admin.Catalog.Notification'));
} else {
$image = new Image();
$image->id_product = (int) ($product->id);
$image->position = Image::getHighestPosition($product->id) + 1;
$legends = Tools::getValue('legend');
if (is_array($legends)) {
foreach ($legends as $key => $legend) {
if (Validate::isGenericName($legend)) {
$image->legend[(int) $key] = $legend;
} else {
return array('error' => Context::getContext()->getTranslator()->trans('Error on image caption: "%1s" is not a valid caption.', array(Tools::safeOutput($legend)), 'Admin.Notifications.Error'));
}
}
}
if (!Image::getCover($image->id_product)) {
$image->cover = 1;
} else {
$image->cover = 0;
}
if (($validate = $image->validateFieldsLang(false, true)) !== true) {
return array('error' => $validate);
}
if (!$image->add()) {
return array('error' => Context::getContext()->getTranslator()->trans('Error while creating additional image', array(), 'Admin.Catalog.Notification'));
} else {
return $this->copyImage($product->id, $image->id);
}
}
}
public function copyImage($id_product, $id_image, $method = 'auto')
{
$image = new Image($id_image);
if (!$new_path = $image->getPathForCreation()) {
return array('error' => Context::getContext()->getTranslator()->trans('An error occurred while attempting to create a new folder.', array(), 'Admin.Notifications.Error'));
}
if (!($tmpName = tempnam(_PS_TMP_IMG_DIR_, 'PS')) || !$this->upload($tmpName)) {
return array('error' => Context::getContext()->getTranslator()->trans('An error occurred while uploading the image.', array(), 'Admin.Notifications.Error'));
} elseif (!ImageManager::resize($tmpName, $new_path . '.' . $image->image_format)) {
return array('error' => Context::getContext()->getTranslator()->trans('An error occurred while uploading the image.', array(), 'Admin.Notifications.Error'));
} elseif ($method == 'auto') {
$imagesTypes = ImageType::getImagesTypes('products');
foreach ($imagesTypes as $imageType) {
if (!ImageManager::resize($tmpName, $new_path . '-' . stripslashes($imageType['name']) . '.' . $image->image_format, $imageType['width'], $imageType['height'], $image->image_format)) {
return array('error' => Context::getContext()->getTranslator()->trans('An error occurred while copying this image: %s', array(stripslashes($imageType['name'])), 'Admin.Notifications.Error'));
}
}
}
unlink($tmpName);
Hook::exec('actionWatermark', array('id_image' => $id_image, 'id_product' => $id_product));
if (!$image->update()) {
return array('error' => Context::getContext()->getTranslator()->trans('Error while updating the status.', array(), 'Admin.Notifications.Error'));
}
$img = array('id_image' => $image->id, 'position' => $image->position, 'cover' => $image->cover, 'name' => $this->getName(), 'legend' => $image->legend);
return array('success' => $img);
}
public function getName()
{
return $_GET['qqfile'];
}
public function getSize()
{
if (isset($_SERVER['CONTENT_LENGTH']) || isset($_SERVER['HTTP_CONTENT_LENGTH'])) {
if (isset($_SERVER['HTTP_CONTENT_LENGTH'])) {
return (int) $_SERVER['HTTP_CONTENT_LENGTH'];
} else {
return (int) $_SERVER['CONTENT_LENGTH'];
}
}
return false;
}
}

138
classes/QuickAccess.php Normal file
View File

@@ -0,0 +1,138 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class QuickAccessCore.
*/
class QuickAccessCore extends ObjectModel
{
/** @var string Name */
public $name;
/** @var string Link */
public $link;
/** @var bool New windows or not */
public $new_window;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'quick_access',
'primary' => 'id_quick_access',
'multilang' => true,
'fields' => array(
'link' => array('type' => self::TYPE_STRING, 'validate' => 'isUrl', 'required' => true, 'size' => 255),
'new_window' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true),
/* Lang fields */
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isCleanHtml', 'required' => true, 'size' => 32),
),
);
/**
* Get all available quick_accesses.
*
* @return array QuickAccesses
*/
public static function getQuickAccesses($idLang)
{
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT *
FROM `' . _DB_PREFIX_ . 'quick_access` qa
LEFT JOIN `' . _DB_PREFIX_ . 'quick_access_lang` qal ON (qa.`id_quick_access` = qal.`id_quick_access` AND qal.`id_lang` = ' . (int) $idLang . ')
ORDER BY `name` ASC');
}
/**
* Get all available quick_accesses with token.
*
* @return array QuickAccesses
*/
public static function getQuickAccessesWithToken($idLang, $idEmployee)
{
$quickAccess = self::getQuickAccesses($idLang);
if (empty($quickAccess)) {
return false;
}
foreach ($quickAccess as $index => $quick) {
// first, clean url to have a real quickLink
$quick['link'] = Context::getContext()->link->getQuickLink($quick['link']);
$tokenString = $idEmployee;
if ('../' === $quick['link'] && Shop::getContext() == Shop::CONTEXT_SHOP) {
$url = Context::getContext()->shop->getBaseURL();
if (!$url) {
unset($quickAccess[$index]);
continue;
}
$quickAccess[$index]['link'] = $url;
} else {
preg_match('/controller=(.+)(&.+)?$/', $quick['link'], $admin_tab);
if (isset($admin_tab[1])) {
if (strpos($admin_tab[1], '&')) {
$admin_tab[1] = substr($admin_tab[1], 0, strpos($admin_tab[1], '&'));
}
$quick_access[$index]['target'] = $admin_tab[1];
$tokenString = $admin_tab[1] . (int) Tab::getIdFromClassName($admin_tab[1]) . $idEmployee;
}
$quickAccess[$index]['link'] = Context::getContext()->link->getBaseLink() . basename(_PS_ADMIN_DIR_) . '/' . $quick['link'];
}
if (false === strpos($quickAccess[$index]['link'], 'token')) {
$separator = strpos($quickAccess[$index]['link'], '?') ? '&' : '?';
$quickAccess[$index]['link'] .= $separator . 'token=' . Tools::getAdminToken($tokenString);
}
}
return $quickAccess;
}
/**
* Toggle new window.
*
* @return bool
*
* @throws PrestaShopException
*/
public function toggleNewWindow()
{
if (!array_key_exists('new_window', $this)) {
throw new PrestaShopException('property "new_window" is missing in object ' . get_class($this));
}
$this->setFieldsToUpdate(array('new_window' => true));
$this->new_window = !(int) $this->new_window;
return $this->update(false);
}
}

392
classes/Referrer.php Normal file
View File

@@ -0,0 +1,392 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class ReferrerCore.
*/
class ReferrerCore extends ObjectModel
{
public $id_shop;
public $name;
public $passwd;
public $http_referer_regexp;
public $http_referer_like;
public $request_uri_regexp;
public $request_uri_like;
public $http_referer_regexp_not;
public $http_referer_like_not;
public $request_uri_regexp_not;
public $request_uri_like_not;
public $base_fee;
public $percent_fee;
public $click_fee;
public $date_add;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'referrer',
'primary' => 'id_referrer',
'fields' => array(
'name' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 64),
'passwd' => array('type' => self::TYPE_STRING, 'validate' => 'isPasswd', 'size' => 255),
'http_referer_regexp' => array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'size' => 64),
'request_uri_regexp' => array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'size' => 64),
'http_referer_like' => array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'size' => 64),
'request_uri_like' => array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'size' => 64),
'http_referer_regexp_not' => array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml'),
'request_uri_regexp_not' => array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml'),
'http_referer_like_not' => array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml'),
'request_uri_like_not' => array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml'),
'base_fee' => array('type' => self::TYPE_FLOAT, 'validate' => 'isFloat'),
'percent_fee' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPercentage'),
'click_fee' => array('type' => self::TYPE_FLOAT, 'validate' => 'isFloat'),
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
),
);
protected static $_join = '(r.http_referer_like IS NULL OR r.http_referer_like = \'\' OR cs.http_referer LIKE r.http_referer_like)
AND (r.request_uri_like IS NULL OR r.request_uri_like = \'\' OR cs.request_uri LIKE r.request_uri_like)
AND (r.http_referer_like_not IS NULL OR r.http_referer_like_not = \'\' OR cs.http_referer NOT LIKE r.http_referer_like_not)
AND (r.request_uri_like_not IS NULL OR r.request_uri_like_not = \'\' OR cs.request_uri NOT LIKE r.request_uri_like_not)
AND (r.http_referer_regexp IS NULL OR r.http_referer_regexp = \'\' OR cs.http_referer REGEXP r.http_referer_regexp)
AND (r.request_uri_regexp IS NULL OR r.request_uri_regexp = \'\' OR cs.request_uri REGEXP r.request_uri_regexp)
AND (r.http_referer_regexp_not IS NULL OR r.http_referer_regexp_not = \'\' OR cs.http_referer NOT REGEXP r.http_referer_regexp_not)
AND (r.request_uri_regexp_not IS NULL OR r.request_uri_regexp_not = \'\' OR cs.request_uri NOT REGEXP r.request_uri_regexp_not)';
public function add($autoDate = true, $nullValues = false)
{
if (!($result = parent::add($autoDate, $nullValues))) {
return false;
}
Referrer::refreshCache(array(array('id_referrer' => $this->id)));
Referrer::refreshIndex(array(array('id_referrer' => $this->id)));
return $result;
}
/**
* Cache new source.
*
* @param $idConnectionsSource
*/
public static function cacheNewSource($idConnectionsSource)
{
if (!$idConnectionsSource) {
return;
}
$sql = 'INSERT INTO ' . _DB_PREFIX_ . 'referrer_cache (id_referrer, id_connections_source) (
SELECT id_referrer, id_connections_source
FROM ' . _DB_PREFIX_ . 'referrer r
LEFT JOIN ' . _DB_PREFIX_ . 'connections_source cs ON (' . self::$_join . ')
WHERE id_connections_source = ' . (int) $idConnectionsSource . '
)';
Db::getInstance()->execute($sql);
}
/**
* Get list of referrers connections of a customer.
*
* @param int $idCustomer Customer ID
*
* @return mixed
*/
public static function getReferrers($idCustomer)
{
$sql = 'SELECT DISTINCT c.date_add, r.name, s.name AS shop_name
FROM ' . _DB_PREFIX_ . 'guest g
LEFT JOIN ' . _DB_PREFIX_ . 'connections c ON c.id_guest = g.id_guest
LEFT JOIN ' . _DB_PREFIX_ . 'connections_source cs ON c.id_connections = cs.id_connections
LEFT JOIN ' . _DB_PREFIX_ . 'referrer r ON (' . self::$_join . ')
LEFT JOIN ' . _DB_PREFIX_ . 'shop s ON s.id_shop = c.id_shop
WHERE g.id_customer = ' . (int) $idCustomer . '
AND r.name IS NOT NULL
ORDER BY c.date_add DESC';
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
}
/**
* Get some statistics on visitors connection for current referrer.
*
* @param int $idProduct
* @param int $employee
*
* @return array|bool|object|null
*/
public function getStatsVisits($idProduct, $employee)
{
$join = $where = '';
if ($idProduct) {
$join = 'LEFT JOIN `' . _DB_PREFIX_ . 'page` p ON cp.`id_page` = p.`id_page`
LEFT JOIN `' . _DB_PREFIX_ . 'page_type` pt ON pt.`id_page_type` = p.`id_page_type`';
$where = ' AND pt.`name` = \'product\'
AND p.`id_object` = ' . (int) $idProduct;
}
$sql = 'SELECT COUNT(DISTINCT cs.id_connections_source) AS visits,
COUNT(DISTINCT cs.id_connections) as visitors,
COUNT(DISTINCT c.id_guest) as uniqs,
COUNT(DISTINCT cp.time_start) as pages
FROM ' . _DB_PREFIX_ . 'referrer_cache rc
LEFT JOIN ' . _DB_PREFIX_ . 'referrer r ON rc.id_referrer = r.id_referrer
LEFT JOIN ' . _DB_PREFIX_ . 'referrer_shop rs ON r.id_referrer = rs.id_referrer
LEFT JOIN ' . _DB_PREFIX_ . 'connections_source cs ON rc.id_connections_source = cs.id_connections_source
LEFT JOIN ' . _DB_PREFIX_ . 'connections c ON cs.id_connections = c.id_connections
LEFT JOIN ' . _DB_PREFIX_ . 'connections_page cp ON cp.id_connections = c.id_connections
' . $join . '
WHERE 1' .
((isset($employee->stats_date_from, $employee->stats_date_to)) ? ' AND cs.date_add BETWEEN \'' . pSQL($employee->stats_date_from) . ' 00:00:00\' AND \'' . pSQL($employee->stats_date_to) . ' 23:59:59\'' : '') .
Shop::addSqlRestriction(false, 'rs') .
Shop::addSqlRestriction(false, 'c') .
' AND rc.id_referrer = ' . (int) $this->id .
$where;
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql);
}
/**
* Get some statistics on customers registrations for current referrer.
*
* @param int $idProduct
* @param int $employee
*/
public function getRegistrations($idProduct, $employee)
{
$join = $where = '';
if ($idProduct) {
$join = 'LEFT JOIN ' . _DB_PREFIX_ . 'connections_page cp ON cp.id_connections = c.id_connections
LEFT JOIN `' . _DB_PREFIX_ . 'page` p ON cp.`id_page` = p.`id_page`
LEFT JOIN `' . _DB_PREFIX_ . 'page_type` pt ON pt.`id_page_type` = p.`id_page_type`';
$where = ' AND pt.`name` = \'product\'
AND p.`id_object` = ' . (int) $idProduct;
}
$sql = 'SELECT COUNT(DISTINCT cu.id_customer) AS registrations
FROM ' . _DB_PREFIX_ . 'referrer_cache rc
LEFT JOIN ' . _DB_PREFIX_ . 'referrer_shop rs ON rc.id_referrer = rs.id_referrer
LEFT JOIN ' . _DB_PREFIX_ . 'connections_source cs ON rc.id_connections_source = cs.id_connections_source
LEFT JOIN ' . _DB_PREFIX_ . 'connections c ON cs.id_connections = c.id_connections
LEFT JOIN ' . _DB_PREFIX_ . 'guest g ON g.id_guest = c.id_guest
LEFT JOIN ' . _DB_PREFIX_ . 'customer cu ON cu.id_customer = g.id_customer
' . $join . '
WHERE cu.date_add BETWEEN ' . ModuleGraph::getDateBetween($employee) . '
' . Shop::addSqlRestriction(false, 'rs') . '
' . Shop::addSqlRestriction(false, 'c') . '
' . Shop::addSqlRestriction(Shop::SHARE_CUSTOMER, 'cu') . '
AND cu.date_add > cs.date_add
AND rc.id_referrer = ' . (int) $this->id
. $where;
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql);
return (int) $result['registrations'];
}
/**
* Get some statistics on orders for current referrer.
*
* @param int $idProduct
* @param int $employee
*
* @return array|bool|object|null
*/
public function getStatsSales($idProduct, $employee)
{
$join = $where = '';
if ($idProduct) {
$join = 'LEFT JOIN ' . _DB_PREFIX_ . 'order_detail od ON oo.id_order = od.id_order';
$where = ' AND od.product_id = ' . (int) $idProduct;
}
$sql = 'SELECT oo.id_order
FROM ' . _DB_PREFIX_ . 'referrer_cache rc
LEFT JOIN ' . _DB_PREFIX_ . 'referrer_shop rs ON rc.id_referrer = rs.id_referrer
INNER JOIN ' . _DB_PREFIX_ . 'connections_source cs ON rc.id_connections_source = cs.id_connections_source
INNER JOIN ' . _DB_PREFIX_ . 'connections c ON cs.id_connections = c.id_connections
INNER JOIN ' . _DB_PREFIX_ . 'guest g ON g.id_guest = c.id_guest
LEFT JOIN ' . _DB_PREFIX_ . 'orders oo ON oo.id_customer = g.id_customer
' . $join . '
WHERE oo.invoice_date BETWEEN ' . ModuleGraph::getDateBetween($employee) . '
' . Shop::addSqlRestriction(false, 'rs') . '
' . Shop::addSqlRestriction(false, 'c') . '
' . Shop::addSqlRestriction(Shop::SHARE_ORDER, 'oo') . '
AND oo.date_add > cs.date_add
AND rc.id_referrer = ' . (int) $this->id . '
AND oo.valid = 1'
. $where;
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
$implode = array();
foreach ($result as $row) {
if ((int) $row['id_order']) {
$implode[] = (int) $row['id_order'];
}
}
if ($implode) {
$sql = 'SELECT COUNT(`id_order`) AS orders, ';
if (Configuration::get('REFERER_SHIPPING')) {
$sql .= '(
SUM(' . (Configuration::get('REFERER_TAX') ? 'total_paid_tax_excl' : 'total_paid_real') . ' / conversion_rate)
- SUM(' . (Configuration::get('REFERER_TAX') ? 'total_shipping_tax_excl' : 'total_shipping_tax_incl') . ' / conversion_rate)
) AS sales ';
} else {
$sql .= 'SUM(' . (Configuration::get('REFERER_TAX') ? 'total_paid_tax_excl' : 'total_paid_real') . ' / conversion_rate) AS sales ';
}
$sql .= ' FROM `' . _DB_PREFIX_ . 'orders`
WHERE `id_order` IN (' . implode(',', $implode) . ')
' . Shop::addSqlRestriction(Shop::SHARE_ORDER) . '
AND `valid` = 1';
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql);
} else {
return array('orders' => 0, 'sales' => 0);
}
}
/**
* Refresh cache data of referrer statistics in referrer_shop table.
*
* @param array $referrers
* @param int $employee
*
* @return true
*/
public static function refreshCache($referrers = null, $employee = null)
{
if (!$referrers || !is_array($referrers)) {
$referrers = Db::getInstance()->executeS('SELECT `id_referrer` FROM ' . _DB_PREFIX_ . 'referrer');
}
foreach ($referrers as $row) {
$referrer = new Referrer($row['id_referrer']);
foreach (Shop::getShops(true, null, true) as $idShop) {
if (!$referrer->isAssociatedToShop($idShop)) {
continue;
}
$statsVisits = $referrer->getStatsVisits(null, $employee);
$registrations = $referrer->getRegistrations(null, $employee);
$statsSales = $referrer->getStatsSales(null, $employee);
Db::getInstance()->update('referrer_shop', array(
'cache_visitors' => (int) $statsVisits['uniqs'],
'cache_visits' => (int) $statsVisits['visits'],
'cache_pages' => (int) $statsVisits['pages'],
'cache_registrations' => (int) $registrations,
'cache_orders' => (int) $statsSales['orders'],
'cache_sales' => number_format($statsSales['sales'], 2, '.', ''),
'cache_reg_rate' => $statsVisits['uniqs'] ? $registrations / $statsVisits['uniqs'] : 0,
'cache_order_rate' => $statsVisits['uniqs'] ? $statsSales['orders'] / $statsVisits['uniqs'] : 0,
), 'id_referrer = ' . (int) $referrer->id . ' AND id_shop = ' . (int) $idShop);
}
}
Configuration::updateValue('PS_REFERRERS_CACHE_LIKE', ModuleGraph::getDateBetween($employee));
Configuration::updateValue('PS_REFERRERS_CACHE_DATE', date('Y-m-d H:i:s'));
return true;
}
/**
* Cache liaison between connections_source data and referrers data.
*
* @param array $referrers
*/
public static function refreshIndex($referrers = null)
{
if (!$referrers || !is_array($referrers)) {
Db::getInstance()->execute('TRUNCATE ' . _DB_PREFIX_ . 'referrer_cache');
Db::getInstance()->execute('
INSERT INTO `' . _DB_PREFIX_ . 'referrer_cache` (`id_referrer`, `id_connections_source`) (
SELECT `id_referrer`, `id_connections_source`
FROM `' . _DB_PREFIX_ . 'referrer` r
LEFT JOIN `' . _DB_PREFIX_ . 'connections_source` cs ON (' . self::$_join . ')
)');
} else {
foreach ($referrers as $row) {
Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'referrer_cache` WHERE `id_referrer` = ' . (int) $row['id_referrer']);
Db::getInstance()->execute('
INSERT INTO ' . _DB_PREFIX_ . 'referrer_cache (id_referrer, id_connections_source) (
SELECT id_referrer, id_connections_source
FROM ' . _DB_PREFIX_ . 'referrer r
LEFT JOIN ' . _DB_PREFIX_ . 'connections_source cs ON (' . self::$_join . ')
WHERE id_referrer = ' . (int) $row['id_referrer'] . '
AND id_connections_source IS NOT NULL
)');
}
}
}
/**
* Get product for ajax call.
*
* @param int $idReferrer Referrer ID
* @param int $idProduct Product ID
* @param int $idEmployee Employee ID
*/
public static function getAjaxProduct($idReferrer, $idProduct, $idEmployee = null)
{
$product = new Product($idProduct, false, Configuration::get('PS_LANG_DEFAULT'));
$currency = Currency::getCurrencyInstance(Configuration::get('PS_CURRENCY_DEFAULT'));
$referrer = new Referrer($idReferrer);
$statsVisits = $referrer->getStatsVisits($idProduct, $idEmployee);
$registrations = $referrer->getRegistrations($idProduct, $idEmployee);
$statsSales = $referrer->getStatsSales($idProduct, $idEmployee);
// If it's a product and it has no visits nor orders
if ((int) $idProduct && !$statsVisits['visits'] && !$statsSales['orders']) {
return;
}
$jsonArray = array(
'id_product' => (int) $product->id,
'product_name' => htmlspecialchars($product->name),
'uniqs' => (int) $statsVisits['uniqs'],
'visitors' => (int) $statsVisits['visitors'],
'visits' => (int) $statsVisits['visits'],
'pages' => (int) $statsVisits['pages'],
'registrations' => (int) $registrations,
'orders' => (int) $statsSales['orders'],
'sales' => Tools::displayPrice($statsSales['sales'], $currency),
'cart' => Tools::displayPrice(((int) $statsSales['orders'] ? $statsSales['sales'] / (int) $statsSales['orders'] : 0), $currency),
'reg_rate' => number_format((int) $statsVisits['uniqs'] ? (int) $registrations / (int) $statsVisits['uniqs'] : 0, 4, '.', ''),
'order_rate' => number_format((int) $statsVisits['uniqs'] ? (int) $statsSales['orders'] / (int) $statsVisits['uniqs'] : 0, 4, '.', ''),
'click_fee' => Tools::displayPrice((int) $statsVisits['visits'] * $referrer->click_fee, $currency),
'base_fee' => Tools::displayPrice($statsSales['orders'] * $referrer->base_fee, $currency),
'percent_fee' => Tools::displayPrice($statsSales['sales'] * $referrer->percent_fee / 100, $currency),
);
return json_encode([$jsonArray]);
}
}

659
classes/RequestSql.php Normal file
View File

@@ -0,0 +1,659 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class RequestSqlCore.
*/
class RequestSqlCore extends ObjectModel
{
public $name;
public $sql;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'request_sql',
'primary' => 'id_request_sql',
'fields' => array(
'name' => array('type' => self::TYPE_STRING, 'validate' => 'isString', 'required' => true, 'size' => 200),
'sql' => array('type' => self::TYPE_SQL, 'validate' => 'isString', 'required' => true),
),
);
/** @var array : List of params to tested */
public $tested = array(
'required' => array('SELECT', 'FROM'),
'option' => array('WHERE', 'ORDER', 'LIMIT', 'HAVING', 'GROUP', 'UNION'),
'operator' => array(
'AND', '&&', 'BETWEEN', 'AND', 'BINARY', '&', '~', '|', '^', 'CASE', 'WHEN', 'END', 'DIV', '/', '<=>', '=', '>=',
'>', 'IS', 'NOT', 'NULL', '<<', '<=', '<', 'LIKE', '-', '%', '!=', '<>', 'REGEXP', '!', '||', 'OR', '+', '>>', 'RLIKE', 'SOUNDS', '*',
'-', 'XOR', 'IN',
),
'function' => array(
'AVG', 'SUM', 'COUNT', 'MIN', 'MAX', 'STDDEV', 'STDDEV_SAMP', 'STDDEV_POP', 'VARIANCE', 'VAR_SAMP', 'VAR_POP',
'GROUP_CONCAT', 'BIT_AND', 'BIT_OR', 'BIT_XOR',
),
'unauthorized' => array(
'DELETE', 'ALTER', 'INSERT', 'REPLACE', 'CREATE', 'TRUNCATE', 'OPTIMIZE', 'GRANT', 'REVOKE', 'SHOW', 'HANDLER',
'LOAD', 'ROLLBACK', 'SAVEPOINT', 'UNLOCK', 'INSTALL', 'UNINSTALL', 'ANALZYE', 'BACKUP', 'CHECK', 'CHECKSUM', 'REPAIR', 'RESTORE', 'CACHE',
'DESCRIBE', 'EXPLAIN', 'USE', 'HELP', 'SET', 'DUPLICATE', 'VALUES', 'INTO', 'RENAME', 'CALL', 'PROCEDURE', 'FUNCTION', 'DATABASE', 'SERVER',
'LOGFILE', 'DEFINER', 'RETURNS', 'EVENT', 'TABLESPACE', 'VIEW', 'TRIGGER', 'DATA', 'DO', 'PASSWORD', 'USER', 'PLUGIN', 'FLUSH', 'KILL',
'RESET', 'START', 'STOP', 'PURGE', 'EXECUTE', 'PREPARE', 'DEALLOCATE', 'LOCK', 'USING', 'DROP', 'FOR', 'UPDATE', 'BEGIN', 'BY', 'ALL', 'SHARE',
'MODE', 'TO', 'KEY', 'DISTINCTROW', 'DISTINCT', 'HIGH_PRIORITY', 'LOW_PRIORITY', 'DELAYED', 'IGNORE', 'FORCE', 'STRAIGHT_JOIN',
'SQL_SMALL_RESULT', 'SQL_BIG_RESULT', 'QUICK', 'SQL_BUFFER_RESULT', 'SQL_CACHE', 'SQL_NO_CACHE', 'SQL_CALC_FOUND_ROWS', 'WITH',
),
);
public $attributes = array(
'passwd' => '*******************',
'secure_key' => '*******************',
);
/** @var array : list of errors */
public $error_sql = array();
/**
* Get list of request SQL.
*
* @return array|bool
*/
public static function getRequestSql()
{
if (!$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'request_sql` ORDER BY `id_request_sql`')) {
return false;
}
$requestSql = array();
foreach ($result as $row) {
$requestSql[] = $row['sql'];
}
return $requestSql;
}
/**
* Get list of request SQL by id request.
*
* @param int $id
*
* @return array
*/
public static function getRequestSqlById($id)
{
return Db::getInstance()->executeS('SELECT `sql` FROM `' . _DB_PREFIX_ . 'request_sql` WHERE `id_request_sql` = ' . (int) $id);
}
/**
* Call the parserSQL() method in Tools class
* Cut the request in table for check it.
*
* @param string $sql
*
* @return bool
*/
public function parsingSql($sql)
{
return Tools::parserSQL($sql);
}
/**
* Check if the parsing of the SQL request is good or not.
*
* @param array $tab
* @param bool $in
* @param string $sql
*
* @return bool
*/
public function validateParser($tab, $in, $sql)
{
if (!$tab) {
return false;
} elseif (isset($tab['UNION'])) {
$union = $tab['UNION'];
foreach ($union as $tab) {
if (!$this->validateSql($tab, $in, $sql)) {
return false;
}
}
return true;
} else {
return $this->validateSql($tab, $in, $sql);
}
}
/**
* Cut the request for check each cutting.
*
* @param $tab
* @param $in
* @param $sql
*
* @return bool
*/
public function validateSql($tab, $in, $sql)
{
if (!$this->testedRequired($tab)) {
return false;
} elseif (!$this->testedUnauthorized($tab)) {
return false;
} elseif (!$this->checkedFrom($tab['FROM'])) {
return false;
} elseif (!$this->checkedSelect($tab['SELECT'], $tab['FROM'], $in)) {
return false;
} elseif (isset($tab['WHERE'])) {
if (!$this->checkedWhere($tab['WHERE'], $tab['FROM'], $sql)) {
return false;
}
} elseif (isset($tab['HAVING'])) {
if (!$this->checkedHaving($tab['HAVING'], $tab['FROM'])) {
return false;
}
} elseif (isset($tab['ORDER'])) {
if (!$this->checkedOrder($tab['ORDER'], $tab['FROM'])) {
return false;
}
} elseif (isset($tab['GROUP'])) {
if (!$this->checkedGroupBy($tab['GROUP'], $tab['FROM'])) {
return false;
}
} elseif (isset($tab['LIMIT'])) {
if (!$this->checkedLimit($tab['LIMIT'])) {
return false;
}
}
if (empty($this->_errors) && !Db::getInstance()->executeS($sql)) {
return false;
}
return true;
}
/**
* Get list of all tables.
*
* @return array
*/
public function getTables()
{
$results = Db::getInstance()->executeS('SHOW TABLES');
foreach ($results as $result) {
$key = array_keys($result);
$tables[] = $result[$key[0]];
}
return $tables;
}
/**
* Get list of all attributes by an table.
*
* @param $table
*
* @return array
*/
public function getAttributesByTable($table)
{
return Db::getInstance()->executeS('DESCRIBE ' . pSQL($table));
}
/**
* Cut an join sentence.
*
* @param $attrs
* @param $from
*
* @return array|bool
*/
public function cutJoin($attrs, $from)
{
$tab = array();
foreach ($attrs as $attr) {
if (in_array($attr['expr_type'], array('operator', 'const'))) {
continue;
}
if ($attribut = $this->cutAttribute($attr['base_expr'], $from)) {
$tab[] = $attribut;
}
}
return $tab;
}
/**
* Cut an attribute with or without the alias.
*
* @param $attr
* @param $from
*
* @return array|bool
*/
public function cutAttribute($attr, $from)
{
$matches = array();
if (preg_match('/((`(\()?([a-z0-9_])+`(\))?)|((\()?([a-z0-9_])+(\))?))\.((`(\()?([a-z0-9_])+`(\))?)|((\()?([a-z0-9_])+(\))?))$/i', $attr, $matches, PREG_OFFSET_CAPTURE)) {
$tab = explode('.', str_replace(array('`', '(', ')'), '', $matches[0][0]));
if ($table = $this->returnNameTable($tab[0], $from)) {
return array(
'table' => $table,
'alias' => $tab[0],
'attribut' => $tab[1],
'string' => $attr,
);
}
} elseif (preg_match('/((`(\()?([a-z0-9_])+`(\))?)|((\()?([a-z0-9_])+(\))?))$/i', $attr, $matches, PREG_OFFSET_CAPTURE)) {
$attribut = str_replace(array('`', '(', ')'), '', $matches[0][0]);
if ($table = $this->returnNameTable(false, $from, $attr)) {
return array(
'table' => $table,
'attribut' => $attribut,
'string' => $attr,
);
}
}
return false;
}
/**
* Get name of table by alias.
*
* @param bool $alias
* @param $tables
*
* @return array|bool
*/
public function returnNameTable($alias, $tables, $attr = null)
{
if ($alias) {
foreach ($tables as $table) {
if (isset($table['alias'], $table['table']) && $table['alias']['no_quotes'] == $alias) {
return array($table['table']);
}
}
} elseif (count($tables) > 1) {
if ($attr !== null) {
$tab = array();
foreach ($tables as $table) {
if ($this->attributExistInTable($attr, $table['table'])) {
$tab = $table['table'];
}
}
if (count($tab) == 1) {
return $tab;
}
}
$this->error_sql['returnNameTable'] = false;
return false;
} else {
$tab = array();
foreach ($tables as $table) {
$tab[] = $table['table'];
}
return $tab;
}
}
/**
* Check if an attributes exists in a table.
*
* @param string $attr
* @param $table
*
* @return bool
*/
public function attributExistInTable($attr, $table)
{
if (!$attr) {
return true;
}
if (is_array($table) && (count($table) == 1)) {
$table = $table[0];
}
$attributs = $this->getAttributesByTable($table);
foreach ($attributs as $attribut) {
if ($attribut['Field'] == trim($attr, ' `')) {
return true;
}
}
return false;
}
/**
* Check if all required sentence existing.
*
* @param $tab
*
* @return bool
*/
public function testedRequired($tab)
{
foreach ($this->tested['required'] as $key) {
if (!array_key_exists($key, $tab)) {
$this->error_sql['testedRequired'] = $key;
return false;
}
}
return true;
}
/**
* Check if an unauthorized existing in an array.
*
* @param string $tab
*
* @return bool
*/
public function testedUnauthorized($tab)
{
foreach ($this->tested['unauthorized'] as $key) {
if (array_key_exists($key, $tab)) {
$this->error_sql['testedUnauthorized'] = $key;
return false;
}
}
return true;
}
/**
* Check a "FROM" sentence.
*
* @param string $from
*
* @return bool
*/
public function checkedFrom($from)
{
$nb = count($from);
for ($i = 0; $i < $nb; ++$i) {
$table = $from[$i];
if (isset($table['table']) && !in_array(str_replace('`', '', $table['table']), $this->getTables())) {
$this->error_sql['checkedFrom']['table'] = $table['table'];
return false;
}
if ($table['ref_type'] == 'ON' && (trim($table['join_type']) == 'LEFT' || trim($table['join_type']) == 'JOIN')) {
if ($attrs = $this->cutJoin($table['ref_clause'], $from)) {
foreach ($attrs as $attr) {
if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) {
$this->error_sql['checkedFrom']['attribut'] = array($attr['attribut'], implode(', ', $attr['table']));
return false;
}
}
} else {
if (isset($this->error_sql['returnNameTable'])) {
$this->error_sql['checkedFrom'] = $this->error_sql['returnNameTable'];
return false;
} else {
$this->error_sql['checkedFrom'] = false;
return false;
}
}
}
}
return true;
}
/**
* Check a "SELECT" sentence.
*
* @param string $select
* @param string $from
* @param bool $in
*
* @return bool
*/
public function checkedSelect($select, $from, $in = false)
{
$nb = count($select);
for ($i = 0; $i < $nb; ++$i) {
$attribut = $select[$i];
if ($attribut['base_expr'] != '*' && !preg_match('/\.*$/', $attribut['base_expr'])) {
if ($attribut['expr_type'] == 'colref') {
if ($attr = $this->cutAttribute(trim($attribut['base_expr']), $from)) {
if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) {
$this->error_sql['checkedSelect']['attribut'] = array($attr['attribut'], implode(', ', $attr['table']));
return false;
}
} else {
if (isset($this->error_sql['returnNameTable'])) {
$this->error_sql['checkedSelect'] = $this->error_sql['returnNameTable'];
return false;
} else {
$this->error_sql['checkedSelect'] = false;
return false;
}
}
}
} elseif ($in) {
$this->error_sql['checkedSelect']['*'] = false;
return false;
}
}
return true;
}
/**
* Check a "WHERE" sentence.
*
* @param string $where
* @param string $from
* @param string $sql
*
* @return bool
*/
public function checkedWhere($where, $from, $sql)
{
$nb = count($where);
for ($i = 0; $i < $nb; ++$i) {
$attribut = $where[$i];
if ($attribut['expr_type'] == 'colref' || $attribut['expr_type'] == 'reserved') {
if ($attr = $this->cutAttribute(trim($attribut['base_expr']), $from)) {
if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) {
$this->error_sql['checkedWhere']['attribut'] = array($attr['attribut'], implode(', ', $attr['table']));
return false;
}
} else {
if (isset($this->error_sql['returnNameTable'])) {
$this->error_sql['checkedWhere'] = $this->error_sql['returnNameTable'];
return false;
} else {
$this->error_sql['checkedWhere'] = false;
return false;
}
}
} elseif ($attribut['expr_type'] == 'operator') {
if (!in_array(strtoupper($attribut['base_expr']), $this->tested['operator'])) {
$this->error_sql['checkedWhere']['operator'] = array($attribut['base_expr']);
return false;
}
} elseif ($attribut['expr_type'] == 'subquery') {
$tab = $attribut['sub_tree'];
return $this->validateParser($tab, true, $sql);
}
}
return true;
}
/**
* Check a "HAVING" sentence.
*
* @param string $having
* @param string $from
*
* @return bool
*/
public function checkedHaving($having, $from)
{
$nb = count($having);
for ($i = 0; $i < $nb; ++$i) {
$attribut = $having[$i];
if ($attribut['expr_type'] == 'colref') {
if ($attr = $this->cutAttribute(trim($attribut['base_expr']), $from)) {
if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) {
$this->error_sql['checkedHaving']['attribut'] = array($attr['attribut'], implode(', ', $attr['table']));
return false;
}
} else {
if (isset($this->error_sql['returnNameTable'])) {
$this->error_sql['checkedHaving'] = $this->error_sql['returnNameTable'];
return false;
} else {
$this->error_sql['checkedHaving'] = false;
return false;
}
}
}
if ($attribut['expr_type'] == 'operator') {
if (!in_array(strtoupper($attribut['base_expr']), $this->tested['operator'])) {
$this->error_sql['checkedHaving']['operator'] = array($attribut['base_expr']);
return false;
}
}
}
return true;
}
/**
* Check a "ORDER" sentence.
*
* @param string $order
* @param string $from
*
* @return bool
*/
public function checkedOrder($order, $from)
{
$order = $order[0];
if (array_key_exists('expression', $order) && $order['type'] == 'expression') {
if ($attr = $this->cutAttribute(trim($order['base_expr']), $from)) {
if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) {
$this->error_sql['checkedOrder']['attribut'] = array($attr['attribut'], implode(', ', $attr['table']));
return false;
}
} else {
if (isset($this->error_sql['returnNameTable'])) {
$this->error_sql['checkedOrder'] = $this->error_sql['returnNameTable'];
return false;
} else {
$this->error_sql['checkedOrder'] = false;
return false;
}
}
}
return true;
}
/**
* Check a "GROUP BY" sentence.
*
* @param string $group
* @param string $from
*
* @return bool
*/
public function checkedGroupBy($group, $from)
{
$group = $group[0];
if ($group['type'] == 'expression') {
if ($attr = $this->cutAttribute(trim($group['base_expr']), $from)) {
if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) {
$this->error_sql['checkedGroupBy']['attribut'] = array($attr['attribut'], implode(', ', $attr['table']));
return false;
}
} else {
if (isset($this->error_sql['returnNameTable'])) {
$this->error_sql['checkedGroupBy'] = $this->error_sql['returnNameTable'];
return false;
} else {
$this->error_sql['checkedGroupBy'] = false;
return false;
}
}
}
return true;
}
/**
* Check a "LIMIT" sentence.
*
* @param string $limit
*
* @return bool
*/
public function checkedLimit($limit)
{
if (!preg_match('#^[0-9]+$#', trim($limit['start'])) || !preg_match('#^[0-9]+$#', trim($limit['end']))) {
$this->error_sql['checkedLimit'] = false;
return false;
}
return true;
}
}

83
classes/Risk.php Normal file
View File

@@ -0,0 +1,83 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class RiskCore.
*
* @since 1.5.0
*/
class RiskCore extends ObjectModel
{
public $id;
public $id_risk;
public $name;
public $color;
public $percent;
public static $definition = array(
'table' => 'risk',
'primary' => 'id_risk',
'multilang' => true,
'fields' => array(
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isString', 'required' => true, 'size' => 20),
'color' => array('type' => self::TYPE_STRING, 'validate' => 'isColor', 'size' => 32),
'percent' => array('type' => self::TYPE_INT, 'validate' => 'isPercentage'),
),
);
/**
* Get fields.
*
* @return mixed
*/
public function getFields()
{
$this->validateFields();
$fields['id_risk'] = (int) $this->id_risk;
$fields['color'] = pSQL($this->color);
$fields['percent'] = (int) $this->percent;
return $fields;
}
/**
* Get Risks.
*
* @param int|null $idLang Language ID
*
* @return PrestaShopCollection
*/
public static function getRisks($idLang = null)
{
if (null === $idLang) {
$idLang = Context::getContext()->language->id;
}
$risks = new PrestaShopCollection('Risk', $idLang);
return $risks;
}
}

947
classes/Search.php Normal file
View File

@@ -0,0 +1,947 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
define('PS_SEARCH_MAX_WORD_LENGTH', 15);
/* Copied from Drupal search module, except for \x{0}-\x{2f} that has been replaced by \x{0}-\x{2c}\x{2e}-\x{2f} in order to keep the char '-' */
define(
'PREG_CLASS_SEARCH_EXCLUDE',
'\x{0}-\x{2c}\x{2e}-\x{2f}\x{3a}-\x{40}\x{5b}-\x{60}\x{7b}-\x{bf}\x{d7}\x{f7}\x{2b0}-' .
'\x{385}\x{387}\x{3f6}\x{482}-\x{489}\x{559}-\x{55f}\x{589}-\x{5c7}\x{5f3}-' .
'\x{61f}\x{640}\x{64b}-\x{65e}\x{66a}-\x{66d}\x{670}\x{6d4}\x{6d6}-\x{6ed}' .
'\x{6fd}\x{6fe}\x{700}-\x{70f}\x{711}\x{730}-\x{74a}\x{7a6}-\x{7b0}\x{901}-' .
'\x{903}\x{93c}\x{93e}-\x{94d}\x{951}-\x{954}\x{962}-\x{965}\x{970}\x{981}-' .
'\x{983}\x{9bc}\x{9be}-\x{9cd}\x{9d7}\x{9e2}\x{9e3}\x{9f2}-\x{a03}\x{a3c}-' .
'\x{a4d}\x{a70}\x{a71}\x{a81}-\x{a83}\x{abc}\x{abe}-\x{acd}\x{ae2}\x{ae3}' .
'\x{af1}-\x{b03}\x{b3c}\x{b3e}-\x{b57}\x{b70}\x{b82}\x{bbe}-\x{bd7}\x{bf0}-' .
'\x{c03}\x{c3e}-\x{c56}\x{c82}\x{c83}\x{cbc}\x{cbe}-\x{cd6}\x{d02}\x{d03}' .
'\x{d3e}-\x{d57}\x{d82}\x{d83}\x{dca}-\x{df4}\x{e31}\x{e34}-\x{e3f}\x{e46}-' .
'\x{e4f}\x{e5a}\x{e5b}\x{eb1}\x{eb4}-\x{ebc}\x{ec6}-\x{ecd}\x{f01}-\x{f1f}' .
'\x{f2a}-\x{f3f}\x{f71}-\x{f87}\x{f90}-\x{fd1}\x{102c}-\x{1039}\x{104a}-' .
'\x{104f}\x{1056}-\x{1059}\x{10fb}\x{10fc}\x{135f}-\x{137c}\x{1390}-\x{1399}' .
'\x{166d}\x{166e}\x{1680}\x{169b}\x{169c}\x{16eb}-\x{16f0}\x{1712}-\x{1714}' .
'\x{1732}-\x{1736}\x{1752}\x{1753}\x{1772}\x{1773}\x{17b4}-\x{17db}\x{17dd}' .
'\x{17f0}-\x{180e}\x{1843}\x{18a9}\x{1920}-\x{1945}\x{19b0}-\x{19c0}\x{19c8}' .
'\x{19c9}\x{19de}-\x{19ff}\x{1a17}-\x{1a1f}\x{1d2c}-\x{1d61}\x{1d78}\x{1d9b}-' .
'\x{1dc3}\x{1fbd}\x{1fbf}-\x{1fc1}\x{1fcd}-\x{1fcf}\x{1fdd}-\x{1fdf}\x{1fed}-' .
'\x{1fef}\x{1ffd}-\x{2070}\x{2074}-\x{207e}\x{2080}-\x{2101}\x{2103}-\x{2106}' .
'\x{2108}\x{2109}\x{2114}\x{2116}-\x{2118}\x{211e}-\x{2123}\x{2125}\x{2127}' .
'\x{2129}\x{212e}\x{2132}\x{213a}\x{213b}\x{2140}-\x{2144}\x{214a}-\x{2b13}' .
'\x{2ce5}-\x{2cff}\x{2d6f}\x{2e00}-\x{3005}\x{3007}-\x{303b}\x{303d}-\x{303f}' .
'\x{3099}-\x{309e}\x{30a0}\x{30fb}\x{30fd}\x{30fe}\x{3190}-\x{319f}\x{31c0}-' .
'\x{31cf}\x{3200}-\x{33ff}\x{4dc0}-\x{4dff}\x{a015}\x{a490}-\x{a716}\x{a802}' .
'\x{e000}-\x{f8ff}\x{fb29}\x{fd3e}-\x{fd3f}\x{fdfc}-\x{fdfd}' .
'\x{fd3f}\x{fdfc}-\x{fe6b}\x{feff}-\x{ff0f}\x{ff1a}-\x{ff20}\x{ff3b}-\x{ff40}' .
'\x{ff5b}-\x{ff65}\x{ff70}\x{ff9e}\x{ff9f}\x{ffe0}-\x{fffd}'
);
define(
'PREG_CLASS_NUMBERS',
'\x{30}-\x{39}\x{b2}\x{b3}\x{b9}\x{bc}-\x{be}\x{660}-\x{669}\x{6f0}-\x{6f9}' .
'\x{966}-\x{96f}\x{9e6}-\x{9ef}\x{9f4}-\x{9f9}\x{a66}-\x{a6f}\x{ae6}-\x{aef}' .
'\x{b66}-\x{b6f}\x{be7}-\x{bf2}\x{c66}-\x{c6f}\x{ce6}-\x{cef}\x{d66}-\x{d6f}' .
'\x{e50}-\x{e59}\x{ed0}-\x{ed9}\x{f20}-\x{f33}\x{1040}-\x{1049}\x{1369}-' .
'\x{137c}\x{16ee}-\x{16f0}\x{17e0}-\x{17e9}\x{17f0}-\x{17f9}\x{1810}-\x{1819}' .
'\x{1946}-\x{194f}\x{2070}\x{2074}-\x{2079}\x{2080}-\x{2089}\x{2153}-\x{2183}' .
'\x{2460}-\x{249b}\x{24ea}-\x{24ff}\x{2776}-\x{2793}\x{3007}\x{3021}-\x{3029}' .
'\x{3038}-\x{303a}\x{3192}-\x{3195}\x{3220}-\x{3229}\x{3251}-\x{325f}\x{3280}-' .
'\x{3289}\x{32b1}-\x{32bf}\x{ff10}-\x{ff19}'
);
define(
'PREG_CLASS_PUNCTUATION',
'\x{21}-\x{23}\x{25}-\x{2a}\x{2c}-\x{2f}\x{3a}\x{3b}\x{3f}\x{40}\x{5b}-\x{5d}' .
'\x{5f}\x{7b}\x{7d}\x{a1}\x{ab}\x{b7}\x{bb}\x{bf}\x{37e}\x{387}\x{55a}-\x{55f}' .
'\x{589}\x{58a}\x{5be}\x{5c0}\x{5c3}\x{5f3}\x{5f4}\x{60c}\x{60d}\x{61b}\x{61f}' .
'\x{66a}-\x{66d}\x{6d4}\x{700}-\x{70d}\x{964}\x{965}\x{970}\x{df4}\x{e4f}' .
'\x{e5a}\x{e5b}\x{f04}-\x{f12}\x{f3a}-\x{f3d}\x{f85}\x{104a}-\x{104f}\x{10fb}' .
'\x{1361}-\x{1368}\x{166d}\x{166e}\x{169b}\x{169c}\x{16eb}-\x{16ed}\x{1735}' .
'\x{1736}\x{17d4}-\x{17d6}\x{17d8}-\x{17da}\x{1800}-\x{180a}\x{1944}\x{1945}' .
'\x{2010}-\x{2027}\x{2030}-\x{2043}\x{2045}-\x{2051}\x{2053}\x{2054}\x{2057}' .
'\x{207d}\x{207e}\x{208d}\x{208e}\x{2329}\x{232a}\x{23b4}-\x{23b6}\x{2768}-' .
'\x{2775}\x{27e6}-\x{27eb}\x{2983}-\x{2998}\x{29d8}-\x{29db}\x{29fc}\x{29fd}' .
'\x{3001}-\x{3003}\x{3008}-\x{3011}\x{3014}-\x{301f}\x{3030}\x{303d}\x{30a0}' .
'\x{30fb}\x{fd3e}\x{fd3f}\x{fe30}-\x{fe52}\x{fe54}-\x{fe61}\x{fe63}\x{fe68}' .
'\x{fe6a}\x{fe6b}\x{ff01}-\x{ff03}\x{ff05}-\x{ff0a}\x{ff0c}-\x{ff0f}\x{ff1a}' .
'\x{ff1b}\x{ff1f}\x{ff20}\x{ff3b}-\x{ff3d}\x{ff3f}\x{ff5b}\x{ff5d}\x{ff5f}-' .
'\x{ff65}'
);
/*
* Matches all CJK characters that are candidates for auto-splitting
* (Chinese, Japanese, Korean).
* Contains kana and BMP ideographs.
*/
define('PREG_CLASS_CJK', '\x{3041}-\x{30ff}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fbb}\x{f900}-\x{fad9}');
class SearchCore
{
public static function extractKeyWords($string, $id_lang, $indexation = false, $iso_code = false)
{
$sanitizedString = Search::sanitize($string, $id_lang, $indexation, $iso_code, false);
$words = explode(' ', $sanitizedString);
if (strpos($string, '-') !== false) {
$sanitizedString = Search::sanitize($string, $id_lang, $indexation, $iso_code, true);
$words2 = explode(' ', $sanitizedString);
// foreach word containing hyphen, we want to index additional word removing the hyphen
// eg: t-shirt => tshirt
foreach ($words2 as $word) {
if (strpos($word, '-') !== false) {
$word = str_replace('-', '', $word);
if (!empty($word)) {
$words[] = $word;
}
}
}
$words = array_merge($words, $words2);
}
return array_unique($words);
}
public static function sanitize($string, $id_lang, $indexation = false, $iso_code = false, $keepHyphens = false)
{
$string = trim($string);
if (empty($string)) {
return '';
}
$string = Tools::strtolower(strip_tags($string));
$string = html_entity_decode($string, ENT_NOQUOTES, 'utf-8');
$string = preg_replace('/([' . PREG_CLASS_NUMBERS . ']+)[' . PREG_CLASS_PUNCTUATION . ']+(?=[' . PREG_CLASS_NUMBERS . '])/u', '\1', $string);
$string = preg_replace('/[' . PREG_CLASS_SEARCH_EXCLUDE . ']+/u', ' ', $string);
if ($indexation) {
if (!$keepHyphens) {
$string = str_replace(['.', '_', '-'], ' ', $string);
} else {
$string = str_replace(['.', '_'], ' ', $string);
}
} else {
$words = explode(' ', $string);
$processed_words = array();
// search for aliases for each word of the query
foreach ($words as $word) {
$alias = new Alias(null, $word);
if (Validate::isLoadedObject($alias)) {
$processed_words[] = $alias->search;
} else {
$processed_words[] = $word;
}
}
$string = implode(' ', $processed_words);
$string = str_replace(['.', '_'], '', $string);
if (!$keepHyphens) {
$string = ltrim(preg_replace('/([^ ])-/', '$1 ', ' ' . $string));
}
}
$blacklist = Tools::strtolower(Configuration::get('PS_SEARCH_BLACKLIST', $id_lang));
if (!empty($blacklist)) {
$string = preg_replace('/(?<=\s)(' . $blacklist . ')(?=\s)/Su', '', $string);
$string = preg_replace('/^(' . $blacklist . ')(?=\s)/Su', '', $string);
$string = preg_replace('/(?<=\s)(' . $blacklist . ')$/Su', '', $string);
$string = preg_replace('/^(' . $blacklist . ')$/Su', '', $string);
}
// If the language is constituted with symbol and there is no "words", then split every chars
if (in_array($iso_code, array('zh', 'tw', 'ja')) && function_exists('mb_strlen')) {
// Cut symbols from letters
$symbols = '';
$letters = '';
foreach (explode(' ', $string) as $mb_word) {
if (strlen(Tools::replaceAccentedChars($mb_word)) == mb_strlen(Tools::replaceAccentedChars($mb_word))) {
$letters .= $mb_word . ' ';
} else {
$symbols .= $mb_word . ' ';
}
}
if (preg_match_all('/./u', $symbols, $matches)) {
$symbols = implode(' ', $matches[0]);
}
$string = $letters . $symbols;
} elseif ($indexation) {
$minWordLen = (int) Configuration::get('PS_SEARCH_MINWORDLEN');
if ($minWordLen > 1) {
--$minWordLen;
$string = preg_replace('/(?<=\s)[^\s]{1,' . $minWordLen . '}(?=\s)/Su', ' ', $string);
$string = preg_replace('/^[^\s]{1,' . $minWordLen . '}(?=\s)/Su', '', $string);
$string = preg_replace('/(?<=\s)[^\s]{1,' . $minWordLen . '}$/Su', '', $string);
$string = preg_replace('/^[^\s]{1,' . $minWordLen . '}$/Su', '', $string);
}
}
$string = Tools::replaceAccentedChars(trim(preg_replace('/\s+/', ' ', $string)));
return $string;
}
public static function find(
$id_lang,
$expr,
$page_number = 1,
$page_size = 1,
$order_by = 'position',
$order_way = 'desc',
$ajax = false,
$use_cookie = true,
Context $context = null
) {
if (!$context) {
$context = Context::getContext();
}
$db = Db::getInstance(_PS_USE_SQL_SLAVE_);
// TODO : smart page management
if ($page_number < 1) {
$page_number = 1;
}
if ($page_size < 1) {
$page_size = 1;
}
if (!Validate::isOrderBy($order_by) || !Validate::isOrderWay($order_way)) {
return false;
}
$intersect_array = array();
$score_array = array();
$words = Search::extractKeyWords($expr, $id_lang, false, $context->language->iso_code);
foreach ($words as $key => $word) {
if (!empty($word) && strlen($word) >= (int) Configuration::get('PS_SEARCH_MINWORDLEN')) {
$sql_param_search = self::getSearchParamFromWord($word);
$intersect_array[] = 'SELECT DISTINCT si.id_product
FROM ' . _DB_PREFIX_ . 'search_word sw
LEFT JOIN ' . _DB_PREFIX_ . 'search_index si ON sw.id_word = si.id_word
WHERE sw.id_lang = ' . (int) $id_lang . '
AND sw.id_shop = ' . $context->shop->id . '
AND sw.word LIKE
\'' . $sql_param_search . '\'';
$score_array[] = 'sw.word LIKE \'' . $sql_param_search . '\'';
} else {
unset($words[$key]);
}
}
if (!count($words)) {
return $ajax ? array() : array('total' => 0, 'result' => array());
}
$score = '';
if (is_array($score_array) && !empty($score_array)) {
$score = ',(
SELECT SUM(weight)
FROM ' . _DB_PREFIX_ . 'search_word sw
LEFT JOIN ' . _DB_PREFIX_ . 'search_index si ON sw.id_word = si.id_word
WHERE sw.id_lang = ' . (int) $id_lang . '
AND sw.id_shop = ' . $context->shop->id . '
AND si.id_product = p.id_product
AND (' . implode(' OR ', $score_array) . ')
) position';
}
$sql_groups = '';
if (Group::isFeatureActive()) {
$groups = FrontController::getCurrentCustomerGroups();
$sql_groups = 'AND cg.`id_group` ' . (count($groups) ? 'IN (' . implode(',', $groups) . ')' : '=' . (int) Configuration::get('PS_UNIDENTIFIED_GROUP'));
}
$results = $db->executeS('
SELECT DISTINCT cp.`id_product`
FROM `' . _DB_PREFIX_ . 'category_product` cp
' . (Group::isFeatureActive() ? 'INNER JOIN `' . _DB_PREFIX_ . 'category_group` cg ON cp.`id_category` = cg.`id_category`' : '') . '
INNER JOIN `' . _DB_PREFIX_ . 'category` c ON cp.`id_category` = c.`id_category`
INNER JOIN `' . _DB_PREFIX_ . 'product` p ON cp.`id_product` = p.`id_product`
' . Shop::addSqlAssociation('product', 'p', false) . '
WHERE c.`active` = 1
AND product_shop.`active` = 1
AND product_shop.`visibility` IN ("both", "search")
AND product_shop.indexed = 1
' . $sql_groups, true, false);
$eligible_products = array();
foreach ($results as $row) {
$eligible_products[] = $row['id_product'];
}
$eligible_products2 = array();
foreach ($intersect_array as $query) {
foreach ($db->executeS($query, true, false) as $row) {
$eligible_products2[] = $row['id_product'];
}
}
$eligible_products = array_unique(array_intersect($eligible_products, array_unique($eligible_products2)));
if (!count($eligible_products)) {
return $ajax ? array() : array('total' => 0, 'result' => array());
}
$product_pool = '';
foreach ($eligible_products as $id_product) {
if ($id_product) {
$product_pool .= (int) $id_product . ',';
}
}
if (empty($product_pool)) {
return $ajax ? array() : array('total' => 0, 'result' => array());
}
$product_pool = ((strpos($product_pool, ',') === false) ? (' = ' . (int) $product_pool . ' ') : (' IN (' . rtrim($product_pool, ',') . ') '));
if ($ajax) {
$sql = 'SELECT DISTINCT p.id_product, pl.name pname, cl.name cname,
cl.link_rewrite crewrite, pl.link_rewrite prewrite ' . $score . '
FROM ' . _DB_PREFIX_ . 'product p
INNER JOIN `' . _DB_PREFIX_ . 'product_lang` pl ON (
p.`id_product` = pl.`id_product`
AND pl.`id_lang` = ' . (int) $id_lang . Shop::addSqlRestrictionOnLang('pl') . '
)
' . Shop::addSqlAssociation('product', 'p') . '
INNER JOIN `' . _DB_PREFIX_ . 'category_lang` cl ON (
product_shop.`id_category_default` = cl.`id_category`
AND cl.`id_lang` = ' . (int) $id_lang . Shop::addSqlRestrictionOnLang('cl') . '
)
WHERE p.`id_product` ' . $product_pool . '
ORDER BY position DESC LIMIT 10';
return $db->executeS($sql, true, false);
}
if (strpos($order_by, '.') > 0) {
$order_by = explode('.', $order_by);
$order_by = pSQL($order_by[0]) . '.`' . pSQL($order_by[1]) . '`';
}
$alias = '';
if ($order_by == 'price') {
$alias = 'product_shop.';
} elseif (in_array($order_by, array('date_upd', 'date_add'))) {
$alias = 'p.';
}
$sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity,
pl.`description_short`, pl.`available_now`, pl.`available_later`, pl.`link_rewrite`, pl.`name`,
image_shop.`id_image` id_image, il.`legend`, m.`name` manufacturer_name ' . $score . ',
DATEDIFF(
p.`date_add`,
DATE_SUB(
"' . date('Y-m-d') . ' 00:00:00",
INTERVAL ' . (Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20) . ' DAY
)
) > 0 new' . (Combination::isFeatureActive() ? ', product_attribute_shop.minimal_quantity AS product_attribute_minimal_quantity, IFNULL(product_attribute_shop.`id_product_attribute`,0) id_product_attribute' : '') . '
FROM ' . _DB_PREFIX_ . 'product p
' . Shop::addSqlAssociation('product', 'p') . '
INNER JOIN `' . _DB_PREFIX_ . 'product_lang` pl ON (
p.`id_product` = pl.`id_product`
AND pl.`id_lang` = ' . (int) $id_lang . Shop::addSqlRestrictionOnLang('pl') . '
)
' . (Combination::isFeatureActive() ? 'LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_shop` product_attribute_shop FORCE INDEX (id_product)
ON (p.`id_product` = product_attribute_shop.`id_product` AND product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop=' . (int) $context->shop->id . ')' : '') . '
' . Product::sqlStock('p', 0) . '
LEFT JOIN `' . _DB_PREFIX_ . 'manufacturer` m FORCE INDEX (PRIMARY)
ON m.`id_manufacturer` = p.`id_manufacturer`
LEFT JOIN `' . _DB_PREFIX_ . 'image_shop` image_shop FORCE INDEX (id_product)
ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop=' . (int) $context->shop->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $id_lang . ')
WHERE p.`id_product` ' . $product_pool . '
GROUP BY product_shop.id_product
' . ($order_by ? 'ORDER BY ' . $alias . $order_by : '') . ($order_way ? ' ' . $order_way : '') . '
LIMIT ' . (int) (($page_number - 1) * $page_size) . ',' . (int) $page_size;
$result = $db->executeS($sql, true, false);
$sql = 'SELECT COUNT(*)
FROM ' . _DB_PREFIX_ . 'product p
' . Shop::addSqlAssociation('product', 'p') . '
INNER JOIN `' . _DB_PREFIX_ . 'product_lang` pl ON (
p.`id_product` = pl.`id_product`
AND pl.`id_lang` = ' . (int) $id_lang . Shop::addSqlRestrictionOnLang('pl') . '
)
LEFT JOIN `' . _DB_PREFIX_ . 'manufacturer` m ON m.`id_manufacturer` = p.`id_manufacturer`
WHERE p.`id_product` ' . $product_pool;
$total = $db->getValue($sql, false);
if (!$result) {
$result_properties = false;
} else {
$result_properties = Product::getProductsProperties((int) $id_lang, $result);
}
return array('total' => $total, 'result' => $result_properties);
}
/**
* @param Db $db
* @param int $id_product
* @param int $id_lang
*
* @return string
*/
public static function getTags($db, $id_product, $id_lang)
{
$tags = '';
$tagsArray = $db->executeS('
SELECT t.name FROM ' . _DB_PREFIX_ . 'product_tag pt
LEFT JOIN ' . _DB_PREFIX_ . 'tag t ON (pt.id_tag = t.id_tag AND t.id_lang = ' . (int) $id_lang . ')
WHERE pt.id_product = ' . (int) $id_product, true, false);
foreach ($tagsArray as $tag) {
$tags .= $tag['name'] . ' ';
}
return $tags;
}
/**
* @param Db $db
* @param int $id_product
* @param int $id_lang
*
* @return string
*/
public static function getAttributes($db, $id_product, $id_lang)
{
if (!Combination::isFeatureActive()) {
return '';
}
$attributes = '';
$attributesArray = $db->executeS('
SELECT al.name FROM ' . _DB_PREFIX_ . 'product_attribute pa
INNER JOIN ' . _DB_PREFIX_ . 'product_attribute_combination pac ON pa.id_product_attribute = pac.id_product_attribute
INNER JOIN ' . _DB_PREFIX_ . 'attribute_lang al ON (pac.id_attribute = al.id_attribute AND al.id_lang = ' . (int) $id_lang . ')
' . Shop::addSqlAssociation('product_attribute', 'pa') . '
WHERE pa.id_product = ' . (int) $id_product, true, false);
foreach ($attributesArray as $attribute) {
$attributes .= $attribute['name'] . ' ';
}
return $attributes;
}
/**
* @param Db $db
* @param int $id_product
* @param int $id_lang
*
* @return string
*/
public static function getFeatures($db, $id_product, $id_lang)
{
if (!Feature::isFeatureActive()) {
return '';
}
$features = '';
$featuresArray = $db->executeS('
SELECT fvl.value FROM ' . _DB_PREFIX_ . 'feature_product fp
LEFT JOIN ' . _DB_PREFIX_ . 'feature_value_lang fvl ON (fp.id_feature_value = fvl.id_feature_value AND fvl.id_lang = ' . (int) $id_lang . ')
WHERE fp.id_product = ' . (int) $id_product, true, false);
foreach ($featuresArray as $feature) {
$features .= $feature['value'] . ' ';
}
return $features;
}
/**
* @param $weight_array
*
* @return string
*/
protected static function getSQLProductAttributeFields(&$weight_array)
{
$sql = '';
if (is_array($weight_array)) {
foreach ($weight_array as $key => $weight) {
if ((int) $weight) {
switch ($key) {
case 'pa_reference':
$sql .= ', pa.reference AS pa_reference';
break;
case 'pa_supplier_reference':
$sql .= ', pa.supplier_reference AS pa_supplier_reference';
break;
case 'pa_ean13':
$sql .= ', pa.ean13 AS pa_ean13';
break;
case 'pa_upc':
$sql .= ', pa.upc AS pa_upc';
break;
}
}
}
}
return $sql;
}
protected static function getProductsToIndex($total_languages, $id_product = false, $limit = 50, $weight_array = array())
{
$ids = null;
if (!$id_product) {
// Limit products for each step but be sure that each attribute is taken into account
$sql = 'SELECT p.id_product FROM ' . _DB_PREFIX_ . 'product p
' . Shop::addSqlAssociation('product', 'p', true, null, true) . '
WHERE product_shop.`indexed` = 0
AND product_shop.`visibility` IN ("both", "search")
AND product_shop.`active` = 1
ORDER BY product_shop.`id_product` ASC
LIMIT ' . (int) $limit;
$res = Db::getInstance()->executeS($sql, false);
while ($row = Db::getInstance()->nextRow($res)) {
$ids[] = $row['id_product'];
}
}
// Now get every attribute in every language
$sql = 'SELECT p.id_product, pl.id_lang, pl.id_shop, l.iso_code';
if (is_array($weight_array)) {
foreach ($weight_array as $key => $weight) {
if ((int) $weight) {
switch ($key) {
case 'pname':
$sql .= ', pl.name pname';
break;
case 'reference':
$sql .= ', p.reference';
break;
case 'supplier_reference':
$sql .= ', p.supplier_reference';
break;
case 'ean13':
$sql .= ', p.ean13';
break;
case 'upc':
$sql .= ', p.upc';
break;
case 'description_short':
$sql .= ', pl.description_short';
break;
case 'description':
$sql .= ', pl.description';
break;
case 'cname':
$sql .= ', cl.name cname';
break;
case 'mname':
$sql .= ', m.name mname';
break;
}
}
}
}
$sql .= ' FROM ' . _DB_PREFIX_ . 'product p
LEFT JOIN ' . _DB_PREFIX_ . 'product_lang pl
ON p.id_product = pl.id_product
' . Shop::addSqlAssociation('product', 'p', true, null, true) . '
LEFT JOIN ' . _DB_PREFIX_ . 'category_lang cl
ON (cl.id_category = product_shop.id_category_default AND pl.id_lang = cl.id_lang AND cl.id_shop = product_shop.id_shop)
LEFT JOIN ' . _DB_PREFIX_ . 'manufacturer m
ON m.id_manufacturer = p.id_manufacturer
LEFT JOIN ' . _DB_PREFIX_ . 'lang l
ON l.id_lang = pl.id_lang
WHERE product_shop.indexed = 0
AND product_shop.visibility IN ("both", "search")
' . ($id_product ? 'AND p.id_product = ' . (int) $id_product : '') . '
' . ($ids ? 'AND p.id_product IN (' . implode(',', array_map('intval', $ids)) . ')' : '') . '
AND product_shop.`active` = 1
AND pl.`id_shop` = product_shop.`id_shop`';
return Db::getInstance()->executeS($sql, true, false);
}
/**
* @param Db $db
* @param int $id_product
* @param string $sql_attribute
*
* @return array|null
*/
protected static function getAttributesFields($db, $id_product, $sql_attribute)
{
return $db->executeS('SELECT id_product ' . $sql_attribute . ' FROM ' .
_DB_PREFIX_ . 'product_attribute pa WHERE pa.id_product = ' . (int) $id_product, true, false);
}
/**
* @param $product_array
* @param $weight_array
* @param $key
* @param $value
* @param $id_lang
* @param $iso_code
*/
protected static function fillProductArray(&$product_array, $weight_array, $key, $value, $id_lang, $iso_code)
{
if (strncmp($key, 'id_', 3) && isset($weight_array[$key])) {
$words = Search::extractKeyWords($value, (int) $id_lang, true, $iso_code);
foreach ($words as $word) {
if (!empty($word)) {
$word = Tools::substr($word, 0, PS_SEARCH_MAX_WORD_LENGTH);
if (!isset($product_array[$word])) {
$product_array[$word] = 0;
}
$product_array[$word] += $weight_array[$key];
}
}
}
}
public static function indexation($full = false, $id_product = false)
{
$db = Db::getInstance();
if ($id_product) {
$full = false;
}
if ($full && Context::getContext()->shop->getContext() == Shop::CONTEXT_SHOP) {
$db->execute('DELETE si, sw FROM `' . _DB_PREFIX_ . 'search_index` si
INNER JOIN `' . _DB_PREFIX_ . 'product` p ON (p.id_product = si.id_product)
' . Shop::addSqlAssociation('product', 'p') . '
INNER JOIN `' . _DB_PREFIX_ . 'search_word` sw ON (sw.id_word = si.id_word AND product_shop.id_shop = sw.id_shop)
WHERE product_shop.`visibility` IN ("both", "search")
AND product_shop.`active` = 1');
$db->execute('UPDATE `' . _DB_PREFIX_ . 'product` p
' . Shop::addSqlAssociation('product', 'p') . '
SET p.`indexed` = 0, product_shop.`indexed` = 0
WHERE product_shop.`visibility` IN ("both", "search")
AND product_shop.`active` = 1
');
} elseif ($full) {
$db->execute('TRUNCATE ' . _DB_PREFIX_ . 'search_index');
$db->execute('TRUNCATE ' . _DB_PREFIX_ . 'search_word');
ObjectModel::updateMultishopTable('Product', array('indexed' => 0));
} else {
$db->execute('DELETE si FROM `' . _DB_PREFIX_ . 'search_index` si
INNER JOIN `' . _DB_PREFIX_ . 'product` p ON (p.id_product = si.id_product)
' . Shop::addSqlAssociation('product', 'p') . '
WHERE product_shop.`visibility` IN ("both", "search")
AND product_shop.`active` = 1
AND ' . ($id_product ? 'p.`id_product` = ' . (int) $id_product : 'product_shop.`indexed` = 0'));
$db->execute('UPDATE `' . _DB_PREFIX_ . 'product` p
' . Shop::addSqlAssociation('product', 'p') . '
SET p.`indexed` = 0, product_shop.`indexed` = 0
WHERE product_shop.`visibility` IN ("both", "search")
AND product_shop.`active` = 1
AND ' . ($id_product ? 'p.`id_product` = ' . (int) $id_product : 'product_shop.`indexed` = 0'));
}
// Every fields are weighted according to the configuration in the backend
$weight_array = array(
'pname' => Configuration::get('PS_SEARCH_WEIGHT_PNAME'),
'reference' => Configuration::get('PS_SEARCH_WEIGHT_REF'),
'pa_reference' => Configuration::get('PS_SEARCH_WEIGHT_REF'),
'supplier_reference' => Configuration::get('PS_SEARCH_WEIGHT_REF'),
'pa_supplier_reference' => Configuration::get('PS_SEARCH_WEIGHT_REF'),
'ean13' => Configuration::get('PS_SEARCH_WEIGHT_REF'),
'pa_ean13' => Configuration::get('PS_SEARCH_WEIGHT_REF'),
'upc' => Configuration::get('PS_SEARCH_WEIGHT_REF'),
'pa_upc' => Configuration::get('PS_SEARCH_WEIGHT_REF'),
'description_short' => Configuration::get('PS_SEARCH_WEIGHT_SHORTDESC'),
'description' => Configuration::get('PS_SEARCH_WEIGHT_DESC'),
'cname' => Configuration::get('PS_SEARCH_WEIGHT_CNAME'),
'mname' => Configuration::get('PS_SEARCH_WEIGHT_MNAME'),
'tags' => Configuration::get('PS_SEARCH_WEIGHT_TAG'),
'attributes' => Configuration::get('PS_SEARCH_WEIGHT_ATTRIBUTE'),
'features' => Configuration::get('PS_SEARCH_WEIGHT_FEATURE'),
);
// Those are kind of global variables required to save the processed data in the database every X occurrences, in order to avoid overloading MySQL
$count_words = 0;
$query_array3 = array();
// Retrieve the number of languages
$total_languages = count(Language::getIDs(false));
$sql_attribute = Search::getSQLProductAttributeFields($weight_array);
// Products are processed 50 by 50 in order to avoid overloading MySQL
while (($products = Search::getProductsToIndex($total_languages, $id_product, 50, $weight_array)) && (count($products) > 0)) {
$products_array = array();
// Now each non-indexed product is processed one by one, langage by langage
foreach ($products as $product) {
if ((int) $weight_array['tags']) {
$product['tags'] = Search::getTags($db, (int) $product['id_product'], (int) $product['id_lang']);
}
if ((int) $weight_array['attributes']) {
$product['attributes'] = Search::getAttributes($db, (int) $product['id_product'], (int) $product['id_lang']);
}
if ((int) $weight_array['features']) {
$product['features'] = Search::getFeatures($db, (int) $product['id_product'], (int) $product['id_lang']);
}
if ($sql_attribute) {
$attribute_fields = Search::getAttributesFields($db, (int) $product['id_product'], $sql_attribute);
if ($attribute_fields) {
$product['attributes_fields'] = $attribute_fields;
}
}
// Data must be cleaned of html, bad characters, spaces and anything, then if the resulting words are long enough, they're added to the array
$product_array = array();
foreach ($product as $key => $value) {
if ($key == 'attributes_fields') {
foreach ($value as $pa_array) {
foreach ($pa_array as $pa_key => $pa_value) {
Search::fillProductArray($product_array, $weight_array, $pa_key, $pa_value, $product['id_lang'], $product['iso_code']);
}
}
} else {
Search::fillProductArray($product_array, $weight_array, $key, $value, $product['id_lang'], $product['iso_code']);
}
}
// If we find words that need to be indexed, they're added to the word table in the database
if (is_array($product_array) && !empty($product_array)) {
$query_array = $query_array2 = array();
foreach ($product_array as $word => $weight) {
if ($weight) {
$query_array[$word] = '(' . (int) $product['id_lang'] . ', ' . (int) $product['id_shop'] . ', \'' . pSQL($word) . '\')';
$query_array2[] = '\'' . pSQL($word) . '\'';
}
}
if (is_array($query_array) && !empty($query_array)) {
// The words are inserted...
$db->execute('
INSERT IGNORE INTO ' . _DB_PREFIX_ . 'search_word (id_lang, id_shop, word)
VALUES ' . implode(',', $query_array), false);
}
$word_ids_by_word = array();
if (is_array($query_array2) && !empty($query_array2)) {
// ...then their IDs are retrieved
$added_words = $db->executeS('
SELECT sw.id_word, sw.word
FROM ' . _DB_PREFIX_ . 'search_word sw
WHERE sw.word IN (' . implode(',', $query_array2) . ')
AND sw.id_lang = ' . (int) $product['id_lang'] . '
AND sw.id_shop = ' . (int) $product['id_shop'], true, false);
foreach ($added_words as $word_id) {
$word_ids_by_word['_' . $word_id['word']] = (int) $word_id['id_word'];
}
}
}
foreach ($product_array as $word => $weight) {
if (!$weight) {
continue;
}
if (!isset($word_ids_by_word['_' . $word])) {
continue;
}
$id_word = $word_ids_by_word['_' . $word];
if (!$id_word) {
continue;
}
$query_array3[] = '(' . (int) $product['id_product'] . ',' .
(int) $id_word . ',' . (int) $weight . ')';
// Force save every 200 words in order to avoid overloading MySQL
if (++$count_words % 200 == 0) {
Search::saveIndex($query_array3);
}
}
$products_array[] = (int) $product['id_product'];
}
$products_array = array_unique($products_array);
Search::setProductsAsIndexed($products_array);
// One last save is done at the end in order to save what's left
Search::saveIndex($query_array3);
}
return true;
}
public static function removeProductsSearchIndex($products)
{
if (is_array($products) && !empty($products)) {
Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'search_index WHERE id_product IN (' . implode(',', array_unique(array_map('intval', $products))) . ')');
ObjectModel::updateMultishopTable('Product', array('indexed' => 0), 'a.id_product IN (' . implode(',', array_map('intval', $products)) . ')');
}
}
protected static function setProductsAsIndexed(&$products)
{
if (is_array($products) && !empty($products)) {
ObjectModel::updateMultishopTable('Product', array('indexed' => 1), 'a.id_product IN (' . implode(',', array_map('intval', $products)) . ')');
}
}
/** $queryArray3 is automatically emptied in order to be reused immediatly */
protected static function saveIndex(&$queryArray3)
{
if (is_array($queryArray3) && !empty($queryArray3)) {
$query = 'INSERT INTO ' . _DB_PREFIX_ . 'search_index (id_product, id_word, weight)
VALUES ' . implode(',', $queryArray3) . '
ON DUPLICATE KEY UPDATE weight = weight + VALUES(weight)';
Db::getInstance()->execute($query, false);
}
$queryArray3 = array();
}
public static function searchTag(
$id_lang,
$tag,
$count = false,
$pageNumber = 0,
$pageSize = 10,
$orderBy = false,
$orderWay = false,
$useCookie = true,
Context $context = null
) {
if (!$context) {
$context = Context::getContext();
}
// Only use cookie if id_customer is not present
if ($useCookie) {
$id_customer = (int) $context->customer->id;
} else {
$id_customer = 0;
}
if (!is_numeric($pageNumber) || !is_numeric($pageSize) || !Validate::isBool($count) || !Validate::isValidSearch($tag)
|| $orderBy && !$orderWay || ($orderBy && !Validate::isOrderBy($orderBy)) || ($orderWay && !Validate::isOrderBy($orderWay))) {
return false;
}
if ($pageNumber < 1) {
$pageNumber = 1;
}
if ($pageSize < 1) {
$pageSize = 10;
}
$id = Context::getContext()->shop->id;
$id_shop = $id ? $id : Configuration::get('PS_SHOP_DEFAULT');
$sql_groups = '';
if (Group::isFeatureActive()) {
$groups = FrontController::getCurrentCustomerGroups();
$sql_groups = 'AND cg.`id_group` ' . (count($groups) ? 'IN (' . implode(',', $groups) . ')' : '=' . (int) Configuration::get('PS_UNIDENTIFIED_GROUP'));
}
if ($count) {
return (int) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
'SELECT COUNT(DISTINCT pt.`id_product`) nb
FROM
`' . _DB_PREFIX_ . 'tag` t
STRAIGHT_JOIN `' . _DB_PREFIX_ . 'product_tag` pt ON (pt.`id_tag` = t.`id_tag` AND t.`id_lang` = ' . (int) $id_lang . ')
STRAIGHT_JOIN `' . _DB_PREFIX_ . 'product` p ON (p.`id_product` = pt.`id_product`)
' . Shop::addSqlAssociation('product', 'p') . '
LEFT JOIN `' . _DB_PREFIX_ . 'category_product` cp ON (cp.`id_product` = p.`id_product`)
LEFT JOIN `' . _DB_PREFIX_ . 'category_shop` cs ON (cp.`id_category` = cs.`id_category` AND cs.`id_shop` = ' . (int) $id_shop . ')
' . (Group::isFeatureActive() ? 'LEFT JOIN `' . _DB_PREFIX_ . 'category_group` cg ON (cg.`id_category` = cp.`id_category`)' : '') . '
WHERE product_shop.`active` = 1
AND product_shop.`visibility` IN (\'both\', \'search\')
AND cs.`id_shop` = ' . (int) Context::getContext()->shop->id . '
' . $sql_groups . '
AND t.`name` LIKE \'%' . pSQL($tag) . '%\''
);
}
$sql = 'SELECT DISTINCT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, pl.`description_short`, pl.`link_rewrite`, pl.`name`, pl.`available_now`, pl.`available_later`,
MAX(image_shop.`id_image`) id_image, il.`legend`, m.`name` manufacturer_name, 1 position,
DATEDIFF(
p.`date_add`,
DATE_SUB(
"' . date('Y-m-d') . ' 00:00:00",
INTERVAL ' . (Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20) . ' DAY
)
) > 0 new
FROM
`' . _DB_PREFIX_ . 'tag` t
STRAIGHT_JOIN `' . _DB_PREFIX_ . 'product_tag` pt ON (pt.`id_tag` = t.`id_tag` AND t.`id_lang` = ' . (int) $id_lang . ')
STRAIGHT_JOIN `' . _DB_PREFIX_ . 'product` p ON (p.`id_product` = pt.`id_product`)
INNER JOIN `' . _DB_PREFIX_ . 'product_lang` pl ON (
p.`id_product` = pl.`id_product`
AND pl.`id_lang` = ' . (int) $id_lang . Shop::addSqlRestrictionOnLang('pl') . '
)
' . Shop::addSqlAssociation('product', 'p', false) . '
LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_shop` product_attribute_shop
ON (p.`id_product` = product_attribute_shop.`id_product` AND product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop=' . (int) $context->shop->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'image_shop` image_shop
ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop=' . (int) $context->shop->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $id_lang . ')
LEFT JOIN `' . _DB_PREFIX_ . 'manufacturer` m ON (m.`id_manufacturer` = p.`id_manufacturer`)
LEFT JOIN `' . _DB_PREFIX_ . 'category_product` cp ON (cp.`id_product` = p.`id_product`)
' . (Group::isFeatureActive() ? 'LEFT JOIN `' . _DB_PREFIX_ . 'category_group` cg ON (cg.`id_category` = cp.`id_category`)' : '') . '
LEFT JOIN `' . _DB_PREFIX_ . 'category_shop` cs ON (cp.`id_category` = cs.`id_category` AND cs.`id_shop` = ' . (int) $id_shop . ')
' . Product::sqlStock('p', 0) . '
WHERE product_shop.`active` = 1
AND product_shop.`visibility` IN (\'both\', \'search\')
AND cs.`id_shop` = ' . (int) Context::getContext()->shop->id . '
' . $sql_groups . '
AND t.`name` LIKE \'%' . pSQL($tag) . '%\'
GROUP BY product_shop.id_product
ORDER BY position DESC' . ($orderBy ? ', ' . $orderBy : '') . ($orderWay ? ' ' . $orderWay : '') . '
LIMIT ' . (int) (($pageNumber - 1) * $pageSize) . ',' . (int) $pageSize;
if (!$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql, true, false)) {
return false;
}
return Product::getProductsProperties((int) $id_lang, $result);
}
/**
* Prepare a word for the SQL requests (Remove hyphen if present, add percentage signs).
*
* @internal Public for tests
*
* @param string $word
*
* @return string
*/
public static function getSearchParamFromWord($word)
{
$word = str_replace(array('%', '_'), array('\\%', '\\_'), $word);
$start_search = Configuration::get('PS_SEARCH_START') ? '%' : '';
$end_search = Configuration::get('PS_SEARCH_END') ? '' : '%';
$start_pos = (int) ($word[0] == '-');
return $start_search . pSQL(Tools::substr($word, $start_pos, PS_SEARCH_MAX_WORD_LENGTH)) . $end_search;
}
}

82
classes/SearchEngine.php Normal file
View File

@@ -0,0 +1,82 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Class SearchEngineCore.
*/
class SearchEngineCore extends ObjectModel
{
public $server;
public $getvar;
/**
* @see ObjectModel::$definition
*/
public static $definition = array(
'table' => 'search_engine',
'primary' => 'id_search_engine',
'fields' => array(
'server' => array('type' => self::TYPE_STRING, 'validate' => 'isUrl', 'required' => true),
'getvar' => array('type' => self::TYPE_STRING, 'validate' => 'isModuleName', 'required' => true),
),
);
/**
* Get keywords.
*
* @param string $url
*
* @return bool|string
*/
public static function getKeywords($url)
{
$parsedUrl = @parse_url($url);
if (!isset($parsedUrl['host']) || !isset($parsedUrl['query'])) {
return false;
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT `server`, `getvar` FROM `' . _DB_PREFIX_ . 'search_engine`');
foreach ($result as $row) {
$host = &$row['server'];
$varname = &$row['getvar'];
if (strstr($parsedUrl['host'], $host)) {
$array = array();
preg_match('/[^a-z]' . $varname . '=.+\&/U', $parsedUrl['query'], $array);
if (empty($array[0])) {
preg_match('/[^a-z]' . $varname . '=.+$/', $parsedUrl['query'], $array);
}
if (empty($array[0])) {
return false;
}
$str = urldecode(str_replace('+', ' ', ltrim(substr(rtrim($array[0], '&'), strlen($varname) + 1), '=')));
if (!Validate::isMessage($str)) {
return false;
}
return $str;
}
}
}
}

View File

@@ -0,0 +1,131 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class Smarty_CacheResource_Mysql extends Smarty_CacheResource_Custom
{
/**
* fetch cached content and its modification time from data source.
*
* @param string $id unique cache content identifier
* @param string $name template name
* @param string $cache_id cache id
* @param string $compile_id compile id
* @param string $content cached content
* @param int $mtime cache modification timestamp (epoch)
*/
protected function fetch($id, $name, $cache_id, $compile_id, &$content, &$mtime)
{
$row = Db::getInstance()->getRow('SELECT modified, content FROM ' . _DB_PREFIX_ . 'smarty_cache WHERE id_smarty_cache = "' . pSQL($id, true) . '"');
if ($row) {
$content = $row['content'];
$mtime = strtotime($row['modified']);
} else {
$content = null;
$mtime = null;
}
}
/**
* Fetch cached content's modification timestamp from data source.
*
* @note implementing this method is optional. Only implement it if modification times can be accessed faster than loading the complete cached content.
*
* @param string $id unique cache content identifier
* @param string $name template name
* @param string $cache_id cache id
* @param string $compile_id compile id
*
* @return int|bool timestamp (epoch) the template was modified, or false if not found
*/
protected function fetchTimestamp($id, $name, $cache_id, $compile_id)
{
$value = Db::getInstance()->getValue('SELECT modified FROM ' . _DB_PREFIX_ . 'smarty_cache WHERE id_smarty_cache = "' . pSQL($id, true) . '"');
$mtime = strtotime($value);
return $mtime;
}
/**
* Save content to cache.
*
* @param string $id unique cache content identifier
* @param string $name template name
* @param string $cache_id cache id
* @param string $compile_id compile id
* @param int|null $exp_time seconds till expiration time in seconds or null
* @param string $content content to cache
*
* @return bool success
*/
protected function save($id, $name, $cache_id, $compile_id, $exp_time, $content)
{
Db::getInstance()->execute('
REPLACE INTO ' . _DB_PREFIX_ . 'smarty_cache (id_smarty_cache, name, cache_id, content)
VALUES (
"' . pSQL($id, true) . '",
"' . pSQL(sha1($name)) . '",
"' . pSQL($cache_id, true) . '",
"' . pSQL($content, true) . '"
)');
return (bool) Db::getInstance()->Affected_Rows();
}
/**
* Delete content from cache.
*
* @param string $name template name
* @param string $cache_id cache id
* @param string $compile_id compile id
* @param int|null $exp_time seconds till expiration or null
*
* @return int number of deleted caches
*/
protected function delete($name, $cache_id, $compile_id, $exp_time)
{
// delete the whole cache
if ($name === null && $cache_id === null && $compile_id === null && $exp_time === null) {
// returning the number of deleted caches would require a second query to count them
Db::getInstance()->execute('TRUNCATE TABLE ' . _DB_PREFIX_ . 'smarty_cache');
return -1;
}
$where = array();
if ($name !== null) {
$where[] = 'name = "' . pSQL(sha1($name)) . '"';
}
if ($exp_time !== null) {
$where[] = 'modified < DATE_SUB(NOW(), INTERVAL ' . (int) $exp_time . ' SECOND)';
}
if ($cache_id !== null) {
$where[] = '(cache_id = "' . pSQL($cache_id, true) . '" OR cache_id LIKE "' . pSQL($cache_id . '|%', true) . '")';
}
Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'smarty_cache WHERE ' . implode(' AND ', $where));
return Db::getInstance()->Affected_Rows();
}
}

View File

@@ -0,0 +1,301 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class SmartyCustomCore extends Smarty
{
public function __construct()
{
parent::__construct();
$this->template_class = 'SmartyCustomTemplate';
}
/**
* Delete compiled template file (lazy delete if resource_name is not specified).
*
* @param string $resource_name template name
* @param string $compile_id compile id
* @param int $exp_time expiration time
*
* @return int number of template files deleted
*/
public function clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null)
{
if ($resource_name == null) {
Db::getInstance()->execute('REPLACE INTO `' . _DB_PREFIX_ . 'smarty_last_flush` (`type`, `last_flush`) VALUES (\'compile\', FROM_UNIXTIME(' . time() . '))');
return 0;
}
return parent::clearCompiledTemplate($resource_name, $compile_id, $exp_time);
}
/**
* Mark all template files to be regenerated.
*
* @param int $exp_time expiration time
* @param string $type resource type
*
* @return int number of cache files which needs to be updated
*/
public function clearAllCache($exp_time = null, $type = null)
{
Db::getInstance()->execute('REPLACE INTO `' . _DB_PREFIX_ . 'smarty_last_flush` (`type`, `last_flush`) VALUES (\'template\', FROM_UNIXTIME(' . time() . '))');
return $this->delete_from_lazy_cache(null, null, null);
}
/**
* Mark file to be regenerated for a specific template.
*
* @param string $template_name template name
* @param string $cache_id cache id
* @param string $compile_id compile id
* @param int $exp_time expiration time
* @param string $type resource type
*
* @return int number of cache files which needs to be updated
*/
public function clearCache($template_name, $cache_id = null, $compile_id = null, $exp_time = null, $type = null)
{
return $this->delete_from_lazy_cache($template_name, $cache_id, $compile_id);
}
/**
* Check the compile cache needs to be invalidated (multi front + local cache compatible).
*/
public function check_compile_cache_invalidation()
{
static $last_flush = null;
if (!file_exists($this->getCompileDir() . 'last_flush')) {
@touch($this->getCompileDir() . 'last_flush', time());
} elseif (defined('_DB_PREFIX_')) {
if ($last_flush === null) {
$sql = 'SELECT UNIX_TIMESTAMP(last_flush) as last_flush FROM `' . _DB_PREFIX_ . 'smarty_last_flush` WHERE type=\'compile\'';
$last_flush = Db::getInstance()->getValue($sql, false);
}
if ((int) $last_flush && @filemtime($this->getCompileDir() . 'last_flush') < $last_flush) {
@touch($this->getCompileDir() . 'last_flush', time());
parent::clearCompiledTemplate();
}
}
}
/**
* {@inheritdoc}
*/
public function fetch($template = null, $cache_id = null, $compile_id = null, $parent = null, $display = false, $merge_tpl_vars = true, $no_output_filter = false)
{
$this->check_compile_cache_invalidation();
return parent::fetch($template, $cache_id, $compile_id, $parent, $display, $merge_tpl_vars, $no_output_filter);
}
/**
* {@inheritdoc}
*/
public function createTemplate($template, $cache_id = null, $compile_id = null, $parent = null, $do_clone = true)
{
$this->check_compile_cache_invalidation();
if ($this->caching) {
$this->check_template_invalidation($template, $cache_id, $compile_id);
}
return parent::createTemplate($template, $cache_id, $compile_id, $parent, $do_clone);
}
/**
* Handle the lazy template cache invalidation.
*
* @param string $template template name
* @param string $cache_id cache id
* @param string $compile_id compile id
*/
public function check_template_invalidation($template, $cache_id, $compile_id)
{
static $last_flush = null;
if (!file_exists($this->getCacheDir() . 'last_template_flush')) {
@touch($this->getCacheDir() . 'last_template_flush', time());
} elseif (defined('_DB_PREFIX_')) {
if ($last_flush === null) {
$sql = 'SELECT UNIX_TIMESTAMP(last_flush) as last_flush FROM `' . _DB_PREFIX_ . 'smarty_last_flush` WHERE type=\'template\'';
$last_flush = Db::getInstance()->getValue($sql, false);
}
if ((int) $last_flush && @filemtime($this->getCacheDir() . 'last_template_flush') < $last_flush) {
@touch($this->getCacheDir() . 'last_template_flush', time());
parent::clearAllCache();
} else {
if ($cache_id !== null && (is_object($cache_id) || is_array($cache_id))) {
$cache_id = null;
}
if ($this->is_in_lazy_cache($template, $cache_id, $compile_id) === false) {
// insert in cache before the effective cache creation to avoid nasty race condition
$this->insert_in_lazy_cache($template, $cache_id, $compile_id);
parent::clearCache($template, $cache_id, $compile_id);
}
}
}
}
/**
* Store the cache file path.
*
* @param string $filepath cache file path
* @param string $template template name
* @param string $cache_id cache id
* @param string $compile_id compile id
*/
public function update_filepath($filepath, $template, $cache_id, $compile_id)
{
$template_md5 = md5($template);
$sql = 'UPDATE `' . _DB_PREFIX_ . 'smarty_lazy_cache`
SET filepath=\'' . pSQL($filepath) . '\'
WHERE `template_hash`=\'' . pSQL($template_md5) . '\'';
$sql .= ' AND cache_id="' . pSQL((string) $cache_id) . '"';
if (strlen($compile_id) > 32) {
$compile_id = md5($compile_id);
}
$sql .= ' AND compile_id="' . pSQL((string) $compile_id) . '"';
Db::getInstance()->execute($sql, false);
}
/**
* Check if the current template is stored in the lazy cache
* Entry in the lazy cache = no need to regenerate the template.
*
* @param string $template template name
* @param string $cache_id cache id
* @param string $compile_id compile id
*
* @return bool
*/
public function is_in_lazy_cache($template, $cache_id, $compile_id)
{
static $is_in_lazy_cache = array();
$template_md5 = md5($template);
if (strlen($compile_id) > 32) {
$compile_id = md5($compile_id);
}
$key = md5($template_md5 . '-' . $cache_id . '-' . $compile_id);
if (isset($is_in_lazy_cache[$key])) {
return $is_in_lazy_cache[$key];
} else {
$sql = 'SELECT UNIX_TIMESTAMP(last_update) as last_update, filepath FROM `' . _DB_PREFIX_ . 'smarty_lazy_cache`
WHERE `template_hash`=\'' . pSQL($template_md5) . '\'';
$sql .= ' AND cache_id="' . pSQL((string) $cache_id) . '"';
$sql .= ' AND compile_id="' . pSQL((string) $compile_id) . '"';
$result = Db::getInstance()->getRow($sql, false);
// If the filepath is not yet set, it means the cache update is in progress in another process.
// In this case do not try to clear the cache again and tell to use the existing cache, if any
if ($result !== false && $result['filepath'] == '') {
// If the cache update is stalled for more than 1min, something should be wrong,
// remove the entry from the lazy cache
if ($result['last_update'] < time() - 60) {
$this->delete_from_lazy_cache($template, $cache_id, $compile_id);
}
$return = true;
} else {
if ($result === false
|| @filemtime($this->getCacheDir() . $result['filepath']) < $result['last_update']) {
$return = false;
} else {
$return = $result['filepath'];
}
}
$is_in_lazy_cache[$key] = $return;
}
return $return;
}
/**
* Insert the current template in the lazy cache.
*
* @param string $template template name
* @param string $cache_id cache id
* @param string $compile_id compile id
*
* @return bool
*/
public function insert_in_lazy_cache($template, $cache_id, $compile_id)
{
$template_md5 = md5($template);
$sql = 'INSERT IGNORE INTO `' . _DB_PREFIX_ . 'smarty_lazy_cache`
(`template_hash`, `cache_id`, `compile_id`, `last_update`)
VALUES (\'' . pSQL($template_md5) . '\'';
$sql .= ',"' . pSQL((string) $cache_id) . '"';
if (strlen($compile_id) > 32) {
$compile_id = md5($compile_id);
}
$sql .= ',"' . pSQL((string) $compile_id) . '"';
$sql .= ', FROM_UNIXTIME(' . time() . '))';
return Db::getInstance()->execute($sql, false);
}
/**
* Delete the current template from the lazy cache or the whole cache if no template name is given.
*
* @param string $template template name
* @param string $cache_id cache id
* @param string $compile_id compile id
*
* @return bool
*/
public function delete_from_lazy_cache($template, $cache_id, $compile_id)
{
if (!$template) {
return Db::getInstance()->execute('TRUNCATE TABLE `' . _DB_PREFIX_ . 'smarty_lazy_cache`', false);
}
$template_md5 = md5($template);
$sql = 'DELETE FROM `' . _DB_PREFIX_ . 'smarty_lazy_cache`
WHERE template_hash=\'' . pSQL($template_md5) . '\'';
if ($cache_id != null) {
$sql .= ' AND cache_id LIKE "' . pSQL((string) $cache_id) . '%"';
}
if ($compile_id != null) {
if (strlen($compile_id) > 32) {
$compile_id = md5($compile_id);
}
$sql .= ' AND compile_id="' . pSQL((string) $compile_id) . '"';
}
Db::getInstance()->execute($sql, false);
return Db::getInstance()->Affected_Rows();
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class SmartyCustomTemplateCore extends Smarty_Internal_Template
{
/** @var SmartyCustom|null */
public $smarty = null;
public function fetch($template = null, $cache_id = null, $compile_id = null, $parent = null, $display = false, $merge_tpl_vars = true, $no_output_filter = false)
{
if ($this->smarty->caching) {
$tpl = parent::fetch($template, $cache_id, $compile_id, $parent, $display, $merge_tpl_vars, $no_output_filter);
if (property_exists($this, 'cached')) {
$filepath = str_replace($this->smarty->getCacheDir(), '', $this->cached->filepath);
if ($this->smarty->is_in_lazy_cache($this->template_resource, $this->cache_id, $this->compile_id) != $filepath) {
$this->smarty->update_filepath($filepath, $this->template_resource, $this->cache_id, $this->compile_id);
}
}
return $tpl;
} else {
return parent::fetch($template, $cache_id, $compile_id, $parent, $display, $merge_tpl_vars, $no_output_filter);
}
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class SmartyDev extends Smarty
{
public function __construct()
{
parent::__construct();
$this->template_class = 'SmartyDevTemplate';
}
/**
* {@inheritdoc}
*/
public function fetch($template = null, $cache_id = null, $compile_id = null, $parent = null, $display = false, $merge_tpl_vars = true, $no_output_filter = false)
{
return "\n<!-- begin $template -->\n"
. parent::fetch($template, $cache_id, $compile_id, $parent, $display, $merge_tpl_vars, $no_output_filter)
. "\n<!-- end $template -->\n";
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class SmartyDevTemplateCore extends Smarty_Internal_Template
{
/** @var SmartyCustom|null */
public $smarty = null;
public function fetch($template = null, $cache_id = null, $compile_id = null, $parent = null, $display = false, $merge_tpl_vars = true, $no_output_filter = false)
{
if (null !== $template) {
$tpl = $template->template_resource;
} else {
$tpl = $this->template_resource;
}
return "\n<!-- begin $tpl -->\n"
. parent::fetch($template, $cache_id, $compile_id, $parent, $display, $merge_tpl_vars, $no_output_filter)
. "\n<!-- end $tpl -->\n";
}
}

View File

@@ -0,0 +1,97 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Used to delay loading of external classes with smarty->register_plugin.
*/
class SmartyLazyRegister
{
protected $registry = array();
protected static $instances = array();
/**
* Register a function or method to be dynamically called later.
*
* @param string|array $params function name or array(object name, method name)
*/
public function register($params)
{
if (is_array($params)) {
$this->registry[$params[1]] = $params;
} else {
$this->registry[$params] = $params;
}
}
public function isRegistered($params)
{
if (is_array($params)) {
$params = $params[1];
}
return isset($this->registry[$params]);
}
/**
* Dynamically call static function or method.
*
* @param string $name function name
* @param mixed $arguments function argument
*
* @return mixed function return
*/
public function __call($name, $arguments)
{
$item = $this->registry[$name];
// case 1: call to static method - case 2 : call to static function
if (is_array($item[1])) {
return call_user_func_array($item[1] . '::' . $item[0], array($arguments[0], &$arguments[1]));
} else {
$args = array();
foreach ($arguments as $a => $argument) {
if ($a == 0) {
$args[] = $arguments[0];
} else {
$args[] = &$arguments[$a];
}
}
return call_user_func_array($item, $args);
}
}
public static function getInstance($smarty)
{
$hash = spl_object_hash($smarty);
if (!isset(self::$instances[$hash])) {
self::$instances[$hash] = new self();
}
return self::$instances[$hash];
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Override module templates easily.
*
* @since 1.7.0.0
*/
class SmartyResourceModuleCore extends Smarty_Resource_Custom
{
public function __construct(array $paths, $isAdmin = false)
{
$this->paths = $paths;
$this->isAdmin = $isAdmin;
}
/**
* Fetch a template.
*
* @param string $name template name
* @param string $source template source
* @param int $mtime template modification timestamp (epoch)
*/
protected function fetch($name, &$source, &$mtime)
{
foreach ($this->paths as $path) {
if (Tools::file_exists_cache($file = $path . $name)) {
if (_PS_MODE_DEV_) {
$source = implode('', array(
'<!-- begin ' . $file . ' -->',
file_get_contents($file),
'<!-- end ' . $file . ' -->',
));
} else {
$source = file_get_contents($file);
}
$mtime = filemtime($file);
return;
}
}
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Override module templates easily.
*
* @since 1.7.0.0
*/
class SmartyResourceParentCore extends Smarty_Resource_Custom
{
public function __construct(array $paths)
{
$this->paths = $paths;
}
/**
* Fetch a template.
*
* @param string $name template name
* @param string $source template source
* @param int $mtime template modification timestamp (epoch)
*/
protected function fetch($name, &$source, &$mtime)
{
foreach ($this->paths as $path) {
if (Tools::file_exists_cache($file = $path . $name)) {
if (_PS_MODE_DEV_) {
$source = implode('', array(
'<!-- begin ' . $file . ' -->',
file_get_contents($file),
'<!-- end ' . $file . ' -->',
));
} else {
$source = file_get_contents($file);
}
$mtime = filemtime($file);
return;
}
}
}
}

View File

@@ -0,0 +1,208 @@
<?php
/**
* 2007-2019 PrestaShop and Contributors
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/OSL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2019 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
/**
* Determine the best existing template.
*
* @since 1.7.0.0
*/
class TemplateFinderCore
{
private $directories;
private $extension;
private $productListEntities = array('category', 'manufacturer', 'supplier');
private $productListSearchEntities = array('search', 'price-drop', 'best-sale');
private $productEntities = array('product');
private $brandListEntities = array('manufacturers', 'suppliers');
public function __construct(array $directories, $extension)
{
$this->directories = $directories;
$this->extension = $extension;
}
public function getTemplate($template, $entity, $id, $locale)
{
$locale = (Validate::isLocale($locale)) ? $locale : '';
$templates = $this->getTemplateHierarchy($template, $entity, $id);
foreach ($this->directories as $dir) {
foreach ($templates as $tpl) {
if (!empty($locale) && is_file($dir . $locale . DIRECTORY_SEPARATOR . $tpl . $this->extension)) {
return $locale . DIRECTORY_SEPARATOR . $tpl . $this->extension;
}
if (is_file($dir . $tpl . $this->extension)) {
return $tpl . $this->extension;
}
if (is_file($dir . $tpl) && false !== strpos($tpl, $this->extension)) {
return $tpl;
}
}
}
throw new PrestaShopException('No template found for ' . $template);
}
private function getTemplateHierarchy($template, $entity, $id)
{
$entity = basename($entity);
$id = (int) $id;
if (in_array($entity, $this->getProductListEntities())) {
$templates = array(
'catalog/listing/' . $entity . '-' . $id,
'catalog/listing/' . $entity,
$template,
'catalog/listing/product-list',
);
} elseif (in_array($entity, $this->getProductListSearchEntities())) {
$templates = array(
'catalog/listing/' . $entity,
$template,
'catalog/listing/product-list',
);
} elseif (in_array($entity, $this->getProductEntities())) {
$templates = array(
'catalog/' . $entity . '-' . $id,
$template,
'catalog/product',
);
} elseif (in_array($entity, $this->getBrandListEntities())) {
$templates = array(
$template,
'catalog/brands',
);
} elseif ('cms' === $entity) {
$templates = array(
'cms/page-' . $id,
$template,
'cms/page',
);
} else {
$templates = array($template);
}
return array_unique($templates);
}
/**
* Get productListEntities.
*
* @return array
*/
public function getProductListEntities()
{
return $this->productListEntities;
}
/**
* Set productListEntities.
*
* @param array $productListEntities
*
* @return TemplateFinderCore
*/
public function setProductListEntities($productListEntities)
{
$this->productListEntities = $productListEntities;
return $this;
}
/**
* Get productListSearch.
*
* @return array
*/
public function getProductListSearchEntities()
{
return $this->productListSearchEntities;
}
/**
* Set productListSearch.
*
* @param array $productListSearch
*
* @return TemplateFinderCore
*/
public function setProductListSearchEntities($productListSearchEntities)
{
$this->productListSearchEntities = $productListSearchEntities;
return $this;
}
/**
* Get productEntities.
*
* @return array
*/
public function getProductEntities()
{
return $this->productEntities;
}
/**
* Set productEntities.
*
* @param array $productEntities
*
* @return TemplateFinderCore
*/
public function setProductEntities($productEntities)
{
$this->productEntities = $productEntities;
return $this;
}
/**
* Get brandListEntities.
*
* @return array
*/
public function getBrandListEntities()
{
return $this->brandListEntities;
}
/**
* Set brandListEntities.
*
* @param array $brandListEntities
*
* @return TemplateFinderCore
*/
public function setBrandListEntities($brandListEntities)
{
$this->brandListEntities = $brandListEntities;
return $this;
}
}

Some files were not shown because too many files have changed in this diff Show More