first commit
This commit is contained in:
10
classes/.htaccess
Normal file
10
classes/.htaccess
Normal 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
436
classes/Access.php
Normal file
@@ -0,0 +1,436 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'access',
|
||||
'primary' => 'id_profile',
|
||||
'fields' => [
|
||||
'id_profile' => ['type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false],
|
||||
'id_authorization_role' => ['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 = [
|
||||
'add' => 'CREATE',
|
||||
'view' => 'READ',
|
||||
'edit' => 'UPDATE',
|
||||
'configure' => 'UPDATE',
|
||||
'delete' => 'DELETE',
|
||||
'uninstall' => 'DELETE',
|
||||
'duplicate' => ['CREATE', 'UPDATE'],
|
||||
'all' => ['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 = [];
|
||||
|
||||
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 = [];
|
||||
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 = [];
|
||||
|
||||
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 = [];
|
||||
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';
|
||||
}
|
||||
}
|
||||
622
classes/Address.php
Normal file
622
classes/Address.php
Normal file
@@ -0,0 +1,622 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
/** @var array Country IDs cache */
|
||||
protected static $_idCountries = [];
|
||||
|
||||
/**
|
||||
* @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 = [
|
||||
'table' => 'address',
|
||||
'primary' => 'id_address',
|
||||
'fields' => [
|
||||
'id_customer' => ['type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false],
|
||||
'id_manufacturer' => ['type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false],
|
||||
'id_supplier' => ['type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false],
|
||||
'id_warehouse' => ['type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId', 'copy_post' => false],
|
||||
'id_country' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_state' => ['type' => self::TYPE_INT, 'validate' => 'isNullOrUnsignedId'],
|
||||
'alias' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 32],
|
||||
'company' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 255],
|
||||
'lastname' => ['type' => self::TYPE_STRING, 'validate' => 'isName', 'required' => true, 'size' => 255],
|
||||
'firstname' => ['type' => self::TYPE_STRING, 'validate' => 'isName', 'required' => true, 'size' => 255],
|
||||
'vat_number' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName'],
|
||||
'address1' => ['type' => self::TYPE_STRING, 'validate' => 'isAddress', 'required' => true, 'size' => 128],
|
||||
'address2' => ['type' => self::TYPE_STRING, 'validate' => 'isAddress', 'size' => 128],
|
||||
'postcode' => ['type' => self::TYPE_STRING, 'validate' => 'isPostCode', 'size' => 12],
|
||||
'city' => ['type' => self::TYPE_STRING, 'validate' => 'isCityName', 'required' => true, 'size' => 64],
|
||||
'other' => ['type' => self::TYPE_STRING, 'validate' => 'isMessage', 'size' => 300],
|
||||
'phone' => ['type' => self::TYPE_STRING, 'validate' => 'isPhoneNumber', 'size' => 32],
|
||||
'phone_mobile' => ['type' => self::TYPE_STRING, 'validate' => 'isPhoneNumber', 'size' => 32],
|
||||
'dni' => ['type' => self::TYPE_STRING, 'validate' => 'isDniLite', 'size' => 16],
|
||||
'deleted' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'copy_post' => false],
|
||||
'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate', 'copy_post' => false],
|
||||
'date_upd' => ['type' => self::TYPE_DATE, 'validate' => 'isDate', 'copy_post' => false],
|
||||
],
|
||||
];
|
||||
|
||||
/** @var array Web service parameters */
|
||||
protected $webserviceParameters = [
|
||||
'objectsNodeName' => 'addresses',
|
||||
'fields' => [
|
||||
'id_customer' => ['xlink_resource' => 'customers'],
|
||||
'id_manufacturer' => ['xlink_resource' => 'manufacturers'],
|
||||
'id_supplier' => ['xlink_resource' => 'suppliers'],
|
||||
'id_warehouse' => ['xlink_resource' => 'warehouse'],
|
||||
'id_country' => ['xlink_resource' => 'countries'],
|
||||
'id_state' => ['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 = [];
|
||||
static::$_idCountries = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @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'] = [];
|
||||
}
|
||||
|
||||
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', ['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);
|
||||
|
||||
if (empty($result['id_zone_state']) && empty($result['id_zone'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self::$_idZones[$id_address] = !empty($result['id_zone_state'])
|
||||
? (int) $result['id_zone_state']
|
||||
: (int) $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 = [], $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 (!$this->deleted && 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)
|
||||
{
|
||||
if ($id_address <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
|
||||
'SELECT `id_address`
|
||||
FROM ' . _DB_PREFIX_ . 'address a
|
||||
WHERE a.`id_address` = ' . (int) $id_address,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ObjectModel::getFieldsRequiredDB();
|
||||
*/
|
||||
public function getFieldsRequiredDB()
|
||||
{
|
||||
return parent::getCachedFieldsRequiredDatabase();
|
||||
}
|
||||
}
|
||||
56
classes/AddressChecksumCore.php
Normal file
56
classes/AddressChecksumCore.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
691
classes/AddressFormat.php
Normal file
691
classes/AddressFormat.php
Normal file
@@ -0,0 +1,691 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
use PrestaShop\PrestaShop\Core\Domain\Address\Exception\AddressException;
|
||||
|
||||
/**
|
||||
* Class AddressFormatCore.
|
||||
*/
|
||||
class AddressFormatCore extends ObjectModel
|
||||
{
|
||||
const FORMAT_NEW_LINE = "\n";
|
||||
|
||||
/** @var int Address format */
|
||||
public $id_address_format;
|
||||
|
||||
/** @var int Country ID */
|
||||
public $id_country;
|
||||
|
||||
/** @var string Format */
|
||||
public $format;
|
||||
|
||||
protected $_errorFormatList = [];
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'address_format',
|
||||
'primary' => 'id_country',
|
||||
'fields' => [
|
||||
'format' => ['type' => self::TYPE_HTML, 'validate' => 'isGenericName', 'required' => true],
|
||||
'id_country' => ['type' => self::TYPE_INT],
|
||||
],
|
||||
];
|
||||
|
||||
/** @var array Default required form fields list */
|
||||
public static $requireFormFieldsList = [
|
||||
'firstname',
|
||||
'lastname',
|
||||
'address1',
|
||||
'city',
|
||||
'Country:name',
|
||||
];
|
||||
|
||||
/** @var array Default forbidden property list */
|
||||
public static $forbiddenPropertyList = [
|
||||
'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 = [
|
||||
'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.', [], '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.', [], '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.', [], '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.', [], 'Admin.Notifications.Error') . ': ' .
|
||||
$associationName[0];
|
||||
}
|
||||
} elseif ($totalNameUsed == 2) {
|
||||
if (empty($associationName[0]) || empty($associationName[1])) {
|
||||
$this->_errorFormatList[] = $this->trans('Syntax error with this pattern.', [], '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.', [], '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 = [];
|
||||
$fieldsValidate = Address::getFieldsValidate();
|
||||
$usedKeyList = [];
|
||||
|
||||
$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.', [], 'Admin.Notifications.Error') .
|
||||
': ' . $patternName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->checkRequiredFields($usedKeyList);
|
||||
}
|
||||
|
||||
return (count($this->_errorFormatList)) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that all required fields exist in a given fields list.
|
||||
* Fills _errorFormatList array in case of absence of a required field.
|
||||
*
|
||||
* @param array $fieldList
|
||||
*/
|
||||
protected function checkRequiredFields($fieldList)
|
||||
{
|
||||
foreach (self::getFieldsRequired() as $requiredField) {
|
||||
if (!in_array($requiredField, $fieldList)) {
|
||||
$this->_errorFormatList[] = $this->trans(
|
||||
'The %s field (in tab %s) is required.',
|
||||
[$requiredField, $this->getFieldTabName($requiredField)],
|
||||
'Admin.Notifications.Error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a field name, get the name of the tab in which the field name can be found.
|
||||
* For ex: Country:name => the tab is 'Country'.
|
||||
* There should be only one separator in the string, otherwise throw an exception.
|
||||
*
|
||||
* @param string $field
|
||||
*
|
||||
* @return bool|string
|
||||
*
|
||||
* @throws AddressException
|
||||
*/
|
||||
private function getFieldTabName($field)
|
||||
{
|
||||
if (strpos($field, ':') === false) {
|
||||
// When there is no ':' separator, the field is in the Address tab
|
||||
return 'Address';
|
||||
}
|
||||
|
||||
$fieldTab = explode(':', $field);
|
||||
if (count($fieldTab) === 2) {
|
||||
// The part preceding the ':' separator is the name of the tab in which there is the required field
|
||||
return $fieldTab[0];
|
||||
}
|
||||
|
||||
throw new AddressException('Address format field is not valid');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
$temporyObject = [];
|
||||
|
||||
// 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);
|
||||
|
||||
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 = [], $newLine = self::FORMAT_NEW_LINE, $separator = ' ', $style = [])
|
||||
{
|
||||
$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'] : []),
|
||||
(isset($params['newLine']) ? $params['newLine'] : self::FORMAT_NEW_LINE),
|
||||
(isset($params['separator']) ? $params['separator'] : ' '),
|
||||
(isset($params['style']) ? $params['style'] : [])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
|
||||
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 = [];
|
||||
|
||||
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 = [];
|
||||
$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 = [];
|
||||
|
||||
if ($address && $address instanceof Address) {
|
||||
$layoutData['ordered'] = AddressFormat::getOrderedAddressFields((int) $address->id_country);
|
||||
$layoutData['formated'] = AddressFormat::getFormattedAddressFieldsValues($address, $layoutData['ordered']);
|
||||
$layoutData['object'] = [];
|
||||
|
||||
$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
171
classes/Alias.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class AliasCore.
|
||||
*/
|
||||
class AliasCore extends ObjectModel
|
||||
{
|
||||
public $alias;
|
||||
public $search;
|
||||
public $active = true;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'alias',
|
||||
'primary' => 'id_alias',
|
||||
'fields' => [
|
||||
'search' => ['type' => self::TYPE_STRING, 'validate' => 'isValidSearch', 'required' => true, 'size' => 255],
|
||||
'alias' => ['type' => self::TYPE_STRING, 'validate' => 'isValidSearch', 'required' => true, 'size' => 255],
|
||||
'active' => ['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, false);
|
||||
|
||||
return isset($row['id_alias']);
|
||||
}
|
||||
}
|
||||
356
classes/Attachment.php
Normal file
356
classes/Attachment.php
Normal file
@@ -0,0 +1,356 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'attachment',
|
||||
'primary' => 'id_attachment',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'file' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 40],
|
||||
'mime' => ['type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'required' => true, 'size' => 128],
|
||||
'file_name' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 128],
|
||||
'file_size' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId'],
|
||||
|
||||
/* Lang fields */
|
||||
'name' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 32],
|
||||
'description' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isCleanHtml'],
|
||||
],
|
||||
'associations' => [
|
||||
'products' => ['type' => self::HAS_MANY, 'field' => 'id_product', 'object' => 'Product', 'association' => 'product_attachment'],
|
||||
],
|
||||
];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'objectNodeNames' => 'attachments',
|
||||
'hidden_fields' => [],
|
||||
'fields' => [
|
||||
'file' => [],
|
||||
'file_name' => [],
|
||||
'file_size' => [],
|
||||
'mime' => [],
|
||||
],
|
||||
'associations' => [
|
||||
'products' => [
|
||||
'resource' => 'product',
|
||||
'api' => 'products',
|
||||
'fields' => [
|
||||
'id' => ['required' => true],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* @see ObjectModel::add()
|
||||
*/
|
||||
public function add($autoDate = true, $nullValues = false)
|
||||
{
|
||||
if (file_exists(_PS_DOWNLOAD_DIR_ . $this->file)) {
|
||||
$this->file_size = filesize(_PS_DOWNLOAD_DIR_ . $this->file);
|
||||
}
|
||||
|
||||
return parent::add($autoDate, $nullValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ObjectModel::update()
|
||||
*/
|
||||
public function update($nullValues = false)
|
||||
{
|
||||
if (file_exists(_PS_DOWNLOAD_DIR_ . $this->file)) {
|
||||
$this->file_size = filesize(_PS_DOWNLOAD_DIR_ . $this->file);
|
||||
}
|
||||
|
||||
return parent::update($nullValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ObjectModel::delete()
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
if (file_exists(_PS_DOWNLOAD_DIR_ . $this->file)) {
|
||||
@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 . '
|
||||
)'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unassociate all products from the current object
|
||||
*
|
||||
* @param bool $updateAttachmentCache [default=true] If set to false attachment cache will not be updated
|
||||
*
|
||||
* @return bool Deletion result
|
||||
*/
|
||||
public function deleteAttachments(bool $updateAttachmentCache = true): bool
|
||||
{
|
||||
if (0 >= (int) $this->id) {
|
||||
// Can not delete attachement without id
|
||||
return false;
|
||||
}
|
||||
|
||||
$res = Db::getInstance()->execute(
|
||||
'DELETE FROM `' . _DB_PREFIX_ . 'product_attachment` ' .
|
||||
'WHERE `id_attachment` = ' . (int) $this->id
|
||||
);
|
||||
|
||||
if ($updateAttachmentCache === true) {
|
||||
$productIds = Db::getInstance()->executeS(
|
||||
'SELECT `id_product` FROM `' . _DB_PREFIX_ . 'product_attachment` ' .
|
||||
'WHERE `id_attachment` = ' . (int) $this->id
|
||||
);
|
||||
|
||||
foreach ($productIds as $productId) {
|
||||
Product::updateCacheAttachment((int) $productId);
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
return static::associateProductAttachment((int) $idProduct, (int) $this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $productId
|
||||
* @param int $attachmentId
|
||||
*
|
||||
* @return bool true if success
|
||||
*/
|
||||
public static function associateProductAttachment(int $productId, int $attachmentId): bool
|
||||
{
|
||||
$res = Db::getInstance()->execute('
|
||||
INSERT INTO ' . _DB_PREFIX_ . 'product_attachment
|
||||
(id_attachment, id_product) VALUES
|
||||
(' . $attachmentId . ', ' . $productId . ')');
|
||||
|
||||
Product::updateCacheAttachment($productId);
|
||||
|
||||
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 = [];
|
||||
foreach ($array as $idAttachment) {
|
||||
if ((int) $idAttachment > 0) {
|
||||
$ids[] = ['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)
|
||||
{
|
||||
if (!is_array($list)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$idsAttachments = array_column($list, '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 = [];
|
||||
foreach ($tmp as $t) {
|
||||
$productAttachments[$t['id_attachment']][] = $t['name'];
|
||||
}
|
||||
|
||||
return $productAttachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attachment products ids of current attachment for association.
|
||||
*
|
||||
* @return array<int, array{ id: string }> An array of product ids
|
||||
*/
|
||||
public function getWsProducts(): array
|
||||
{
|
||||
return Db::getInstance()->executeS(
|
||||
'SELECT p.`id_product` AS id ' .
|
||||
'FROM `' . _DB_PREFIX_ . 'product_attachment` pa ' .
|
||||
'INNER JOIN `' . _DB_PREFIX_ . 'product` p ON (p.id_product = pa.id_product) ' .
|
||||
'' . Shop::addSqlAssociation('product', 'p') . ' ' .
|
||||
'WHERE pa.`id_attachment` = ' . (int) $this->id
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set products ids of current attachment for association.
|
||||
*
|
||||
* @param array<int, array{id: int|string }> $products Products ids
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setWsProducts(array $products): bool
|
||||
{
|
||||
$this->deleteAttachments(true);
|
||||
foreach ($products as $product) {
|
||||
Db::getInstance()->execute('INSERT INTO `' . _DB_PREFIX_ . 'product_attachment` (`id_product`, `id_attachment`) VALUES (' . (int) $product['id'] . ', ' . (int) $this->id . ')');
|
||||
Product::updateCacheAttachment((int) $product['id']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
401
classes/Attribute.php
Normal file
401
classes/Attribute.php
Normal file
@@ -0,0 +1,401 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class AttributeCore.
|
||||
*/
|
||||
class AttributeCore extends ObjectModel
|
||||
{
|
||||
/** @var int Group id which attribute belongs */
|
||||
public $id_attribute_group;
|
||||
|
||||
/** @var string Name */
|
||||
public $name;
|
||||
/** @var string */
|
||||
public $color;
|
||||
/** @var int */
|
||||
public $position;
|
||||
/** @todo Find type */
|
||||
public $default;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'attribute',
|
||||
'primary' => 'id_attribute',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'id_attribute_group' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'color' => ['type' => self::TYPE_STRING, 'validate' => 'isColor'],
|
||||
'position' => ['type' => self::TYPE_INT, 'validate' => 'isInt'],
|
||||
|
||||
/* Lang fields */
|
||||
'name' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 128],
|
||||
],
|
||||
];
|
||||
|
||||
/** @var string */
|
||||
protected $image_dir = _PS_COL_IMG_DIR_;
|
||||
|
||||
/** @var array Web service parameters */
|
||||
protected $webserviceParameters = [
|
||||
'objectsNodeName' => 'product_option_values',
|
||||
'objectNodeName' => 'product_option_value',
|
||||
'fields' => [
|
||||
'id_attribute_group' => ['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 = [];
|
||||
|
||||
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', ['id_attribute' => $this->id]);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ObjectModel::update()
|
||||
*/
|
||||
public function update($nullValues = false)
|
||||
{
|
||||
$return = parent::update($nullValues);
|
||||
|
||||
if ($return) {
|
||||
Hook::exec('actionAttributeSave', ['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', ['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 [];
|
||||
}
|
||||
|
||||
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 [];
|
||||
}
|
||||
|
||||
$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
420
classes/AttributeGroup.php
Normal file
@@ -0,0 +1,420 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class AttributeGroupCore.
|
||||
*/
|
||||
class AttributeGroupCore extends ObjectModel
|
||||
{
|
||||
/** @var string Name */
|
||||
public $name;
|
||||
/** @var bool Whether the attribute group is a color group */
|
||||
public $is_color_group;
|
||||
/** @var int Position */
|
||||
public $position;
|
||||
/** @var string Group type */
|
||||
public $group_type;
|
||||
|
||||
/** @var string Public Name */
|
||||
public $public_name;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'attribute_group',
|
||||
'primary' => 'id_attribute_group',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'is_color_group' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'group_type' => ['type' => self::TYPE_STRING, 'required' => true],
|
||||
'position' => ['type' => self::TYPE_INT, 'validate' => 'isInt'],
|
||||
|
||||
/* Lang fields */
|
||||
'name' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 128],
|
||||
'public_name' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 64],
|
||||
],
|
||||
];
|
||||
|
||||
/** @var array Web service parameters */
|
||||
protected $webserviceParameters = [
|
||||
'objectsNodeName' => 'product_options',
|
||||
'objectNodeName' => 'product_option',
|
||||
'fields' => [],
|
||||
'associations' => [
|
||||
'product_option_values' => [
|
||||
'resource' => 'product_option_value',
|
||||
'fields' => [
|
||||
'id' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* 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', ['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', ['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 = [];
|
||||
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 = [];
|
||||
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', ['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 [];
|
||||
}
|
||||
|
||||
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 [];
|
||||
}
|
||||
|
||||
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 = [];
|
||||
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
365
classes/CMS.php
Normal file
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'cms',
|
||||
'primary' => 'id_cms',
|
||||
'multilang' => true,
|
||||
'multilang_shop' => true,
|
||||
'fields' => [
|
||||
'id_cms_category' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
|
||||
'position' => ['type' => self::TYPE_INT],
|
||||
'indexation' => ['type' => self::TYPE_BOOL],
|
||||
'active' => ['type' => self::TYPE_BOOL],
|
||||
|
||||
/* Lang fields */
|
||||
'meta_description' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 512],
|
||||
'meta_keywords' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255],
|
||||
'meta_title' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 255],
|
||||
'head_seo_title' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255],
|
||||
'link_rewrite' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isLinkRewrite', 'required' => true, 'size' => 128],
|
||||
'content' => ['type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml', 'size' => 3999999999999],
|
||||
],
|
||||
];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'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 . ' AND cl.id_shop = ' . (int) Context::getContext()->shop->id . ')
|
||||
' . 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 = [];
|
||||
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';
|
||||
}
|
||||
}
|
||||
715
classes/CMSCategory.php
Normal file
715
classes/CMSCategory.php
Normal file
@@ -0,0 +1,715 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
class CMSCategoryCore extends ObjectModel
|
||||
{
|
||||
public $id;
|
||||
|
||||
/** @var int CMSCategory ID */
|
||||
public $id_cms_category;
|
||||
|
||||
/** @var array<string> Name */
|
||||
public $name;
|
||||
|
||||
/** @var bool Status for display */
|
||||
public $active = 1;
|
||||
|
||||
/** @var array<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 array<string> string used in rewrited URL */
|
||||
public $link_rewrite;
|
||||
|
||||
/** @var array<string> Meta title */
|
||||
public $meta_title;
|
||||
|
||||
/** @var array<string> Meta keywords */
|
||||
public $meta_keywords;
|
||||
|
||||
/** @var array<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 = [];
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'cms_category',
|
||||
'primary' => 'id_cms_category',
|
||||
'multilang' => true,
|
||||
'multilang_shop' => true,
|
||||
'fields' => [
|
||||
'active' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true],
|
||||
'id_parent' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'required' => true],
|
||||
'position' => ['type' => self::TYPE_INT],
|
||||
'level_depth' => ['type' => self::TYPE_INT],
|
||||
'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
'date_upd' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
|
||||
/* Lang fields */
|
||||
'name' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isCatalogName', 'required' => true, 'size' => 64],
|
||||
'link_rewrite' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isLinkRewrite', 'required' => true, 'size' => 64],
|
||||
'description' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isCleanHtml'],
|
||||
'meta_title' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255],
|
||||
'meta_description' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 512],
|
||||
'meta_keywords' => ['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 = [];
|
||||
$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 [
|
||||
'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' : '') .
|
||||
' ORDER BY c.`position`';
|
||||
|
||||
$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 . ($active ? ' AND c.`active` = 1' : '') . '
|
||||
AND cl.`id_shop` = ' . (int) Context::getContext()->shop->id . '
|
||||
AND cl.`id_lang` = ' . (int) $id_lang . '
|
||||
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(' ', $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 = [];
|
||||
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 `position` 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 = [];
|
||||
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 = [(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
58
classes/CMSRole.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class CMSRoleCore.
|
||||
*/
|
||||
class CMSRoleCore extends ObjectModel
|
||||
{
|
||||
/** @var string name */
|
||||
public $name;
|
||||
/** @var int id_cms */
|
||||
public $id_cms;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'cms_role',
|
||||
'primary' => 'id_cms_role',
|
||||
'fields' => [
|
||||
'name' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 50],
|
||||
'id_cms' => ['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
113
classes/CSV.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Simple class to output CSV data
|
||||
* Uses CollectionCore.
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
class CSVCore
|
||||
{
|
||||
public $filename;
|
||||
public $collection;
|
||||
public $delimiter;
|
||||
|
||||
/**
|
||||
* Loads objects, filename and optionally 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(['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(['"', ';'], '', $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"');
|
||||
}
|
||||
}
|
||||
1718
classes/Carrier.php
Normal file
1718
classes/Carrier.php
Normal file
File diff suppressed because it is too large
Load Diff
5375
classes/Cart.php
Normal file
5375
classes/Cart.php
Normal file
File diff suppressed because it is too large
Load Diff
1895
classes/CartRule.php
Normal file
1895
classes/CartRule.php
Normal file
File diff suppressed because it is too large
Load Diff
2455
classes/Category.php
Normal file
2455
classes/Category.php
Normal file
File diff suppressed because it is too large
Load Diff
143
classes/Chart.php
Normal file
143
classes/Chart.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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 = [];
|
||||
|
||||
/** @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 = [];
|
||||
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.');
|
||||
}
|
||||
}
|
||||
}
|
||||
34
classes/ChecksumInterface.php
Normal file
34
classes/ChecksumInterface.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
interface ChecksumInterface
|
||||
{
|
||||
/**
|
||||
* @param $object checksum target
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateChecksum($object);
|
||||
}
|
||||
546
classes/Combination.php
Normal file
546
classes/Combination.php
Normal file
@@ -0,0 +1,546 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
use PrestaShopBundle\Translation\Translator;
|
||||
|
||||
/**
|
||||
* Class CombinationCore.
|
||||
*/
|
||||
class CombinationCore extends ObjectModel
|
||||
{
|
||||
/** @var int Product ID */
|
||||
public $id_product;
|
||||
|
||||
public $reference;
|
||||
|
||||
/** @var string */
|
||||
public $supplier_reference;
|
||||
|
||||
/**
|
||||
* @deprecated since 1.7.8
|
||||
* @see StockAvailable::$location instead
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $location = '';
|
||||
|
||||
public $ean13;
|
||||
|
||||
public $isbn;
|
||||
|
||||
public $upc;
|
||||
|
||||
public $mpn;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @deprecated since 1.7.8
|
||||
* @see StockAvailable::$quantity instead
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $quantity;
|
||||
|
||||
public $weight;
|
||||
|
||||
public $default_on;
|
||||
|
||||
public $available_date = '0000-00-00';
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'product_attribute',
|
||||
'primary' => 'id_product_attribute',
|
||||
'fields' => [
|
||||
'id_product' => ['type' => self::TYPE_INT, 'shop' => 'both', 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'location' => ['type' => self::TYPE_STRING, 'validate' => 'isString', 'size' => 255],
|
||||
'ean13' => ['type' => self::TYPE_STRING, 'validate' => 'isEan13', 'size' => 13],
|
||||
'isbn' => ['type' => self::TYPE_STRING, 'validate' => 'isIsbn', 'size' => 32],
|
||||
'upc' => ['type' => self::TYPE_STRING, 'validate' => 'isUpc', 'size' => 12],
|
||||
'mpn' => ['type' => self::TYPE_STRING, 'validate' => 'isMpn', 'size' => 40],
|
||||
'quantity' => ['type' => self::TYPE_INT, 'validate' => 'isInt', 'size' => 10],
|
||||
'reference' => ['type' => self::TYPE_STRING, 'size' => 64],
|
||||
'supplier_reference' => ['type' => self::TYPE_STRING, 'size' => 64],
|
||||
|
||||
/* Shop fields */
|
||||
'wholesale_price' => ['type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isNegativePrice', 'size' => 27],
|
||||
'price' => ['type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isNegativePrice', 'size' => 20],
|
||||
'ecotax' => ['type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isPrice', 'size' => 20],
|
||||
'weight' => ['type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isFloat'],
|
||||
'unit_price_impact' => ['type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isNegativePrice', 'size' => 20],
|
||||
'minimal_quantity' => ['type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'low_stock_threshold' => ['type' => self::TYPE_INT, 'shop' => true, 'allow_null' => true, 'validate' => 'isInt'],
|
||||
'low_stock_alert' => ['type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'],
|
||||
'default_on' => ['type' => self::TYPE_BOOL, 'allow_null' => true, 'shop' => true, 'validate' => 'isBool'],
|
||||
'available_date' => ['type' => self::TYPE_DATE, 'shop' => true, 'validate' => 'isDateFormat'],
|
||||
],
|
||||
];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'objectNodeName' => 'combination',
|
||||
'objectsNodeName' => 'combinations',
|
||||
'fields' => [
|
||||
'id_product' => ['required' => true, 'xlink_resource' => 'products'],
|
||||
],
|
||||
'associations' => [
|
||||
'product_option_values' => ['resource' => 'product_option_value'],
|
||||
'images' => ['resource' => 'image', 'api' => 'images/products'],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* @param int|null $id
|
||||
* @param int|null $id_lang
|
||||
* @param int|null $id_shop
|
||||
* @param Translator|null $translator
|
||||
*/
|
||||
public function __construct(?int $id = null, ?int $id_lang = null, ?int $id_shop = null, ?Translator $translator = null)
|
||||
{
|
||||
parent::__construct($id, $id_lang, $id_shop, $translator);
|
||||
$this->loadStockData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the variables used for stock management.
|
||||
*/
|
||||
public function loadStockData(): void
|
||||
{
|
||||
if (false === Validate::isLoadedObject($this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->quantity = StockAvailable::getQuantityAvailableByProduct($this->id_product, $this->id);
|
||||
$this->location = StockAvailable::getLocation($this->id_product, $this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
if (!$this->deleteCartProductCombination()) {
|
||||
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([(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()
|
||||
{
|
||||
if ((int) $this->id === 0) {
|
||||
return false;
|
||||
}
|
||||
$result = Db::getInstance()->delete('product_attribute_combination', '`id_product_attribute` = ' . (int) $this->id);
|
||||
$result &= Db::getInstance()->delete('product_attribute_image', '`id_product_attribute` = ' . (int) $this->id);
|
||||
|
||||
if ($result) {
|
||||
Hook::exec('actionAttributeCombinationDelete', ['id_product_attribute' => (int) $this->id]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete product combination from cart.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function deleteCartProductCombination(): bool
|
||||
{
|
||||
if ((int) $this->id === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Db::getInstance()->delete('cart_product', 'id_product_attribute = ' . (int) $this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $idsAttribute
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setAttributes($idsAttribute)
|
||||
{
|
||||
$result = $this->deleteAssociations();
|
||||
if ($result && !empty($idsAttribute)) {
|
||||
$sqlValues = [];
|
||||
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', ['id_product_attribute' => (int) $this->id, 'id_attributes' => $idsAttribute]);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setWsProductOptionValues($values)
|
||||
{
|
||||
$idsAttributes = [];
|
||||
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 = [];
|
||||
|
||||
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 = [];
|
||||
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 ean13 reference, returns the corresponding id.
|
||||
*
|
||||
* @param string $ean13
|
||||
*
|
||||
* @return int|string Product attribute identifier
|
||||
*/
|
||||
public static function getIdByEan13($ean13)
|
||||
{
|
||||
if (empty($ean13)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!Validate::isEan13($ean13)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$query = new DbQuery();
|
||||
$query->select('pa.id_product_attribute');
|
||||
$query->from('product_attribute', 'pa');
|
||||
$query->where('pa.ean13 = \'' . pSQL($ean13) . '\'');
|
||||
|
||||
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
);
|
||||
}
|
||||
}
|
||||
755
classes/Configuration.php
Normal file
755
classes/Configuration.php
Normal file
@@ -0,0 +1,755 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'configuration',
|
||||
'primary' => 'id_configuration',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'name' => ['type' => self::TYPE_STRING, 'validate' => 'isConfigName', 'required' => true, 'size' => 254],
|
||||
'id_shop_group' => ['type' => self::TYPE_NOTHING, 'validate' => 'isUnsignedId'],
|
||||
'id_shop' => ['type' => self::TYPE_NOTHING, 'validate' => 'isUnsignedId'],
|
||||
'value' => ['type' => self::TYPE_STRING],
|
||||
'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
'date_upd' => ['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 = [];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'fields' => [
|
||||
'value' => [],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* @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] = [
|
||||
'global' => [],
|
||||
'group' => [],
|
||||
'shop' => [],
|
||||
];
|
||||
}
|
||||
|
||||
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|false 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use Configuration::getConfigInMultipleLangs() instead
|
||||
*/
|
||||
public static function getInt($key, $idShopGroup = null, $idShop = null)
|
||||
{
|
||||
return self::getConfigInMultipleLangs($key, $idShopGroup, $idShop);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 getConfigInMultipleLangs($key, $idShopGroup = null, $idShop = null)
|
||||
{
|
||||
$resultsArray = [];
|
||||
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 = [];
|
||||
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 = [];
|
||||
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', [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 = [$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', [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 = [$values];
|
||||
}
|
||||
|
||||
if ($html) {
|
||||
$values = array_map(function ($v) {
|
||||
return Tools::purifyHTML($v);
|
||||
}, $values);
|
||||
}
|
||||
|
||||
$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'], [
|
||||
'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 = [
|
||||
'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 }}',
|
||||
[
|
||||
'{{ 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 }}"',
|
||||
[
|
||||
'{{ primary_key_column }}' => self::$definition['primary'],
|
||||
'{{ config_id }}' => $configID,
|
||||
'{{ update_date }}' => $now,
|
||||
'{{ value }}' => $sanitizedValue,
|
||||
]
|
||||
);
|
||||
$result &= Db::getInstance()->update($table, [
|
||||
'value' => $sanitizedValue,
|
||||
'date_upd' => date('Y-m-d H:i:s'),
|
||||
], $condition, 1, true);
|
||||
} else {
|
||||
$result &= Db::getInstance()->insert($table, [
|
||||
self::$definition['primary'] => $configID,
|
||||
'id_lang' => (int) $lang,
|
||||
'value' => $sanitizedValue,
|
||||
'date_upd' => $now,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Configuration::set($key, $values, $idShopGroup, $idShop);
|
||||
|
||||
return (bool) $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
|
||||
* @param int $idShopGroup
|
||||
* @param int $idShop
|
||||
*/
|
||||
public static function deleteFromContext($key, int $idShopGroup = null, int $idShop = null)
|
||||
{
|
||||
if (Shop::getContext() == Shop::CONTEXT_ALL) {
|
||||
return;
|
||||
}
|
||||
|
||||
$idShopGroup = $idShopGroup ?? Shop::getContextShopGroupID(true);
|
||||
if (!isset($idShop) && 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);
|
||||
}
|
||||
}
|
||||
314
classes/ConfigurationKPI.php
Normal file
314
classes/ConfigurationKPI.php
Normal file
@@ -0,0 +1,314 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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::getConfigInMultipleLangs($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 $key
|
||||
* @param int|null $idShopGroup
|
||||
* @param int|null $idShop
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function deleteFromContext($key, int $idShopGroup = null, int $idShop = null)
|
||||
{
|
||||
ConfigurationKPI::setKpiDefinition();
|
||||
$deleteSuccess = parent::deleteFromContext($key, $idShopGroup, $idShop);
|
||||
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;
|
||||
}
|
||||
}
|
||||
446
classes/ConfigurationTest.php
Normal file
446
classes/ConfigurationTest.php
Normal file
@@ -0,0 +1,446 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
class ConfigurationTestCore
|
||||
{
|
||||
public static $test_files = [
|
||||
'/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 = [
|
||||
'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, [
|
||||
'system' => [
|
||||
'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,
|
||||
'memory_limit' => false,
|
||||
'mbstring' => 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 [
|
||||
'new_phpversion' => false,
|
||||
'gz' => false,
|
||||
'mbstring' => false,
|
||||
'dom' => false,
|
||||
'pdo_mysql' => false,
|
||||
'fopen' => false,
|
||||
'intl' => false,
|
||||
'memory_limit' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* run all test defined in $tests.
|
||||
*
|
||||
* @param array $tests
|
||||
*
|
||||
* @return array results of tests
|
||||
*/
|
||||
public static function check($tests)
|
||||
{
|
||||
$res = [];
|
||||
foreach ($tests as $key => $test) {
|
||||
$res[$key] = ConfigurationTest::run($key, $test);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public static function run($ptr, $arg = 0)
|
||||
{
|
||||
if (call_user_func(['ConfigurationTest', 'test_' . $ptr], $arg)) {
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
return 'fail';
|
||||
}
|
||||
|
||||
public static function test_phpversion()
|
||||
{
|
||||
return version_compare(PHP_VERSION, '7.1.3', '>=');
|
||||
}
|
||||
|
||||
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 static::test_phpversion();
|
||||
}
|
||||
|
||||
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_memory_limit()
|
||||
{
|
||||
$memoryLimit = Tools::getMemoryLimit();
|
||||
|
||||
return $memoryLimit === '-1' || $memoryLimit >= Tools::getOctets('256M');
|
||||
}
|
||||
|
||||
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'), ['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 extension_loaded('mbstring');
|
||||
}
|
||||
|
||||
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 = [];
|
||||
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);
|
||||
}
|
||||
}
|
||||
233
classes/Connection.php
Normal file
233
classes/Connection.php
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class ConnectionCore.
|
||||
*/
|
||||
class ConnectionCore extends ObjectModel
|
||||
{
|
||||
/** @var int */
|
||||
public $id_guest;
|
||||
|
||||
/** @var int */
|
||||
public $id_page;
|
||||
|
||||
/** @var string */
|
||||
public $ip_address;
|
||||
|
||||
/** @var string */
|
||||
public $http_referer;
|
||||
|
||||
/** @var int */
|
||||
public $id_shop;
|
||||
|
||||
/** @var int */
|
||||
public $id_shop_group;
|
||||
|
||||
/** @var string */
|
||||
public $date_add;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'connections',
|
||||
'primary' => 'id_connections',
|
||||
'fields' => [
|
||||
'id_guest' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_page' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'ip_address' => ['type' => self::TYPE_INT, 'validate' => 'isInt'],
|
||||
'http_referer' => ['type' => self::TYPE_STRING, 'validate' => 'isAbsoluteUrl'],
|
||||
'id_shop' => ['type' => self::TYPE_INT, 'required' => true],
|
||||
'id_shop_group' => ['type' => self::TYPE_INT, 'required' => true],
|
||||
'date_add' => ['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) || !isset($_SERVER['HTTP_REFERER']) || strstr($_SERVER['HTTP_REFERER'], Tools::getHttpHost(false, 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 [];
|
||||
}
|
||||
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 ['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',
|
||||
[
|
||||
'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 [
|
||||
'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 : don't log connection
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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 (empty($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 . '))');
|
||||
}
|
||||
}
|
||||
}
|
||||
147
classes/ConnectionsSource.php
Normal file
147
classes/ConnectionsSource.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'connections_source',
|
||||
'primary' => 'id_connections_source',
|
||||
'fields' => [
|
||||
'id_connections' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'http_referer' => ['type' => self::TYPE_STRING, 'validate' => 'isAbsoluteUrl'],
|
||||
'request_uri' => ['type' => self::TYPE_STRING, 'validate' => 'isUrl'],
|
||||
'keywords' => ['type' => self::TYPE_STRING, 'validate' => 'isMessage'],
|
||||
'date_add' => ['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');
|
||||
}
|
||||
}
|
||||
122
classes/Contact.php
Normal file
122
classes/Contact.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class ContactCore.
|
||||
*/
|
||||
class ContactCore extends ObjectModel
|
||||
{
|
||||
public $id;
|
||||
|
||||
/** @var array<string> Name */
|
||||
public $name;
|
||||
|
||||
/** @var string E-mail */
|
||||
public $email;
|
||||
|
||||
/** @var array<string> Detailed description */
|
||||
public $description;
|
||||
|
||||
/** @var bool */
|
||||
public $customer_service;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'contact',
|
||||
'primary' => 'id_contact',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'email' => [
|
||||
'type' => self::TYPE_STRING,
|
||||
'validate' => 'isEmail',
|
||||
'size' => 255,
|
||||
],
|
||||
'customer_service' => [
|
||||
'type' => self::TYPE_BOOL,
|
||||
'validate' => 'isBool',
|
||||
],
|
||||
|
||||
/* Lang fields */
|
||||
'name' => [
|
||||
'type' => self::TYPE_STRING,
|
||||
'lang' => true,
|
||||
'validate' => 'isGenericName',
|
||||
'required' => true,
|
||||
'size' => 255,
|
||||
],
|
||||
'description' => [
|
||||
'type' => self::TYPE_STRING,
|
||||
'lang' => true,
|
||||
'validate' => 'isString',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* 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`
|
||||
');
|
||||
}
|
||||
}
|
||||
503
classes/Context.php
Normal file
503
classes/Context.php
Normal file
@@ -0,0 +1,503 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
use PrestaShop\PrestaShop\Adapter\ContainerFinder;
|
||||
use PrestaShop\PrestaShop\Adapter\Module\Repository\ModuleRepository;
|
||||
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
|
||||
use PrestaShop\PrestaShop\Core\Exception\ContainerNotFoundException;
|
||||
use PrestaShop\PrestaShop\Core\Localization\CLDR\ComputingPrecision;
|
||||
use PrestaShop\PrestaShop\Core\Localization\Locale;
|
||||
use PrestaShopBundle\Install\Language as InstallLanguage;
|
||||
use PrestaShopBundle\Translation\TranslatorComponent as Translator;
|
||||
use PrestaShopBundle\Translation\TranslatorLanguageLoader;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
|
||||
/**
|
||||
* 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 SessionInterface|null */
|
||||
public $session;
|
||||
|
||||
/** @var Link */
|
||||
public $link;
|
||||
|
||||
/** @var Country */
|
||||
public $country;
|
||||
|
||||
/** @var Employee|null */
|
||||
public $employee;
|
||||
|
||||
/** @var AdminController|FrontController */
|
||||
public $controller;
|
||||
|
||||
/** @var string */
|
||||
public $override_controller_name_for_translations;
|
||||
|
||||
/** @var Language|InstallLanguage */
|
||||
public $language;
|
||||
|
||||
/** @var Currency|null */
|
||||
public $currency;
|
||||
|
||||
/**
|
||||
* Current locale instance.
|
||||
*
|
||||
* @var Locale|null
|
||||
*/
|
||||
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;
|
||||
|
||||
/** @var int */
|
||||
protected $priceComputingPrecision = 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|null
|
||||
*/
|
||||
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|null
|
||||
*/
|
||||
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();
|
||||
|
||||
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);
|
||||
$this->cart->secure_key = $customer->secure_key;
|
||||
} else {
|
||||
$idCarrier = (int) $this->cart->id_carrier;
|
||||
$this->cart->secure_key = $customer->secure_key;
|
||||
$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 && $this->language->locale === $this->translator->getLocale()) {
|
||||
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 InstallLanguage
|
||||
// because it means that we're looking for the installer translations, so we're not yet connected to the DB
|
||||
$withDB = !$this->language instanceof InstallLanguage;
|
||||
$theme = $this->shop !== null ? $this->shop->theme : null;
|
||||
|
||||
try {
|
||||
$containerFinder = new ContainerFinder($this);
|
||||
$container = $containerFinder->getContainer();
|
||||
$translatorLoader = $container->get('prestashop.translation.translator_language_loader');
|
||||
} catch (ContainerNotFoundException $exception) {
|
||||
$translatorLoader = null;
|
||||
} catch (ServiceNotFoundException $e) {
|
||||
$translatorLoader = null;
|
||||
}
|
||||
|
||||
if (null === $translatorLoader) {
|
||||
// If a container is still not found, instantiate manually the translator loader
|
||||
// This will happen in the Front as we have legacy controllers, the Sf container won't be available.
|
||||
// As we get the translator in the controller's constructor and the container is built in the init method, we won't find it here
|
||||
$translatorLoader = (new TranslatorLanguageLoader(new ModuleRepository()));
|
||||
}
|
||||
|
||||
$translatorLoader
|
||||
->setIsAdminContext($adminContext)
|
||||
->loadLanguage($translator, $locale, $withDB, $theme)
|
||||
;
|
||||
|
||||
return $translator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getTranslationResourcesDirectories()
|
||||
{
|
||||
$locations = [_PS_ROOT_DIR_ . '/app/Resources/translations'];
|
||||
|
||||
if (null !== $this->shop) {
|
||||
$activeThemeLocation = _PS_ROOT_DIR_ . '/themes/' . $this->shop->theme_name . '/translations';
|
||||
if (is_dir($activeThemeLocation)) {
|
||||
$locations[] = $activeThemeLocation;
|
||||
}
|
||||
}
|
||||
|
||||
return $locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the computing precision according to the current currency
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getComputingPrecision()
|
||||
{
|
||||
if ($this->priceComputingPrecision === null) {
|
||||
$computingPrecision = new ComputingPrecision();
|
||||
$this->priceComputingPrecision = $computingPrecision->getPrecision($this->currency->precision);
|
||||
}
|
||||
|
||||
return $this->priceComputingPrecision;
|
||||
}
|
||||
}
|
||||
598
classes/Cookie.php
Normal file
598
classes/Cookie.php
Normal file
@@ -0,0 +1,598 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
use Defuse\Crypto\Key;
|
||||
use PrestaShop\PrestaShop\Core\Exception\CoreException;
|
||||
use PrestaShop\PrestaShop\Core\Session\SessionInterface;
|
||||
|
||||
/**
|
||||
* @property string $passwd
|
||||
*/
|
||||
class CookieCore
|
||||
{
|
||||
const SAMESITE_NONE = 'None';
|
||||
const SAMESITE_LAX = 'Lax';
|
||||
const SAMESITE_STRICT = 'Strict';
|
||||
|
||||
const SAMESITE_AVAILABLE_VALUES = [
|
||||
self::SAMESITE_NONE => self::SAMESITE_NONE,
|
||||
self::SAMESITE_LAX => self::SAMESITE_LAX,
|
||||
self::SAMESITE_STRICT => self::SAMESITE_STRICT,
|
||||
];
|
||||
|
||||
/** @var array Contain cookie content in a key => value format */
|
||||
protected $_content = [];
|
||||
|
||||
/** @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 string|bool SameSite for setcookie() */
|
||||
protected $_sameSite;
|
||||
|
||||
/** @var array Path for setcookie() */
|
||||
protected $_path;
|
||||
|
||||
/** @var array cipher tool instance */
|
||||
protected $cipherTool;
|
||||
|
||||
protected $_modified = false;
|
||||
|
||||
protected $_allow_writing;
|
||||
|
||||
protected $_salt;
|
||||
|
||||
protected $_standalone;
|
||||
|
||||
/** @var bool */
|
||||
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 = [];
|
||||
$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', '%7E', '%2B', '%26'], ['/', '~', '+', '&'], $this->_path);
|
||||
$this->_domain = $this->getDomain($shared_urls);
|
||||
$this->_sameSite = Configuration::get('PS_COOKIE_SAMESITE');
|
||||
$this->_name = 'PrestaShop-' . md5(($this->_standalone ? '' : _PS_VERSION_) . $name . $this->_domain);
|
||||
$this->_allow_writing = true;
|
||||
$this->_salt = $this->_standalone ? str_pad('', 32, md5('ps' . __FILE__)) : _COOKIE_IV_;
|
||||
|
||||
if ($this->_standalone) {
|
||||
$asciiSafeString = \Defuse\Crypto\Encoding::saveBytesToChecksummedAsciiSafeString(Key::KEY_CURRENT_VERSION, str_pad($name, Key::KEY_BYTE_SIZE, md5(__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()
|
||||
{
|
||||
$this->deleteSession();
|
||||
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);
|
||||
// remove the checksum which is the last element
|
||||
array_pop($tmpTab);
|
||||
$content_for_checksum = implode('¤', $tmpTab) . '¤';
|
||||
$checksum = hash('sha256', $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;
|
||||
}
|
||||
|
||||
/*
|
||||
* The alternative signature supporting an options array is only available since
|
||||
* PHP 7.3.0, before there is no support for SameSite attribute.
|
||||
*/
|
||||
if (PHP_VERSION_ID < 70300) {
|
||||
return setcookie(
|
||||
$this->_name,
|
||||
$content,
|
||||
$time,
|
||||
$this->_path,
|
||||
$this->_domain . '; SameSite=' . $this->_sameSite,
|
||||
$this->_secure,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
return setcookie(
|
||||
$this->_name,
|
||||
$content,
|
||||
[
|
||||
'expires' => $time,
|
||||
'path' => $this->_path,
|
||||
'domain' => $this->_domain,
|
||||
'secure' => $this->_secure,
|
||||
'httponly' => true,
|
||||
'samesite' => $this->_sameSite,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->write();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save cookie with setcookie().
|
||||
*/
|
||||
public function write()
|
||||
{
|
||||
if (!$this->_modified || headers_sent() || !$this->_allow_writing) {
|
||||
return;
|
||||
}
|
||||
|
||||
$previousChecksum = $cookie = '';
|
||||
|
||||
/* Serialize cookie content */
|
||||
if (isset($this->_content['checksum'])) {
|
||||
$previousChecksum = $this->_content['checksum'];
|
||||
unset($this->_content['checksum']);
|
||||
}
|
||||
foreach ($this->_content as $key => $value) {
|
||||
$cookie .= $key . '|' . $value . '¤';
|
||||
}
|
||||
|
||||
/* Add checksum to cookie */
|
||||
$newChecksum = hash('sha256', $this->_salt . $cookie);
|
||||
// do not set cookie if the checksum is the same: it means the content has not changed!
|
||||
if ($previousChecksum === $newChecksum) {
|
||||
return;
|
||||
}
|
||||
$cookie .= 'checksum|' . $newChecksum;
|
||||
$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 = [];
|
||||
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) || !isset($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 (isset($session) && !empty($session->getId())) {
|
||||
return $session;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
521
classes/Country.php
Normal file
521
classes/Country.php
Normal file
@@ -0,0 +1,521 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
|
||||
const GEOLOC_ALLOWED = 0;
|
||||
|
||||
const GEOLOC_CATALOG_MODE = 1;
|
||||
|
||||
const GEOLOC_FORBIDDEN = 2;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'country',
|
||||
'primary' => 'id_country',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'id_zone' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_currency' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId'],
|
||||
'call_prefix' => ['type' => self::TYPE_INT, 'validate' => 'isInt'],
|
||||
'iso_code' => ['type' => self::TYPE_STRING, 'validate' => 'isLanguageIsoCode', 'required' => true, 'size' => 3],
|
||||
'active' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'contains_states' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true],
|
||||
'need_identification_number' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true],
|
||||
'need_zip_code' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'zip_code_format' => ['type' => self::TYPE_STRING, 'validate' => 'isZipCodeFormat'],
|
||||
'display_tax_label' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true],
|
||||
|
||||
/* Lang fields */
|
||||
'name' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 64],
|
||||
],
|
||||
'associations' => [
|
||||
'zone' => ['type' => self::HAS_ONE],
|
||||
'currency' => ['type' => self::HAS_ONE],
|
||||
],
|
||||
];
|
||||
|
||||
protected static $cache_iso_by_id = [];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'objectsNodeName' => 'countries',
|
||||
'fields' => [
|
||||
'id_zone' => ['xlink_resource' => 'zones'],
|
||||
'id_currency' => ['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 = [];
|
||||
$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 $countries = [], array $modules = [])
|
||||
{
|
||||
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([], [['id_country' => $this->id]], []);
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
1199
classes/Currency.php
Normal file
1199
classes/Currency.php
Normal file
File diff suppressed because it is too large
Load Diff
115
classes/Curve.php
Normal file
115
classes/Curve.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data structure to store curves
|
||||
*/
|
||||
class CurveCore
|
||||
{
|
||||
/**
|
||||
* @var float[] indexed by string
|
||||
*/
|
||||
protected $values = [];
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
/**
|
||||
* Can be: bars, steps
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
*/
|
||||
public function setValues($values)
|
||||
{
|
||||
$this->values = $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $time_mode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
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 : '') . '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $x
|
||||
* @param float $y
|
||||
*/
|
||||
public function setPoint($x, $y)
|
||||
{
|
||||
$this->values[(string) $x] = (float) $y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $label
|
||||
*/
|
||||
public function setLabel($label)
|
||||
{
|
||||
$this->label = $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type accepts only 'bars' or 'steps'
|
||||
*/
|
||||
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}';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $x
|
||||
*
|
||||
* @return float|null return point if found, null else
|
||||
*/
|
||||
public function getPoint($x)
|
||||
{
|
||||
if (array_key_exists((string) $x, $this->values)) {
|
||||
return $this->values[(string) $x];
|
||||
}
|
||||
}
|
||||
}
|
||||
1487
classes/Customer.php
Normal file
1487
classes/Customer.php
Normal file
File diff suppressed because it is too large
Load Diff
35
classes/CustomerAddress.php
Normal file
35
classes/CustomerAddress.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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
187
classes/CustomerMessage.php
Normal file
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class CustomerMessageCore.
|
||||
*/
|
||||
class CustomerMessageCore extends ObjectModel
|
||||
{
|
||||
public $id;
|
||||
/** @var int CustomerThread ID */
|
||||
public $id_customer_thread;
|
||||
|
||||
/** @var */
|
||||
public $id_employee;
|
||||
|
||||
/** @var string */
|
||||
public $message;
|
||||
|
||||
/** @var string */
|
||||
public $file_name;
|
||||
|
||||
/** @var string */
|
||||
public $ip_address;
|
||||
|
||||
/** @var string */
|
||||
public $user_agent;
|
||||
|
||||
/** @var bool */
|
||||
public $private;
|
||||
|
||||
/** @var string */
|
||||
public $date_add;
|
||||
|
||||
/** @var string */
|
||||
public $date_upd;
|
||||
|
||||
/** @var bool */
|
||||
public $read;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'customer_message',
|
||||
'primary' => 'id_customer_message',
|
||||
'fields' => [
|
||||
'id_employee' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId'],
|
||||
'id_customer_thread' => ['type' => self::TYPE_INT],
|
||||
'ip_address' => ['type' => self::TYPE_STRING, 'validate' => 'isIp2Long', 'size' => 15],
|
||||
'message' => ['type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'required' => true, 'size' => 16777216],
|
||||
'file_name' => ['type' => self::TYPE_STRING],
|
||||
'user_agent' => ['type' => self::TYPE_STRING],
|
||||
'private' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
'date_upd' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
'read' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
],
|
||||
];
|
||||
|
||||
/** @var array */
|
||||
protected $webserviceParameters = [
|
||||
'fields' => [
|
||||
'id_employee' => [
|
||||
'xlink_resource' => 'employees',
|
||||
],
|
||||
'id_customer_thread' => [
|
||||
'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'
|
||||
);
|
||||
}
|
||||
}
|
||||
89
classes/CustomerSession.php
Normal file
89
classes/CustomerSession.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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
294
classes/CustomerThread.php
Normal file
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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 = [
|
||||
'table' => 'customer_thread',
|
||||
'primary' => 'id_customer_thread',
|
||||
'fields' => [
|
||||
'id_lang' => [
|
||||
'type' => self::TYPE_INT,
|
||||
'validate' => 'isUnsignedId',
|
||||
'required' => true,
|
||||
],
|
||||
'id_contact' => [
|
||||
'type' => self::TYPE_INT,
|
||||
'validate' => 'isUnsignedId',
|
||||
'required' => true,
|
||||
],
|
||||
'id_shop' => [
|
||||
'type' => self::TYPE_INT,
|
||||
'validate' => 'isUnsignedId',
|
||||
],
|
||||
'id_customer' => [
|
||||
'type' => self::TYPE_INT,
|
||||
'validate' => 'isUnsignedId',
|
||||
],
|
||||
'id_order' => [
|
||||
'type' => self::TYPE_INT,
|
||||
'validate' => 'isUnsignedId',
|
||||
],
|
||||
'id_product' => [
|
||||
'type' => self::TYPE_INT,
|
||||
'validate' => 'isUnsignedId',
|
||||
],
|
||||
'email' => [
|
||||
'type' => self::TYPE_STRING,
|
||||
'validate' => 'isEmail',
|
||||
'size' => 255,
|
||||
],
|
||||
'token' => [
|
||||
'type' => self::TYPE_STRING,
|
||||
'validate' => 'isGenericName',
|
||||
'required' => true,
|
||||
],
|
||||
'status' => [
|
||||
'type' => self::TYPE_STRING,
|
||||
],
|
||||
'date_add' => [
|
||||
'type' => self::TYPE_DATE,
|
||||
'validate' => 'isDate',
|
||||
],
|
||||
'date_upd' => [
|
||||
'type' => self::TYPE_DATE,
|
||||
'validate' => 'isDate',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'fields' => [
|
||||
'id_lang' => [
|
||||
'xlink_resource' => 'languages',
|
||||
],
|
||||
'id_shop' => [
|
||||
'xlink_resource' => 'shops',
|
||||
],
|
||||
'id_customer' => [
|
||||
'xlink_resource' => 'customers',
|
||||
],
|
||||
'id_order' => [
|
||||
'xlink_resource' => 'orders',
|
||||
],
|
||||
'id_product' => [
|
||||
'xlink_resource' => 'products',
|
||||
],
|
||||
],
|
||||
'associations' => [
|
||||
'customer_messages' => [
|
||||
'resource' => 'customer_message',
|
||||
'id' => [
|
||||
'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
430
classes/Customization.php
Normal file
@@ -0,0 +1,430 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'customization',
|
||||
'primary' => 'id_customization',
|
||||
'fields' => [
|
||||
/* Classic fields */
|
||||
'id_product_attribute' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_address_delivery' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_cart' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_product' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'quantity' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'quantity_refunded' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'quantity_returned' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'in_cart' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true],
|
||||
],
|
||||
];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'fields' => [
|
||||
'id_address_delivery' => [
|
||||
'xlink_resource' => [
|
||||
'resourceName' => 'addresses',
|
||||
],
|
||||
],
|
||||
'id_cart' => [
|
||||
'xlink_resource' => [
|
||||
'resourceName' => 'carts',
|
||||
],
|
||||
],
|
||||
'id_product' => [
|
||||
'xlink_resource' => [
|
||||
'resourceName' => 'products',
|
||||
],
|
||||
],
|
||||
],
|
||||
'associations' => [
|
||||
'customized_data_text_fields' => [
|
||||
'resource' => 'customized_data_text_field',
|
||||
'virtual_entity' => true,
|
||||
'fields' => [
|
||||
'id_customization_field' => ['required' => true, 'xlink_resource' => 'product_customization_fields'],
|
||||
'value' => [],
|
||||
],
|
||||
],
|
||||
'customized_data_images' => [
|
||||
'resource' => 'customized_data_image',
|
||||
'virtual_entity' => true,
|
||||
'setter' => false,
|
||||
'fields' => [
|
||||
'id_customization_field' => ['xlink_resource' => 'product_customization_fields'],
|
||||
'value' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
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 = [];
|
||||
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 = [];
|
||||
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 = [];
|
||||
|
||||
$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 = [];
|
||||
|
||||
$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 [];
|
||||
}
|
||||
|
||||
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 [];
|
||||
}
|
||||
|
||||
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', [$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;
|
||||
}
|
||||
}
|
||||
78
classes/CustomizationField.php
Normal file
78
classes/CustomizationField.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Domain\Product\Customization\CustomizationFieldSettings;
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'customization_field',
|
||||
'primary' => 'id_customization_field',
|
||||
'multilang' => true,
|
||||
'multilang_shop' => true,
|
||||
'fields' => [
|
||||
/* Classic fields */
|
||||
'id_product' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'type' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'required' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true],
|
||||
'is_module' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => false],
|
||||
'is_deleted' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => false],
|
||||
|
||||
/* Lang fields */
|
||||
'name' => ['type' => self::TYPE_STRING, 'lang' => true, 'required' => true, 'size' => CustomizationFieldSettings::MAX_NAME_LENGTH],
|
||||
],
|
||||
];
|
||||
|
||||
/** @var array */
|
||||
protected $webserviceParameters = [
|
||||
'fields' => [
|
||||
'id_product' => [
|
||||
'xlink_resource' => [
|
||||
'resourceName' => 'products',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
74
classes/DateRange.php
Normal file
74
classes/DateRange.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class DateRangeCore.
|
||||
*/
|
||||
class DateRangeCore extends ObjectModel
|
||||
{
|
||||
/** @var string */
|
||||
public $time_start;
|
||||
|
||||
/** @var string */
|
||||
public $time_end;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'date_range',
|
||||
'primary' => 'id_date_range',
|
||||
'fields' => [
|
||||
'time_start' => ['type' => self::TYPE_DATE, 'validate' => 'isDate', 'required' => true],
|
||||
'time_end' => ['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 (!isset($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
107
classes/Delivery.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'delivery',
|
||||
'primary' => 'id_delivery',
|
||||
'fields' => [
|
||||
'id_carrier' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_range_price' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_range_weight' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_zone' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_shop' => ['type' => self::TYPE_INT],
|
||||
'id_shop_group' => ['type' => self::TYPE_INT],
|
||||
'price' => ['type' => self::TYPE_FLOAT, 'validate' => 'isPrice', 'required' => true],
|
||||
],
|
||||
];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'objectsNodeName' => 'deliveries',
|
||||
'fields' => [
|
||||
'id_carrier' => ['xlink_resource' => 'carriers'],
|
||||
'id_range_price' => ['xlink_resource' => 'price_ranges'],
|
||||
'id_range_weight' => ['xlink_resource' => 'weight_ranges'],
|
||||
'id_zone' => ['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;
|
||||
}
|
||||
}
|
||||
1238
classes/Dispatcher.php
Normal file
1238
classes/Dispatcher.php
Normal file
File diff suppressed because it is too large
Load Diff
759
classes/Employee.php
Normal file
759
classes/Employee.php
Normal file
@@ -0,0 +1,759 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
use PrestaShop\PrestaShop\Adapter\CoreException;
|
||||
use PrestaShop\PrestaShop\Adapter\ServiceLocator;
|
||||
use PrestaShop\PrestaShop\Core\Crypto\Hashing;
|
||||
|
||||
/**
|
||||
* Class EmployeeCore.
|
||||
*/
|
||||
class EmployeeCore extends ObjectModel
|
||||
{
|
||||
/** @var int Employee ID */
|
||||
public $id;
|
||||
|
||||
/** @var int Employee profile */
|
||||
public $id_profile;
|
||||
|
||||
/** @var int 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 string 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 */
|
||||
public $bo_menu = 1;
|
||||
|
||||
/* Deprecated */
|
||||
public $bo_show_screencast = false;
|
||||
|
||||
/** @var bool Status */
|
||||
public $active = 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 password feature */
|
||||
public $reset_password_token;
|
||||
|
||||
/** @var string token validity date for forgot password feature */
|
||||
public $reset_password_validity;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $has_enabled_gravatar = false;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'employee',
|
||||
'primary' => 'id_employee',
|
||||
'fields' => [
|
||||
'lastname' => ['type' => self::TYPE_STRING, 'validate' => 'isName', 'required' => true, 'size' => 255],
|
||||
'firstname' => ['type' => self::TYPE_STRING, 'validate' => 'isName', 'required' => true, 'size' => 255],
|
||||
'email' => ['type' => self::TYPE_STRING, 'validate' => 'isEmail', 'required' => true, 'size' => 255],
|
||||
'id_lang' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'required' => true],
|
||||
'passwd' => ['type' => self::TYPE_STRING, 'validate' => 'isPasswd', 'required' => true, 'size' => 255],
|
||||
'last_passwd_gen' => ['type' => self::TYPE_STRING],
|
||||
'active' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'id_profile' => ['type' => self::TYPE_INT, 'validate' => 'isInt', 'required' => true],
|
||||
'bo_color' => ['type' => self::TYPE_STRING, 'validate' => 'isColor', 'size' => 32],
|
||||
'default_tab' => ['type' => self::TYPE_INT, 'validate' => 'isInt'],
|
||||
'bo_theme' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 32],
|
||||
'bo_css' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 64],
|
||||
'bo_width' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
|
||||
'bo_menu' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'stats_date_from' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
'stats_date_to' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
'stats_compare_from' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
'stats_compare_to' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
'stats_compare_option' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
|
||||
'preselect_date_range' => ['type' => self::TYPE_STRING, 'size' => 32],
|
||||
'id_last_order' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
|
||||
'id_last_customer_message' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
|
||||
'id_last_customer' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
|
||||
'reset_password_token' => ['type' => self::TYPE_STRING, 'validate' => 'isSha1', 'size' => 40, 'copy_post' => false],
|
||||
'reset_password_validity' => ['type' => self::TYPE_DATE, 'validate' => 'isDateOrNull', 'copy_post' => false],
|
||||
'has_enabled_gravatar' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
],
|
||||
];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'fields' => [
|
||||
'id_lang' => ['xlink_resource' => 'languages'],
|
||||
'last_passwd_gen' => ['setter' => null],
|
||||
'stats_date_from' => ['setter' => null],
|
||||
'stats_date_to' => ['setter' => null],
|
||||
'stats_compare_from' => ['setter' => null],
|
||||
'stats_compare_to' => ['setter' => null],
|
||||
'passwd' => ['setter' => 'setWsPasswd'],
|
||||
],
|
||||
];
|
||||
|
||||
protected $associated_shops = [];
|
||||
|
||||
/**
|
||||
* 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->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);
|
||||
|
||||
$this->updateTextDirection();
|
||||
|
||||
return parent::update($nullValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) . '\'
|
||||
', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
$defaultSystem = Tools::getAdminImageUrl('pr/default.jpg');
|
||||
$imageUrl = null;
|
||||
|
||||
// Default from Profile
|
||||
$profile = new Profile($this->id_profile);
|
||||
$defaultProfile = (int) $profile->id === (int) $this->id_profile ? $profile->getProfileImage() : null;
|
||||
$imageUrl = $imageUrl ?? $defaultProfile;
|
||||
|
||||
// Gravatar
|
||||
if ($this->has_enabled_gravatar) {
|
||||
$imageUrl = $imageUrl ?? 'https://www.gravatar.com/avatar/' . md5(strtolower(trim($this->email))) . '?d=' . urlencode($defaultSystem);
|
||||
}
|
||||
|
||||
// Local Image
|
||||
$imagePath = $this->image_dir . $this->id . '.jpg';
|
||||
if (file_exists($imagePath)) {
|
||||
$imageUrl = $imageUrl ?? Context::getContext()->link->getMediaLink(
|
||||
str_replace($this->image_dir, _THEME_EMPLOYEE_DIR_, $imagePath)
|
||||
);
|
||||
}
|
||||
|
||||
// Default from System
|
||||
$imageUrl = $imageUrl ?? $defaultSystem;
|
||||
|
||||
// Hooks
|
||||
Hook::exec(
|
||||
'actionOverrideEmployeeImage',
|
||||
[
|
||||
'employee' => $this,
|
||||
'imageUrl' => &$imageUrl,
|
||||
]
|
||||
);
|
||||
|
||||
return $imageUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 is_array($access) && $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;
|
||||
}
|
||||
}
|
||||
89
classes/EmployeeSession.php
Normal file
89
classes/EmployeeSession.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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
369
classes/Feature.php
Normal file
@@ -0,0 +1,369 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class FeatureCore.
|
||||
*/
|
||||
class FeatureCore extends ObjectModel
|
||||
{
|
||||
/** @var array<string> Name */
|
||||
public $name;
|
||||
|
||||
/** @var int */
|
||||
public $position;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'feature',
|
||||
'primary' => 'id_feature',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'position' => ['type' => self::TYPE_INT, 'validate' => 'isInt'],
|
||||
|
||||
/* Lang fields */
|
||||
'name' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 128],
|
||||
],
|
||||
];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'objectsNodeName' => 'product_features',
|
||||
'objectNodeName' => 'product_feature',
|
||||
'fields' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* 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', ['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', ['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', ['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
259
classes/FeatureValue.php
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class FeatureValueCore.
|
||||
*/
|
||||
class FeatureValueCore extends ObjectModel
|
||||
{
|
||||
/** @var int Group id which attribute belongs */
|
||||
public $id_feature;
|
||||
|
||||
/** @var string|array Name */
|
||||
public $value;
|
||||
|
||||
/** @var bool Custom */
|
||||
public $custom = 0;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'feature_value',
|
||||
'primary' => 'id_feature_value',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'id_feature' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'custom' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
|
||||
/* Lang fields */
|
||||
'value' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 255],
|
||||
],
|
||||
];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'objectsNodeName' => 'product_feature_values',
|
||||
'objectNodeName' => 'product_feature_value',
|
||||
'fields' => [
|
||||
'id_feature' => ['xlink_resource' => 'product_features'],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Get all values for a given feature.
|
||||
*
|
||||
* @param int $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 int $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 int $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', ['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', ['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', ['id_feature_value' => $this->id]);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
98
classes/FileUploader.php
Normal file
98
classes/FileUploader.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
class FileUploaderCore
|
||||
{
|
||||
protected $allowedExtensions = [];
|
||||
|
||||
/** @var QqUploadedFileXhr|QqUploadedFileForm|false */
|
||||
protected $file;
|
||||
protected $sizeLimit;
|
||||
|
||||
public function __construct(array $allowedExtensions = [], $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 ['error' => Context::getContext()->getTranslator()->trans('No files were uploaded.', [], 'Admin.Notifications.Error')];
|
||||
}
|
||||
|
||||
$size = $this->file->getSize();
|
||||
|
||||
if ($size == 0) {
|
||||
return ['error' => Context::getContext()->getTranslator()->trans('Source file does not exist or is empty.', [], 'Admin.Notifications.Error')];
|
||||
}
|
||||
if ($size > $this->sizeLimit) {
|
||||
return ['error' => Context::getContext()->getTranslator()->trans('The uploaded file is too large.', [], 'Admin.Notifications.Error')];
|
||||
}
|
||||
|
||||
$pathinfo = pathinfo($this->file->getName());
|
||||
$these = implode(', ', $this->allowedExtensions);
|
||||
if (!isset($pathinfo['extension'])) {
|
||||
return ['error' => Context::getContext()->getTranslator()->trans('File has an invalid extension, it should be one of these: %s.', [$these], 'Admin.Notifications.Error')];
|
||||
}
|
||||
$ext = $pathinfo['extension'];
|
||||
if ($this->allowedExtensions && !in_array(strtolower($ext), $this->allowedExtensions)) {
|
||||
return ['error' => Context::getContext()->getTranslator()->trans('File has an invalid extension, it should be one of these: %s.', [$these], 'Admin.Notifications.Error')];
|
||||
}
|
||||
|
||||
return $this->file->save();
|
||||
}
|
||||
}
|
||||
99
classes/Gender.php
Normal file
99
classes/Gender.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'gender',
|
||||
'primary' => 'id_gender',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'type' => ['type' => self::TYPE_INT, 'required' => true],
|
||||
|
||||
/* Lang fields */
|
||||
'name' => ['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';
|
||||
}
|
||||
}
|
||||
427
classes/Group.php
Normal file
427
classes/Group.php
Normal file
@@ -0,0 +1,427 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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 = [
|
||||
'table' => 'group',
|
||||
'primary' => 'id_group',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'reduction' => ['type' => self::TYPE_FLOAT, 'validate' => 'isFloat'],
|
||||
'price_display_method' => ['type' => self::TYPE_INT, 'validate' => 'isPriceDisplayMethod', 'required' => true],
|
||||
'show_prices' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
'date_upd' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
|
||||
/* Lang fields */
|
||||
'name' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 32],
|
||||
],
|
||||
];
|
||||
|
||||
protected static $cache_reduction = [];
|
||||
protected static $group_price_display_method = [];
|
||||
protected static $ps_group_feature_active = null;
|
||||
protected static $groups = [];
|
||||
protected static $ps_unidentified_group = null;
|
||||
protected static $ps_customer_group = null;
|
||||
|
||||
protected $webserviceParameters = [];
|
||||
|
||||
public const PRICE_DISPLAY_METHOD_TAX_INCL = 0;
|
||||
public const PRICE_DISPLAY_METHOD_TAX_EXCL = 1;
|
||||
|
||||
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 = [];
|
||||
self::$group_price_display_method = [];
|
||||
self::$ps_group_feature_active = null;
|
||||
self::$groups = [];
|
||||
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 = [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 = [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) . '\'
|
||||
');
|
||||
}
|
||||
}
|
||||
296
classes/GroupReduction.php
Normal file
296
classes/GroupReduction.php
Normal file
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
class GroupReductionCore extends ObjectModel
|
||||
{
|
||||
public $id_group;
|
||||
public $id_category;
|
||||
public $reduction;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'group_reduction',
|
||||
'primary' => 'id_group_reduction',
|
||||
'fields' => [
|
||||
'id_group' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_category' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'reduction' => ['type' => self::TYPE_FLOAT, 'validate' => 'isPrice', 'required' => true],
|
||||
],
|
||||
];
|
||||
|
||||
protected static $reduction_cache = [];
|
||||
|
||||
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 = [];
|
||||
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 = [];
|
||||
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 = [];
|
||||
foreach ($products as $product) {
|
||||
$ids[] = $product['id_product'];
|
||||
}
|
||||
|
||||
$result = true;
|
||||
if ($ids) {
|
||||
$result &= Db::getInstance()->update('product_group_reduction_cache', [
|
||||
'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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset static cache (mainly for test environment)
|
||||
*/
|
||||
public static function resetStaticCache()
|
||||
{
|
||||
static::$reduction_cache = [];
|
||||
}
|
||||
}
|
||||
254
classes/Guest.php
Normal file
254
classes/Guest.php
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'guest',
|
||||
'primary' => 'id_guest',
|
||||
'fields' => [
|
||||
'id_operating_system' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId'],
|
||||
'id_web_browser' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId'],
|
||||
'id_customer' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId'],
|
||||
'javascript' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'screen_resolution_x' => ['type' => self::TYPE_INT, 'validate' => 'isInt'],
|
||||
'screen_resolution_y' => ['type' => self::TYPE_INT, 'validate' => 'isInt'],
|
||||
'screen_color' => ['type' => self::TYPE_INT, 'validate' => 'isInt'],
|
||||
'sun_java' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'adobe_flash' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'adobe_director' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'apple_quicktime' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'real_player' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'windows_media' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'accept_language' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 8],
|
||||
'mobile_theme' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
],
|
||||
];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'fields' => [
|
||||
'id_customer' => ['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 = [];
|
||||
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 = [
|
||||
'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 = [
|
||||
'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
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function mergeWithCustomer($idGuest, $idCustomer)
|
||||
{
|
||||
// Since the guests are merged, the guest id in the connections table must be changed too
|
||||
Db::getInstance()->update('connections', [
|
||||
'id_guest' => (int) $idGuest,
|
||||
], 'id_guest = ' . (int) $this->id);
|
||||
|
||||
// Since the guests are merged, the guest id in the cart table must be changed too
|
||||
Db::getInstance()->update('cart', [
|
||||
'id_guest' => (int) $idGuest,
|
||||
], 'id_guest = ' . (int) $this->id);
|
||||
|
||||
// The existing guest is removed from the database
|
||||
$existingGuest = new Guest((int) $idGuest);
|
||||
$existingGuest->delete();
|
||||
|
||||
// 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->force_id = true;
|
||||
|
||||
return $this->add();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
1294
classes/Hook.php
Normal file
1294
classes/Hook.php
Normal file
File diff suppressed because it is too large
Load Diff
882
classes/Image.php
Normal file
882
classes/Image.php
Normal file
@@ -0,0 +1,882 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 array<int,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 = [
|
||||
'table' => 'image',
|
||||
'primary' => 'id_image',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'id_product' => ['type' => self::TYPE_INT, 'shop' => 'both', 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'position' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
|
||||
'cover' => ['type' => self::TYPE_BOOL, 'allow_null' => true, 'validate' => 'isBool', 'shop' => true],
|
||||
'legend' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 128],
|
||||
],
|
||||
];
|
||||
|
||||
protected static $_cacheGetSize = [];
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param int $idShop Shop ID
|
||||
*
|
||||
* @return array Images
|
||||
*/
|
||||
public static function getImages($idLang, $idProduct, $idProductAttribute = null, $idShop = null)
|
||||
{
|
||||
$attributeFilter = ($idProductAttribute ? ' AND ai.`id_product_attribute` = ' . (int) $idProductAttribute : '');
|
||||
$shopFilter = ($idShop ? ' AND ims.`id_shop` = ' . (int) $idShop : '');
|
||||
$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`)';
|
||||
}
|
||||
|
||||
if ($idShop) {
|
||||
$sql .= ' LEFT JOIN `' . _DB_PREFIX_ . 'image_shop` ims ON (i.`id_image` = ims.`id_image`)';
|
||||
}
|
||||
|
||||
$sql .= ' WHERE i.`id_product` = ' . (int) $idProduct . ' AND il.`id_lang` = ' . (int) $idLang . $attributeFilter . $shopFilter . '
|
||||
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 int $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 = [];
|
||||
|
||||
// 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 fileType
|
||||
$filesToDelete[] = $this->image_dir . $this->getImgFolder() . 'fileType';
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
680
classes/ImageManager.php
Normal file
680
classes/ImageManager.php
Normal file
@@ -0,0 +1,680 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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;
|
||||
const MIME_TYPE_SUPPORTED = [
|
||||
'image/gif',
|
||||
'image/jpg',
|
||||
'image/jpeg',
|
||||
'image/pjpeg',
|
||||
'image/png',
|
||||
'image/x-png',
|
||||
];
|
||||
|
||||
/**
|
||||
* 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 __PS_BASE_URI__ . 'img/tmp/' . $cacheImage . $cacheParam;
|
||||
}
|
||||
|
||||
return _PS_TMP_IMG_ . $cacheImage . $cacheParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if memory limit is too long or not.
|
||||
*
|
||||
* @param string $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', ['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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
public static function getMimeType(string $filename)
|
||||
{
|
||||
$mimeType = false;
|
||||
// Try with GD
|
||||
if (function_exists('getimagesize')) {
|
||||
$imageInfo = @getimagesize($filename);
|
||||
if ($imageInfo) {
|
||||
$mimeType = $imageInfo['mime'];
|
||||
}
|
||||
}
|
||||
// Try with FileInfo
|
||||
if (!$mimeType && function_exists('finfo_open')) {
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mimeType = finfo_file($finfo, $filename);
|
||||
finfo_close($finfo);
|
||||
}
|
||||
// Try with Mime
|
||||
if (!$mimeType && function_exists('mime_content_type')) {
|
||||
$mimeType = mime_content_type($filename);
|
||||
}
|
||||
// Try with exec command and file binary
|
||||
if (!$mimeType && 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)));
|
||||
}
|
||||
}
|
||||
|
||||
return $mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<string>|null $mimeTypeList Allowed MIME types
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isRealImage($filename, $fileMimeType = null, $mimeTypeList = null)
|
||||
{
|
||||
if (!$mimeTypeList) {
|
||||
$mimeTypeList = static::MIME_TYPE_SUPPORTED;
|
||||
}
|
||||
|
||||
$mimeType = static::getMimeType($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 = ['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
|
||||
* @param array<string>|null $types Authorized extensions
|
||||
* @param array<string>|null $mimeTypeList Authorized mimetypes
|
||||
*
|
||||
* @return bool|string Return false if no error encountered
|
||||
*/
|
||||
public static function validateUpload($file, $maxFileSize = 0, $types = null, $mimeTypeList = 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', [$file['size'] / 1024, $maxFileSize / 1024], 'Admin.Notifications.Error');
|
||||
}
|
||||
if (!ImageManager::isRealImage($file['tmp_name'], $file['type'], $mimeTypeList) || !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', [], 'Admin.Notifications.Error');
|
||||
}
|
||||
if ($file['error']) {
|
||||
return Context::getContext()->getTranslator()->trans('Error while uploading image; please change your server\'s settings. (Error code: %s)', [$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', [$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', [], 'Admin.Notifications.Error');
|
||||
}
|
||||
if ($file['error']) {
|
||||
return Context::getContext()->getTranslator()->trans('Error while uploading image; please change your server\'s settings.', [], '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 = [
|
||||
'width' => $srcInfo[0],
|
||||
'height' => $srcInfo[1],
|
||||
'ressource' => ImageManager::create($srcInfo[2], $srcFile),
|
||||
];
|
||||
|
||||
// Destination information
|
||||
$dest = [];
|
||||
$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', ['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 = [
|
||||
'image/gif' => ['gif'],
|
||||
'image/jpeg' => ['jpg', 'jpeg'],
|
||||
'image/png' => ['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;
|
||||
}
|
||||
}
|
||||
231
classes/ImageType.php
Normal file
231
classes/ImageType.php
Normal file
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 bool Apply to categories */
|
||||
public $categories;
|
||||
|
||||
/** @var bool Apply to manufacturers */
|
||||
public $manufacturers;
|
||||
|
||||
/** @var bool Apply to suppliers */
|
||||
public $suppliers;
|
||||
|
||||
/** @var bool Apply to store */
|
||||
public $stores;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'image_type',
|
||||
'primary' => 'id_image_type',
|
||||
'fields' => [
|
||||
'name' => ['type' => self::TYPE_STRING, 'validate' => 'isImageTypeName', 'required' => true, 'size' => 64],
|
||||
'width' => ['type' => self::TYPE_INT, 'validate' => 'isImageSize', 'required' => true],
|
||||
'height' => ['type' => self::TYPE_INT, 'validate' => 'isImageSize', 'required' => true],
|
||||
'categories' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'products' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'manufacturers' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'suppliers' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'stores' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array Image types cache
|
||||
*/
|
||||
protected static $images_types_cache = [];
|
||||
|
||||
protected static $images_types_name_cache = [];
|
||||
|
||||
protected $webserviceParameters = [];
|
||||
|
||||
/**
|
||||
* 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) . '\'', false);
|
||||
|
||||
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 = ['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(['_' . $themeName, $themeName . '_'], '', $name);
|
||||
|
||||
//check if the theme name is already in $name if yes only return $name
|
||||
if ($themeName !== null && strstr($name, $themeName) && self::getByNameNType($name)) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
if (self::getByNameNType($nameWithoutThemeName . '_' . $themeName)) {
|
||||
return $nameWithoutThemeName . '_' . $themeName;
|
||||
}
|
||||
|
||||
if (self::getByNameNType($themeName . '_' . $nameWithoutThemeName)) {
|
||||
return $themeName . '_' . $nameWithoutThemeName;
|
||||
}
|
||||
|
||||
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 : [];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
1795
classes/Language.php
Normal file
1795
classes/Language.php
Normal file
File diff suppressed because it is too large
Load Diff
1602
classes/Link.php
Normal file
1602
classes/Link.php
Normal file
File diff suppressed because it is too large
Load Diff
594
classes/LocalizationPack.php
Normal file
594
classes/LocalizationPack.php
Normal file
@@ -0,0 +1,594 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
|
||||
use PrestaShop\PrestaShop\Core\Addon\Module\ModuleManagerBuilder;
|
||||
use PrestaShop\PrestaShop\Core\Domain\Currency\Command\AddCurrencyCommand;
|
||||
use PrestaShop\PrestaShop\Core\Domain\Currency\Exception\CurrencyException;
|
||||
use PrestaShop\PrestaShop\Core\Domain\Currency\ValueObject\CurrencyId;
|
||||
use PrestaShop\PrestaShop\Core\Localization\CLDR\LocaleRepository;
|
||||
|
||||
class LocalizationPackCore
|
||||
{
|
||||
public $name;
|
||||
public $version;
|
||||
|
||||
protected $iso_code_lang;
|
||||
protected $iso_currency;
|
||||
protected $_errors = [];
|
||||
|
||||
/**
|
||||
* 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',
|
||||
[$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',
|
||||
[$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) {
|
||||
$res &= Validate::isLocalizationPackSelection($selected) ? $this->{'_install' . $selected}($xml, $install_mode) : 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.', [], '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.', [], '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', [$country->name], 'Admin.International.Notification');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$state->add()) {
|
||||
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error occurred while adding the state.', [], '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.', [], '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 = [];
|
||||
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.', [], 'Admin.International.Notification') . ' ' . $error;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$tax->add()) {
|
||||
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error occurred while importing the tax: %s', [(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.', [], '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 = (string) $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;
|
||||
}
|
||||
|
||||
$sfContainer = SymfonyContainer::getInstance();
|
||||
$commandBus = $sfContainer->get('prestashop.core.command_bus');
|
||||
|
||||
$command = new AddCurrencyCommand(
|
||||
(string) $attributes['iso_code'],
|
||||
(float) 1,
|
||||
true
|
||||
);
|
||||
|
||||
/* @var CurrencyId $currencyId */
|
||||
try {
|
||||
$currencyId = $commandBus->handle($command);
|
||||
} catch (CurrencyException $e) {
|
||||
$this->_errors[] = null;
|
||||
Context::getContext()->getTranslator()->trans(
|
||||
'An error occurred while importing the currency: %s',
|
||||
[(string) ($attributes['name'])],
|
||||
'Admin.International.Notification'
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Cache::clear();
|
||||
|
||||
PaymentModule::addCurrencyPermissions($currencyId->getValue());
|
||||
}
|
||||
|
||||
$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 SimpleXMLElement $xml
|
||||
* @param bool $install_mode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function _installLanguages($xml, $install_mode = false)
|
||||
{
|
||||
$attributes = [];
|
||||
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 = ['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.', [], '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.', [], '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', [$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', [$name], 'Admin.International.Notification');
|
||||
}
|
||||
}
|
||||
|
||||
unset($module);
|
||||
} else {
|
||||
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error has occurred, this module does not exist: %s', [$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',
|
||||
[$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'], [0, 1])) {
|
||||
Configuration::updateValue('PRICE_DISPLAY_METHOD', (int) $attributes['price_display_method']);
|
||||
|
||||
foreach ([(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', [], 'Admin.International.Notification');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->_errors[] = Context::getContext()->getTranslator()->trans('An error has occurred during the default group update', [], 'Admin.International.Notification');
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->_errors;
|
||||
}
|
||||
}
|
||||
951
classes/Mail.php
Normal file
951
classes/Mail.php
Normal file
@@ -0,0 +1,951 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 array $templateVars Template variables for the email
|
||||
* @param string|array<string> $to To email
|
||||
* @param string|array<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 = new Swift_Message();
|
||||
|
||||
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 = (new Swift_SmtpTransport(
|
||||
$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 {
|
||||
/**
|
||||
* mail() support was removed from SwiftMailer for security reasons
|
||||
* previously => $connection = \Swift_MailTransport::newInstance();
|
||||
* Use Swift_SendmailTransport instead
|
||||
*
|
||||
* @see https://github.com/swiftmailer/swiftmailer/issues/866
|
||||
*/
|
||||
$connection = new Swift_SendmailTransport();
|
||||
}
|
||||
|
||||
if (!$connection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$swift = new Swift_Mailer($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));
|
||||
}
|
||||
|
||||
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['{order_slip_url}'] = Context::getContext()->link->getPageLink(
|
||||
'order-slip',
|
||||
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([self::toPunycode($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 = [$fileAttachment];
|
||||
}
|
||||
|
||||
foreach ($fileAttachment as $attachment) {
|
||||
if (isset($attachment['content'], $attachment['name'], $attachment['mime'])) {
|
||||
$message->attach(
|
||||
(new Swift_Attachment())->setFilename(
|
||||
$attachment['name']
|
||||
)->setContentType($attachment['mime'])
|
||||
->setBody($attachment['content'])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Send mail */
|
||||
$message->setFrom([$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($message->getSubject(), 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 $smtpServer 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,
|
||||
$smtpServer,
|
||||
$content,
|
||||
$subject,
|
||||
$type,
|
||||
$to,
|
||||
$from,
|
||||
$smtpLogin,
|
||||
$smtpPassword,
|
||||
$smtpPort,
|
||||
$smtpEncryption
|
||||
) {
|
||||
$result = false;
|
||||
|
||||
try {
|
||||
if ($smtpChecked) {
|
||||
if (Tools::strtolower($smtpEncryption) === 'off') {
|
||||
$smtpEncryption = false;
|
||||
}
|
||||
$connection = (new Swift_SmtpTransport(
|
||||
$smtpServer,
|
||||
$smtpPort,
|
||||
$smtpEncryption
|
||||
))
|
||||
->setUsername($smtpLogin)
|
||||
->setPassword($smtpPassword);
|
||||
} else {
|
||||
/**
|
||||
* mail() support was removed from SwiftMailer for security reasons
|
||||
* previously => $connection = \Swift_MailTransport::newInstance();
|
||||
* Use Swift_SendmailTransport instead
|
||||
*
|
||||
* @see https://github.com/swiftmailer/swiftmailer/issues/866
|
||||
*/
|
||||
$connection = new Swift_SendmailTransport();
|
||||
}
|
||||
|
||||
$swift = new Swift_Mailer($connection);
|
||||
$message = new Swift_Message();
|
||||
|
||||
$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('"', '"', $string);
|
||||
}
|
||||
|
||||
$key = str_replace('\'', '\\\'', $string);
|
||||
|
||||
return str_replace(
|
||||
'"',
|
||||
'"',
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
635
classes/Manufacturer.php
Normal file
635
classes/Manufacturer.php
Normal file
@@ -0,0 +1,635 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class ManufacturerCore.
|
||||
*/
|
||||
class ManufacturerCore extends ObjectModel
|
||||
{
|
||||
public $id;
|
||||
|
||||
/** @var string Name */
|
||||
public $name;
|
||||
|
||||
/** @var array<string> Description */
|
||||
public $description;
|
||||
|
||||
/** @var array<string> 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 array<string> Meta title */
|
||||
public $meta_title;
|
||||
|
||||
/** @var array<string> Meta keywords */
|
||||
public $meta_keywords;
|
||||
|
||||
/** @var array<string> Meta description */
|
||||
public $meta_description;
|
||||
|
||||
/** @var bool active */
|
||||
public $active;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'manufacturer',
|
||||
'primary' => 'id_manufacturer',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'name' => ['type' => self::TYPE_STRING, 'validate' => 'isCatalogName', 'required' => true, 'size' => 64],
|
||||
'active' => ['type' => self::TYPE_BOOL],
|
||||
'date_add' => ['type' => self::TYPE_DATE],
|
||||
'date_upd' => ['type' => self::TYPE_DATE],
|
||||
|
||||
/* Lang fields */
|
||||
'description' => ['type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml'],
|
||||
'short_description' => ['type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml'],
|
||||
'meta_title' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255],
|
||||
'meta_description' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 512],
|
||||
'meta_keywords' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName'],
|
||||
],
|
||||
];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'fields' => [
|
||||
'active' => [],
|
||||
'link_rewrite' => ['getter' => 'getLink', 'setter' => false],
|
||||
],
|
||||
'associations' => [
|
||||
'addresses' => [
|
||||
'resource' => 'address',
|
||||
'setter' => false,
|
||||
'fields' => [
|
||||
'id' => ['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) Group::getCurrent()->id);
|
||||
}
|
||||
|
||||
$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 = [];
|
||||
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 = [];
|
||||
$manufacturers = Manufacturer::getManufacturers(false, $idLang);
|
||||
if ($manufacturers && count($manufacturers)) {
|
||||
foreach ($manufacturers as $manufacturer) {
|
||||
if ($format === 'sitemap') {
|
||||
$manufacturersList[] = [
|
||||
'id' => 'manufacturer-page-' . (int) $manufacturer['id_manufacturer'],
|
||||
'label' => $manufacturer['name'],
|
||||
'url' => Context::getContext()->link->getManufacturerLink($manufacturer['id_manufacturer'], $manufacturer['link_rewrite']),
|
||||
'children' => [],
|
||||
];
|
||||
} else {
|
||||
$manufacturersList[] = [
|
||||
'id' => (int) $manufacturer['id_manufacturer'],
|
||||
'link' => Context::getContext()->link->getManufacturerLink($manufacturer['id_manufacturer'], $manufacturer['link_rewrite']),
|
||||
'name' => $manufacturer['name'],
|
||||
'desc' => $manufacturer['description'],
|
||||
'children' => [],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $manufacturersList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return name from id.
|
||||
*
|
||||
* @param int $id_manufacturer Manufacturer ID
|
||||
*
|
||||
* @return string name
|
||||
*/
|
||||
protected static $cacheName = [];
|
||||
|
||||
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, ['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) Group::getCurrent()->id;
|
||||
|
||||
/* 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';
|
||||
|
||||
if ($orderBy !== 'price') {
|
||||
$sql .= '
|
||||
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);
|
||||
$result = array_slice($result, (int) (($p - 1) * $n), (int) $n);
|
||||
}
|
||||
|
||||
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, ['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,
|
||||
false
|
||||
);
|
||||
|
||||
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 = [];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
35
classes/ManufacturerAddress.php
Normal file
35
classes/ManufacturerAddress.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
}
|
||||
881
classes/Media.php
Normal file
881
classes/Media.php
Normal file
@@ -0,0 +1,881 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class MediaCore.
|
||||
*/
|
||||
class MediaCore
|
||||
{
|
||||
public static $jquery_ui_dependencies = [
|
||||
'ui.core' => ['fileName' => 'jquery.ui.core.min.js', 'dependencies' => [], 'theme' => true],
|
||||
'ui.widget' => ['fileName' => 'jquery.ui.widget.min.js', 'dependencies' => [], 'theme' => false],
|
||||
'ui.mouse' => ['fileName' => 'jquery.ui.mouse.min.js', 'dependencies' => ['ui.core', 'ui.widget'], 'theme' => false],
|
||||
'ui.position' => ['fileName' => 'jquery.ui.position.min.js', 'dependencies' => [], 'theme' => false],
|
||||
'ui.draggable' => ['fileName' => 'jquery.ui.draggable.min.js', 'dependencies' => ['ui.core', 'ui.widget', 'ui.mouse'], 'theme' => false],
|
||||
'ui.droppable' => ['fileName' => 'jquery.ui.droppable.min.js', 'dependencies' => ['ui.core', 'ui.widget', 'ui.mouse', 'ui.draggable'], 'theme' => false],
|
||||
'ui.resizable' => ['fileName' => 'jquery.ui.resizable.min.js', 'dependencies' => ['ui.core', 'ui.widget', 'ui.mouse'], 'theme' => true],
|
||||
'ui.selectable' => ['fileName' => 'jquery.ui.selectable.min.js', 'dependencies' => ['ui.core', 'ui.widget', 'ui.mouse'], 'theme' => true],
|
||||
'ui.sortable' => ['fileName' => 'jquery.ui.sortable.min.js', 'dependencies' => ['ui.core', 'ui.widget', 'ui.mouse'], 'theme' => true],
|
||||
'ui.autocomplete' => ['fileName' => 'jquery.ui.autocomplete.min.js', 'dependencies' => ['ui.core', 'ui.widget', 'ui.position', 'ui.menu'], 'theme' => true],
|
||||
'ui.button' => ['fileName' => 'jquery.ui.button.min.js', 'dependencies' => ['ui.core', 'ui.widget'], 'theme' => true],
|
||||
'ui.dialog' => ['fileName' => 'jquery.ui.dialog.min.js', 'dependencies' => ['ui.core', 'ui.widget', 'ui.position', 'ui.button'], 'theme' => true],
|
||||
'ui.menu' => ['fileName' => 'jquery.ui.menu.min.js', 'dependencies' => ['ui.core', 'ui.widget', 'ui.position'], 'theme' => true],
|
||||
'ui.slider' => ['fileName' => 'jquery.ui.slider.min.js', 'dependencies' => ['ui.core', 'ui.widget', 'ui.mouse'], 'theme' => true],
|
||||
'ui.spinner' => ['fileName' => 'jquery.ui.spinner.min.js', 'dependencies' => ['ui.core', 'ui.widget', 'ui.button'], 'theme' => true],
|
||||
'ui.tabs' => ['fileName' => 'jquery.ui.tabs.min.js', 'dependencies' => ['ui.core', 'ui.widget'], 'theme' => true],
|
||||
'ui.datepicker' => ['fileName' => 'jquery.ui.datepicker.min.js', 'dependencies' => ['ui.core'], 'theme' => true],
|
||||
'ui.progressbar' => ['fileName' => 'jquery.ui.progressbar.min.js', 'dependencies' => ['ui.core', 'ui.widget'], 'theme' => true],
|
||||
'ui.tooltip' => ['fileName' => 'jquery.ui.tooltip.min.js', 'dependencies' => ['ui.core', 'ui.widget', 'ui.position', 'effects.core'], 'theme' => true],
|
||||
'ui.accordion' => ['fileName' => 'jquery.ui.accordion.min.js', 'dependencies' => ['ui.core', 'ui.widget', 'effects.core'], 'theme' => true],
|
||||
'effects.core' => ['fileName' => 'jquery.effects.core.min.js', 'dependencies' => [], 'theme' => false],
|
||||
'effects.blind' => ['fileName' => 'jquery.effects.blind.min.js', 'dependencies' => ['effects.core'], 'theme' => false],
|
||||
'effects.bounce' => ['fileName' => 'jquery.effects.bounce.min.js', 'dependencies' => ['effects.core'], 'theme' => false],
|
||||
'effects.clip' => ['fileName' => 'jquery.effects.clip.min.js', 'dependencies' => ['effects.core'], 'theme' => false],
|
||||
'effects.drop' => ['fileName' => 'jquery.effects.drop.min.js', 'dependencies' => ['effects.core'], 'theme' => false],
|
||||
'effects.explode' => ['fileName' => 'jquery.effects.explode.min.js', 'dependencies' => ['effects.core'], 'theme' => false],
|
||||
'effects.fade' => ['fileName' => 'jquery.effects.fade.min.js', 'dependencies' => ['effects.core'], 'theme' => false],
|
||||
'effects.fold' => ['fileName' => 'jquery.effects.fold.min.js', 'dependencies' => ['effects.core'], 'theme' => false],
|
||||
'effects.highlight' => ['fileName' => 'jquery.effects.highlight.min.js', 'dependencies' => ['effects.core'], 'theme' => false],
|
||||
'effects.pulsate' => ['fileName' => 'jquery.effects.pulsate.min.js', 'dependencies' => ['effects.core'], 'theme' => false],
|
||||
'effects.scale' => ['fileName' => 'jquery.effects.scale.min.js', 'dependencies' => ['effects.core'], 'theme' => false],
|
||||
'effects.shake' => ['fileName' => 'jquery.effects.shake.min.js', 'dependencies' => ['effects.core'], 'theme' => false],
|
||||
'effects.slide' => ['fileName' => 'jquery.effects.slide.min.js', 'dependencies' => ['effects.core'], 'theme' => false],
|
||||
'effects.transfer' => ['fileName' => 'jquery.effects.transfer.min.js', 'dependencies' => ['effects.core'], 'theme' => false],
|
||||
];
|
||||
|
||||
private static $jquery_ui_datepicker_iso_code = [
|
||||
'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 = [];
|
||||
|
||||
/**
|
||||
* @var array list of javascript inline scripts
|
||||
*/
|
||||
protected static $inline_script = [];
|
||||
|
||||
/**
|
||||
* @var array list of javascript external scripts
|
||||
*/
|
||||
protected static $inline_script_src = [];
|
||||
|
||||
/**
|
||||
* @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 = [])
|
||||
{
|
||||
Media::$current_css_file = $fileUri;
|
||||
|
||||
if (strlen($cssContent) > 0) {
|
||||
$cssContent = Minify_CSSmin::minify($cssContent);
|
||||
$limit = Media::getBackTrackLimit();
|
||||
$cssContent = preg_replace_callback(Media::$pattern_callback, ['Media', 'replaceByAbsoluteURL'], $cssContent, $limit);
|
||||
$cssContent = str_replace('\'images_ie/', '\'images/', $cssContent);
|
||||
$cssContent = preg_replace_callback('#(AlphaImageLoader\(src=\')([^\']*\',)#s', ['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(['/', '\\'], DIRECTORY_SEPARATOR, _PS_CORE_DIR_), __PS_BASE_URI__, $mediaUri), '/\\');
|
||||
$mediaUri = '/' . ltrim(str_replace(str_replace(['/', '\\'], 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 [$mediaUri => $cssMediaType];
|
||||
}
|
||||
|
||||
return $mediaUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* return jquery path.
|
||||
*
|
||||
* @param mixed $version
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @deprecated 1.7.7 jQuery is always included, this method should no longer be used
|
||||
*/
|
||||
public static function getJqueryPath($version = null, $folder = null, $minifier = true)
|
||||
{
|
||||
@trigger_error(
|
||||
'Media::getJqueryPath() is deprecated since version 1.7.7.0, jquery is always included',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
$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 = [];
|
||||
|
||||
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 = ['js' => [], 'css' => []];
|
||||
$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 = [];
|
||||
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'] = [$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 = ['js' => [], 'css' => []];
|
||||
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 = [];
|
||||
$externalCssFiles = [];
|
||||
$compressedCssFiles = [];
|
||||
$compressedCssFilesNotFound = [];
|
||||
$compressedCssFilesInfos = [];
|
||||
$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] = [];
|
||||
}
|
||||
|
||||
$infos = [];
|
||||
$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] = ['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 = [
|
||||
'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 = [];
|
||||
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 = [];
|
||||
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 = [];
|
||||
$jsFilesInfos = [];
|
||||
$jsFilesDate = 0;
|
||||
$compressedJsFilename = '';
|
||||
$jsExternalFiles = [];
|
||||
$protocolLink = Tools::getCurrentUrlProtocolPrefix();
|
||||
$cachePath = _PS_THEME_DIR_ . 'cache/';
|
||||
|
||||
// get js files infos
|
||||
foreach ($jsFiles as $filename) {
|
||||
if (Validate::isAbsoluteUrl($filename)) {
|
||||
$jsExternalFiles[] = $filename;
|
||||
} else {
|
||||
$infos = [];
|
||||
$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([$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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $output
|
||||
*
|
||||
* @return string|string[]|null
|
||||
*/
|
||||
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 = [
|
||||
'#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 = [];
|
||||
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, ['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
193
classes/Message.php
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'message',
|
||||
'primary' => 'id_message',
|
||||
'fields' => [
|
||||
'message' => ['type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'required' => true, 'size' => 1600],
|
||||
'id_cart' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId'],
|
||||
'id_order' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId'],
|
||||
'id_customer' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId'],
|
||||
'id_employee' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId'],
|
||||
'private' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
],
|
||||
];
|
||||
|
||||
protected $webserviceParameters = [
|
||||
'fields' => [
|
||||
'id_cart' => [
|
||||
'xlink_resource' => 'carts',
|
||||
],
|
||||
'id_order' => [
|
||||
'xlink_resource' => 'orders',
|
||||
],
|
||||
'id_customer' => [
|
||||
'xlink_resource' => 'customers',
|
||||
],
|
||||
'id_employee' => [
|
||||
'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;
|
||||
}
|
||||
}
|
||||
545
classes/Meta.php
Normal file
545
classes/Meta.php
Normal file
@@ -0,0 +1,545 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
use PrestaShop\PrestaShop\Adapter\Presenter\Object\ObjectPresenter;
|
||||
use Symfony\Component\HttpFoundation\IpUtils;
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'meta',
|
||||
'primary' => 'id_meta',
|
||||
'multilang' => true,
|
||||
'multilang_shop' => true,
|
||||
'fields' => [
|
||||
'page' => ['type' => self::TYPE_STRING, 'validate' => 'isFileName', 'required' => true, 'size' => 64],
|
||||
'configurable' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
|
||||
|
||||
/* Lang fields */
|
||||
'title' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 128],
|
||||
'description' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255],
|
||||
'keywords' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255],
|
||||
'url_rewrite' => ['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 = [];
|
||||
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', [], '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', [], 'Admin.Notifications.Error')));
|
||||
}
|
||||
|
||||
$files = array_values(array_unique(array_merge($files, $overrideFiles)));
|
||||
|
||||
// Exclude pages forbidden
|
||||
$exludePages = [
|
||||
'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() : [];
|
||||
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)', [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')
|
||||
|| IpUtils::checkIp(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
177
classes/Notification.php
Normal file
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class NotificationCore.
|
||||
*/
|
||||
class NotificationCore
|
||||
{
|
||||
public $types;
|
||||
|
||||
/**
|
||||
* NotificationCore constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->types = ['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 = [];
|
||||
$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 = ['total' => $total, 'results' => []];
|
||||
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'][] = [
|
||||
'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::getContextLocale(Context::getContext())->formatPrice((float) $value['total_paid'], Currency::getIsoCodeById((int) $value['id_currency'])) : 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;
|
||||
}
|
||||
}
|
||||
2225
classes/ObjectModel.php
Normal file
2225
classes/ObjectModel.php
Normal file
File diff suppressed because it is too large
Load Diff
601
classes/Pack.php
Normal file
601
classes/Pack.php
Normal file
@@ -0,0 +1,601 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Domain\Product\Pack\ValueObject\PackStockType;
|
||||
use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductType;
|
||||
|
||||
class PackCore extends Product
|
||||
{
|
||||
/**
|
||||
* Only decrement pack quantity.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const STOCK_TYPE_PACK_ONLY = PackStockType::STOCK_TYPE_PACK_ONLY;
|
||||
|
||||
/**
|
||||
* Only decrement pack products quantities.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const STOCK_TYPE_PRODUCTS_ONLY = PackStockType::STOCK_TYPE_PRODUCTS_ONLY;
|
||||
|
||||
/**
|
||||
* Decrement pack quantity and pack products quantities.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const STOCK_TYPE_PACK_BOTH = PackStockType::STOCK_TYPE_BOTH;
|
||||
|
||||
/**
|
||||
* Use pack quantity default setting.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const STOCK_TYPE_DEFAULT = PackStockType::STOCK_TYPE_DEFAULT;
|
||||
|
||||
protected static $cachePackItems = [];
|
||||
protected static $cacheIsPack = [];
|
||||
protected static $cacheIsPacked = [];
|
||||
|
||||
public static function resetStaticCache()
|
||||
{
|
||||
self::$cachePackItems = [];
|
||||
self::$cacheIsPack = [];
|
||||
self::$cacheIsPacked = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 int $id_product
|
||||
* @param int|bool $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 [];
|
||||
}
|
||||
|
||||
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 = [];
|
||||
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, pa.`reference` AS attribute_reference
|
||||
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'];
|
||||
$p->reference = $combination['attribute_reference'];
|
||||
}
|
||||
}
|
||||
$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 (this method already have decreased products in cart).
|
||||
*
|
||||
* @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 = [
|
||||
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, [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, [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, $item->id_pack_product_attribute ?: null, null, $cart, $idCustomization);
|
||||
$nbPackAvailableForItem = (int) 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 [];
|
||||
}
|
||||
|
||||
$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 pa.`reference` AS attribute_reference, 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['reference'] = $attr_name[0]['attribute_reference'] ?? '';
|
||||
|
||||
$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 = [];
|
||||
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 [];
|
||||
}
|
||||
|
||||
$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 [];
|
||||
}
|
||||
|
||||
$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 = [];
|
||||
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', ['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', ['cache_is_pack' => 1, 'product_type' => ProductType::TYPE_PACK], 'id_product = ' . (int) $id_product) &&
|
||||
Db::getInstance()->insert('pack', [
|
||||
'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 contains 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 [];
|
||||
}
|
||||
|
||||
$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 = [];
|
||||
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
147
classes/Page.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class PageCore.
|
||||
*/
|
||||
class PageCore extends ObjectModel
|
||||
{
|
||||
public $id_page_type;
|
||||
public $id_object;
|
||||
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'page',
|
||||
'primary' => 'id_page',
|
||||
'fields' => [
|
||||
'id_page_type' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_object' => ['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 = [
|
||||
'product' => 'id_product',
|
||||
'category' => 'id_category',
|
||||
'order' => 'step',
|
||||
'manufacturer' => 'id_manufacturer',
|
||||
];
|
||||
|
||||
$where = '';
|
||||
$insertData = [
|
||||
'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 (!empty($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', ['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',
|
||||
[
|
||||
'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
36
classes/PaymentFree.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class PaymentFree
|
||||
* Simple class to allow free order.
|
||||
*/
|
||||
class PaymentFree extends PaymentModule
|
||||
{
|
||||
public $active = 1;
|
||||
public $name = 'free_order';
|
||||
public $displayName = 'Free order';
|
||||
}
|
||||
1255
classes/PaymentModule.php
Normal file
1255
classes/PaymentModule.php
Normal file
File diff suppressed because it is too large
Load Diff
122
classes/PhpEncryption.php
Normal file
122
classes/PhpEncryption.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
166
classes/PhpEncryptionEngine.php
Normal file
166
classes/PhpEncryptionEngine.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
168
classes/PhpEncryptionLegacyEngine.php
Normal file
168
classes/PhpEncryptionLegacyEngine.php
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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
|
||||
);
|
||||
}
|
||||
}
|
||||
349
classes/PrestaShopAutoload.php
Normal file
349
classes/PrestaShopAutoload.php
Normal file
@@ -0,0 +1,349 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
|
||||
public $_include_override_path = true;
|
||||
|
||||
protected static $class_aliases = [
|
||||
'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_CACHE_DIR_ . 'class_index.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Namespaced class stub file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getNamespacedStubFileIndex()
|
||||
{
|
||||
return _PS_CACHE_DIR_ . 'namespaced_class_stub.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Class stub file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getStubFileIndex()
|
||||
{
|
||||
return _PS_CACHE_DIR_ . '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 = [];
|
||||
$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);
|
||||
|
||||
$namePattern = '[a-z_\x7f-\xff][a-z0-9_\x7f-\xff]*';
|
||||
$nameWithNsPattern = '(?:\\\\?(?:' . $namePattern . '\\\\)*' . $namePattern . ')';
|
||||
$pattern = '~(?<!\w)((abstract\s+)?class|interface)\s+(?P<classname>' . basename($file, '.php') . '(?:Core)?)'
|
||||
. '(?:\s+extends\s+' . $nameWithNsPattern . ')?(?:\s+implements\s+' . $nameWithNsPattern . '(?:\s*,\s*' . $nameWithNsPattern . ')*)?\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']] = [
|
||||
'path' => $path . $file,
|
||||
'type' => trim($m[1]),
|
||||
'override' => $hostMode,
|
||||
];
|
||||
|
||||
if (substr($m['classname'], -4) == 'Core') {
|
||||
$classes[substr($m['classname'], 0, -4)] = [
|
||||
'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([PrestaShopAutoload::getInstance(), 'load']);
|
||||
350
classes/PrestaShopBackup.php
Normal file
350
classes/PrestaShopBackup.php
Normal file
@@ -0,0 +1,350 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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.', [], '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.', [], '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', [], 'Admin.Advparameters.Notification') . ' ' . ($this->id ? '"' . $this->id . '"' :
|
||||
Context::getContext()->getTranslator()->trans('Invalid ID', [], '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 = [_DB_PREFIX_ . 'connections', _DB_PREFIX_ . 'connections_page', _DB_PREFIX_
|
||||
. 'connections_source', _DB_PREFIX_ . 'guest', _DB_PREFIX_ . 'statssearch',
|
||||
];
|
||||
} else {
|
||||
$ignoreInsertTable = [];
|
||||
}
|
||||
|
||||
// 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', [], '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 \'utf8mb4\';');
|
||||
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', [$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.', [], 'Admin.Advparameters.Notification');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
797
classes/PrestaShopCollection.php
Normal file
797
classes/PrestaShopCollection.php
Normal file
@@ -0,0 +1,797 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
|
||||
/**
|
||||
* @var DbQuery
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* @var array Collection of objects in an array
|
||||
*/
|
||||
protected $results = [];
|
||||
|
||||
/**
|
||||
* @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 = [];
|
||||
protected $alias = [];
|
||||
protected $alias_iterator = 0;
|
||||
protected $join_list = [];
|
||||
protected $association_definition = [];
|
||||
|
||||
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] = [
|
||||
'table' => ($definition['is_lang']) ? $definition['table'] . '_lang' : $definition['table'],
|
||||
'alias' => $this->generateAlias($association),
|
||||
'on' => [],
|
||||
];
|
||||
}
|
||||
|
||||
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|bool
|
||||
*/
|
||||
public function getFirst()
|
||||
{
|
||||
$this->getAll();
|
||||
if (!count($this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the last result.
|
||||
*
|
||||
* @return ObjectModel|false
|
||||
*/
|
||||
public function getLast()
|
||||
{
|
||||
$this->getAll();
|
||||
if (!count($this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this[count($this) - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
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] = [
|
||||
'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];
|
||||
}
|
||||
}
|
||||
250
classes/PrestaShopLogger.php
Normal file
250
classes/PrestaShopLogger.php
Normal file
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class PrestaShopLoggerCore.
|
||||
*/
|
||||
class PrestaShopLoggerCore extends ObjectModel
|
||||
{
|
||||
/**
|
||||
* List of log level types.
|
||||
*/
|
||||
const LOG_SEVERITY_LEVEL_INFORMATIVE = 1;
|
||||
const LOG_SEVERITY_LEVEL_WARNING = 2;
|
||||
const LOG_SEVERITY_LEVEL_ERROR = 3;
|
||||
const LOG_SEVERITY_LEVEL_MAJOR = 4;
|
||||
|
||||
/** @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 Employee ID */
|
||||
public $id_employee;
|
||||
|
||||
/** @var string Object creation date */
|
||||
public $date_add;
|
||||
|
||||
/** @var string Object last modification date */
|
||||
public $date_upd;
|
||||
|
||||
/** @var int|null Shop ID */
|
||||
public $id_shop;
|
||||
|
||||
/** @var int|null Shop group ID */
|
||||
public $id_shop_group;
|
||||
|
||||
/** @var int|null Language ID */
|
||||
public $id_lang;
|
||||
|
||||
/** @var bool In all shops */
|
||||
public $in_all_shops;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'log',
|
||||
'primary' => 'id_log',
|
||||
'fields' => [
|
||||
'severity' => ['type' => self::TYPE_INT, 'validate' => 'isInt', 'required' => true],
|
||||
'error_code' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
|
||||
'message' => ['type' => self::TYPE_STRING, 'validate' => 'isString', 'required' => true],
|
||||
'object_id' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
|
||||
'id_shop' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'allow_null' => true],
|
||||
'id_shop_group' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'allow_null' => true],
|
||||
'id_lang' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'allow_null' => true],
|
||||
'in_all_shops' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'id_employee' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
|
||||
'object_type' => ['type' => self::TYPE_STRING, 'validate' => 'isName'],
|
||||
'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
'date_upd' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
],
|
||||
];
|
||||
|
||||
protected static $is_present = [];
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$config_severity = (int) Configuration::get('PS_LOGS_BY_EMAIL');
|
||||
if (!empty($config_severity) && $config_severity <= (int) $log->severity) {
|
||||
$to = array_map('trim', explode(',', Configuration::get('PS_LOGS_EMAIL_RECEIVERS')));
|
||||
$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',
|
||||
[],
|
||||
'Emails.Subject',
|
||||
$language->locale
|
||||
),
|
||||
[],
|
||||
$to
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
||||
$context = Context::getContext();
|
||||
|
||||
if ($idEmployee === null && isset($context->employee->id)) {
|
||||
$idEmployee = $context->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;
|
||||
}
|
||||
|
||||
$log->id_lang = (int) $context->language->id ?? null;
|
||||
$log->in_all_shops = Shop::getContext() == Shop::CONTEXT_ALL;
|
||||
$log->id_shop = (Shop::getContext() == Shop::CONTEXT_SHOP) ? (int) $context->shop->getContextualShopId() : null;
|
||||
$log->id_shop_group = (Shop::getContext() == Shop::CONTEXT_GROUP) ? (int) $context->shop->getContextShopGroupID() : null;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 .
|
||||
$this->id_shop .
|
||||
$this->id_shop_group .
|
||||
$this->id_lang .
|
||||
$this->in_all_shops
|
||||
);
|
||||
}
|
||||
|
||||
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(
|
||||
(new DbQuery())
|
||||
->select('COUNT(*)')
|
||||
->from('log', 'l')
|
||||
->where('message = "' . pSQL($this->message) . '"')
|
||||
->where('severity = ' . (int) $this->severity)
|
||||
->where('error_code = ' . (int) $this->error_code)
|
||||
->where('object_type = "' . pSQL($this->object_type) . '"')
|
||||
->where('object_id = ' . (int) $this->object_id)
|
||||
->where('id_shop = ' . (int) $this->id_shop)
|
||||
->where('id_shop_group = ' . (int) $this->id_shop_group)
|
||||
->where('id_lang = ' . (int) $this->id_lang)
|
||||
->where('in_all_shops = ' . (int) $this->in_all_shops)
|
||||
);
|
||||
}
|
||||
|
||||
return self::$is_present[$this->getHash()];
|
||||
}
|
||||
}
|
||||
8385
classes/Product.php
Normal file
8385
classes/Product.php
Normal file
File diff suppressed because it is too large
Load Diff
120
classes/ProductAssembler.php
Normal file
120
classes/ProductAssembler.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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.*,
|
||||
ps.*,
|
||||
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
|
||||
LEFT JOIN {$prefix}product_shop ps
|
||||
ON ps.id_product = p.id_product
|
||||
AND ps.id_shop = $idShop
|
||||
WHERE p.id_product = $idProduct
|
||||
LIMIT 1";
|
||||
|
||||
$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
|
||||
);
|
||||
}
|
||||
}
|
||||
337
classes/ProductDownload.php
Normal file
337
classes/ProductDownload.php
Normal file
@@ -0,0 +1,337 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Domain\Product\VirtualProductFile\VirtualProductFileSettings;
|
||||
|
||||
/**
|
||||
* 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 int NbDaysAccessible how many days the customer can access to file */
|
||||
public $nb_days_accessible;
|
||||
|
||||
/** @var int 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 = [];
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'product_download',
|
||||
'primary' => 'id_product_download',
|
||||
'fields' => [
|
||||
'id_product' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'display_filename' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => VirtualProductFileSettings::MAX_DISPLAY_FILENAME_LENGTH],
|
||||
'filename' => ['type' => self::TYPE_STRING, 'validate' => 'isSha1', 'size' => VirtualProductFileSettings::MAX_FILENAME_LENGTH],
|
||||
'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
|
||||
'date_expiration' => ['type' => self::TYPE_DATE, 'validate' => 'isDateOrNull'],
|
||||
'nb_days_accessible' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'size' => 10],
|
||||
'nb_downloadable' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'size' => 10],
|
||||
'active' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'],
|
||||
'is_shareable' => ['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');
|
||||
}
|
||||
}
|
||||
108
classes/ProductPresenterFactory.php
Normal file
108
classes/ProductPresenterFactory.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
use PrestaShop\PrestaShop\Adapter\Configuration as AdapterConfiguration;
|
||||
use PrestaShop\PrestaShop\Adapter\HookManager;
|
||||
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');
|
||||
$settings->showLabelOOSListingPages = (bool) Configuration::get('PS_SHOW_LABEL_OOS_LISTING_PAGES');
|
||||
|
||||
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(),
|
||||
new HookManager(),
|
||||
new AdapterConfiguration()
|
||||
);
|
||||
}
|
||||
}
|
||||
291
classes/ProductSale.php
Normal file
291
classes/ProductSale.php
Normal file
@@ -0,0 +1,291 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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) Group::getCurrent()->id) . ')
|
||||
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) Group::getCurrent()->id) . ')
|
||||
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;
|
||||
}
|
||||
}
|
||||
280
classes/ProductSupplier.php
Normal file
280
classes/ProductSupplier.php
Normal file
@@ -0,0 +1,280 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'product_supplier',
|
||||
'primary' => 'id_product_supplier',
|
||||
'fields' => [
|
||||
'product_supplier_reference' => ['type' => self::TYPE_STRING, 'validate' => 'isReference', 'size' => 64],
|
||||
'id_product' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_product_attribute' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'id_supplier' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
|
||||
'product_supplier_price_te' => ['type' => self::TYPE_FLOAT, 'validate' => 'isPrice'],
|
||||
'id_currency' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId'],
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$webserviceParameters
|
||||
*/
|
||||
protected $webserviceParameters = [
|
||||
'objectsNodeName' => 'product_suppliers',
|
||||
'objectNodeName' => 'product_supplier',
|
||||
'fields' => [
|
||||
'id_product' => ['xlink_resource' => 'products'],
|
||||
'id_product_attribute' => ['xlink_resource' => 'combinations'],
|
||||
'id_supplier' => ['xlink_resource' => 'suppliers'],
|
||||
'id_currency' => ['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 string
|
||||
*/
|
||||
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 int
|
||||
*/
|
||||
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 (empty($row)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
265
classes/Profile.php
Normal file
265
classes/Profile.php
Normal file
@@ -0,0 +1,265 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class ProfileCore.
|
||||
*/
|
||||
class ProfileCore extends ObjectModel
|
||||
{
|
||||
const ALLOWED_PROFILE_TYPE_CHECK = [
|
||||
'id_tab',
|
||||
'class_name',
|
||||
];
|
||||
|
||||
/** @var array<string> Name */
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'profile',
|
||||
'primary' => 'id_profile',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
/* Lang fields */
|
||||
'name' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 32],
|
||||
],
|
||||
];
|
||||
|
||||
protected static $_cache_accesses = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct($id = null, $idLang = null, $idShop = null, $translator = null)
|
||||
{
|
||||
parent::__construct($id, $idLang, $idShop, $translator);
|
||||
|
||||
$this->image_dir = _PS_PROFILE_IMG_DIR_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getProfileImage(): ?string
|
||||
{
|
||||
$path = $this->image_dir . $this->id . '.jpg';
|
||||
|
||||
return file_exists($path)
|
||||
? Context::getContext()->link->getMediaLink(
|
||||
str_replace($this->image_dir, _THEME_PROFILE_DIR_, $path)
|
||||
)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 array|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 array|false
|
||||
*/
|
||||
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] = [];
|
||||
}
|
||||
|
||||
if (!isset(self::$_cache_accesses[$idProfile][$type])) {
|
||||
self::$_cache_accesses[$idProfile][$type] = [];
|
||||
// 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 = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @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(
|
||||
[
|
||||
'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;
|
||||
}
|
||||
}
|
||||
107
classes/QqUploadedFileForm.php
Normal file
107
classes/QqUploadedFileForm.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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 ['error' => Context::getContext()->getTranslator()->trans('Cannot add image because product creation failed.', [], '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 ['error' => Context::getContext()->getTranslator()->trans('Error on image caption: "%1s" is not a valid caption.', [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 ['error' => $validate];
|
||||
}
|
||||
if (!$image->add()) {
|
||||
return ['error' => Context::getContext()->getTranslator()->trans('Error while creating additional image', [], '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 ['error' => Context::getContext()->getTranslator()->trans('An error occurred while attempting to create a new folder.', [], 'Admin.Notifications.Error')];
|
||||
}
|
||||
if (!($tmpName = tempnam(_PS_TMP_IMG_DIR_, 'PS')) || !move_uploaded_file($_FILES['qqfile']['tmp_name'], $tmpName)) {
|
||||
return ['error' => Context::getContext()->getTranslator()->trans('An error occurred while uploading the image.', [], 'Admin.Notifications.Error')];
|
||||
} elseif (!ImageManager::resize($tmpName, $new_path . '.' . $image->image_format)) {
|
||||
return ['error' => Context::getContext()->getTranslator()->trans('An error occurred while copying the image.', [], '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 ['error' => Context::getContext()->getTranslator()->trans('An error occurred while copying this image: %s', [stripslashes($imageType['name'])], 'Admin.Notifications.Error')];
|
||||
}
|
||||
}
|
||||
}
|
||||
unlink($tmpName);
|
||||
Hook::exec('actionWatermark', ['id_image' => $id_image, 'id_product' => $id_product]);
|
||||
|
||||
if (!$image->update()) {
|
||||
return ['error' => Context::getContext()->getTranslator()->trans('Error while updating the status.', [], 'Admin.Notifications.Error')];
|
||||
}
|
||||
$img = ['id_image' => $image->id, 'position' => $image->position, 'cover' => $image->cover, 'name' => $this->getName(), 'legend' => $image->legend];
|
||||
|
||||
return ['success' => $img];
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $_FILES['qqfile']['name'];
|
||||
}
|
||||
|
||||
public function getSize()
|
||||
{
|
||||
return $_FILES['qqfile']['size'];
|
||||
}
|
||||
}
|
||||
135
classes/QqUploadedFileXhr.php
Normal file
135
classes/QqUploadedFileXhr.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 ['error' => Context::getContext()->getTranslator()->trans('Cannot add image because product creation failed.', [], '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 ['error' => Context::getContext()->getTranslator()->trans('Error on image caption: "%1s" is not a valid caption.', [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 ['error' => $validate];
|
||||
}
|
||||
if (!$image->add()) {
|
||||
return ['error' => Context::getContext()->getTranslator()->trans('Error while creating additional image', [], '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 ['error' => Context::getContext()->getTranslator()->trans('An error occurred while attempting to create a new folder.', [], 'Admin.Notifications.Error')];
|
||||
}
|
||||
if (!($tmpName = tempnam(_PS_TMP_IMG_DIR_, 'PS')) || !$this->upload($tmpName)) {
|
||||
return ['error' => Context::getContext()->getTranslator()->trans('An error occurred while uploading the image.', [], 'Admin.Notifications.Error')];
|
||||
} elseif (!ImageManager::resize($tmpName, $new_path . '.' . $image->image_format)) {
|
||||
return ['error' => Context::getContext()->getTranslator()->trans('An error occurred while uploading the image.', [], '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 ['error' => Context::getContext()->getTranslator()->trans('An error occurred while copying this image: %s', [stripslashes($imageType['name'])], 'Admin.Notifications.Error')];
|
||||
}
|
||||
}
|
||||
}
|
||||
unlink($tmpName);
|
||||
Hook::exec('actionWatermark', ['id_image' => $id_image, 'id_product' => $id_product]);
|
||||
|
||||
if (!$image->update()) {
|
||||
return ['error' => Context::getContext()->getTranslator()->trans('Error while updating the status.', [], 'Admin.Notifications.Error')];
|
||||
}
|
||||
$img = ['id_image' => $image->id, 'position' => $image->position, 'cover' => $image->cover, 'name' => $this->getName(), 'legend' => $image->legend];
|
||||
|
||||
return ['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
138
classes/QuickAccess.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'quick_access',
|
||||
'primary' => 'id_quick_access',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'link' => ['type' => self::TYPE_STRING, 'validate' => 'isUrl', 'required' => true, 'size' => 255],
|
||||
'new_window' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true],
|
||||
|
||||
/* Lang fields */
|
||||
'name' => ['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', get_object_vars($this))) {
|
||||
throw new PrestaShopException('property "new_window" is missing in object ' . get_class($this));
|
||||
}
|
||||
|
||||
$this->setFieldsToUpdate(['new_window' => true]);
|
||||
|
||||
$this->new_window = !(int) $this->new_window;
|
||||
|
||||
return $this->update(false);
|
||||
}
|
||||
}
|
||||
392
classes/Referrer.php
Normal file
392
classes/Referrer.php
Normal file
@@ -0,0 +1,392 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'table' => 'referrer',
|
||||
'primary' => 'id_referrer',
|
||||
'fields' => [
|
||||
'name' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 64],
|
||||
'passwd' => ['type' => self::TYPE_STRING, 'validate' => 'isPasswd', 'size' => 255],
|
||||
'http_referer_regexp' => ['type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'size' => 64],
|
||||
'request_uri_regexp' => ['type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'size' => 64],
|
||||
'http_referer_like' => ['type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'size' => 64],
|
||||
'request_uri_like' => ['type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'size' => 64],
|
||||
'http_referer_regexp_not' => ['type' => self::TYPE_STRING, 'validate' => 'isCleanHtml'],
|
||||
'request_uri_regexp_not' => ['type' => self::TYPE_STRING, 'validate' => 'isCleanHtml'],
|
||||
'http_referer_like_not' => ['type' => self::TYPE_STRING, 'validate' => 'isCleanHtml'],
|
||||
'request_uri_like_not' => ['type' => self::TYPE_STRING, 'validate' => 'isCleanHtml'],
|
||||
'base_fee' => ['type' => self::TYPE_FLOAT, 'validate' => 'isFloat'],
|
||||
'percent_fee' => ['type' => self::TYPE_FLOAT, 'validate' => 'isPercentage'],
|
||||
'click_fee' => ['type' => self::TYPE_FLOAT, 'validate' => 'isFloat'],
|
||||
'date_add' => ['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([['id_referrer' => $this->id]]);
|
||||
Referrer::refreshIndex([['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 = [];
|
||||
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 ['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', [
|
||||
'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 = [
|
||||
'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' => Context::getContext()->getCurrentLocale()->formatPrice($statsSales['sales'], $currency->iso_code),
|
||||
'cart' => Context::getContext()->getCurrentLocale()->formatPrice(((int) $statsSales['orders'] ? $statsSales['sales'] / (int) $statsSales['orders'] : 0), $currency->iso_code),
|
||||
'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' => Context::getContext()->getCurrentLocale()->formatPrice((int) $statsVisits['visits'] * $referrer->click_fee, $currency->iso_code),
|
||||
'base_fee' => Context::getContext()->getCurrentLocale()->formatPrice($statsSales['orders'] * $referrer->base_fee, $currency->iso_code),
|
||||
'percent_fee' => Context::getContext()->getCurrentLocale()->formatPrice($statsSales['sales'] * $referrer->percent_fee / 100, $currency->iso_code),
|
||||
];
|
||||
|
||||
return json_encode([$jsonArray]);
|
||||
}
|
||||
}
|
||||
675
classes/RequestSql.php
Normal file
675
classes/RequestSql.php
Normal file
@@ -0,0 +1,675 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class RequestSqlCore.
|
||||
*/
|
||||
class RequestSqlCore extends ObjectModel
|
||||
{
|
||||
public $name;
|
||||
public $sql;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'request_sql',
|
||||
'primary' => 'id_request_sql',
|
||||
'fields' => [
|
||||
'name' => ['type' => self::TYPE_STRING, 'validate' => 'isString', 'required' => true, 'size' => 200],
|
||||
'sql' => ['type' => self::TYPE_SQL, 'validate' => 'isString', 'required' => true],
|
||||
],
|
||||
];
|
||||
|
||||
/** @var array : List of params to tested */
|
||||
public $tested = [
|
||||
'required' => ['SELECT', 'FROM'],
|
||||
'option' => ['WHERE', 'ORDER', 'LIMIT', 'HAVING', 'GROUP', 'UNION'],
|
||||
'operator' => [
|
||||
'AND', '&&', 'BETWEEN', 'AND', 'BINARY', '&', '~', '|', '^', 'CASE', 'WHEN', 'END', 'DIV', '/', '<=>', '=', '>=',
|
||||
'>', 'IS', 'NOT', 'NULL', '<<', '<=', '<', 'LIKE', '-', '%', '!=', '<>', 'REGEXP', '!', '||', 'OR', '+', '>>', 'RLIKE', 'SOUNDS', '*',
|
||||
'-', 'XOR', 'IN',
|
||||
],
|
||||
'function' => [
|
||||
'AVG', 'SUM', 'COUNT', 'MIN', 'MAX', 'STDDEV', 'STDDEV_SAMP', 'STDDEV_POP', 'VARIANCE', 'VAR_SAMP', 'VAR_POP',
|
||||
'GROUP_CONCAT', 'BIT_AND', 'BIT_OR', 'BIT_XOR',
|
||||
],
|
||||
'unauthorized' => [
|
||||
'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 = [
|
||||
'passwd' => '*******************',
|
||||
'secure_key' => '*******************',
|
||||
];
|
||||
|
||||
/** @var array : list of errors */
|
||||
public $error_sql = [];
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
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 array|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 array $attrs
|
||||
* @param array $from
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function cutJoin($attrs, $from)
|
||||
{
|
||||
$tab = [];
|
||||
|
||||
foreach ($attrs as $attr) {
|
||||
if (in_array($attr['expr_type'], ['operator', 'const'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($attr['sub_tree'])) {
|
||||
foreach ($attr['sub_tree'] as $treeItem) {
|
||||
if ($treeItem['expr_type'] !== 'colref') {
|
||||
continue;
|
||||
}
|
||||
if ($attribut = $this->cutAttribute($treeItem['base_expr'], $from)) {
|
||||
$tab[] = $attribut;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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 = [];
|
||||
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(['`', '(', ')'], '', $matches[0][0]));
|
||||
if ($table = $this->returnNameTable($tab[0], $from)) {
|
||||
return [
|
||||
'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(['`', '(', ')'], '', $matches[0][0]);
|
||||
if ($table = $this->returnNameTable(false, $from, $attr)) {
|
||||
return [
|
||||
'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']) || !isset($table['table'])) {
|
||||
continue;
|
||||
}
|
||||
if ($table['alias']['no_quotes'] == $alias || $table['alias']['no_quotes']['parts'][0] == $alias) {
|
||||
return [$table['table']];
|
||||
}
|
||||
}
|
||||
} elseif (count($tables) > 1) {
|
||||
if ($attr !== null) {
|
||||
$tab = [];
|
||||
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 = [];
|
||||
foreach ($tables as $table) {
|
||||
$tab[] = $table['table'];
|
||||
}
|
||||
|
||||
return $tab;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an attributes exists in a table.
|
||||
*
|
||||
* @param string $attr
|
||||
* @param array $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 array $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')) {
|
||||
$attrs = $this->cutJoin($table['ref_clause'], $from);
|
||||
if (is_array($attrs)) {
|
||||
foreach ($attrs as $attr) {
|
||||
if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) {
|
||||
$this->error_sql['checkedFrom']['attribut'] = [$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'] = [$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'] = [$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'] = [$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'] = [$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'] = [$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'] = [$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 array $group
|
||||
* @param array $from
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function checkedGroupBy($group, $from)
|
||||
{
|
||||
$group = $group[0];
|
||||
if ($group['expr_type'] == 'colref') {
|
||||
if ($attr = $this->cutAttribute(trim($group['base_expr']), $from)) {
|
||||
if (!$this->attributExistInTable($attr['attribut'], $attr['table'])) {
|
||||
$this->error_sql['checkedGroupBy']['attribut'] = [$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
83
classes/Risk.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class RiskCore.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
class RiskCore extends ObjectModel
|
||||
{
|
||||
public $id;
|
||||
public $id_risk;
|
||||
public $name;
|
||||
public $color;
|
||||
public $percent;
|
||||
|
||||
public static $definition = [
|
||||
'table' => 'risk',
|
||||
'primary' => 'id_risk',
|
||||
'multilang' => true,
|
||||
'fields' => [
|
||||
'name' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isString', 'required' => true, 'size' => 20],
|
||||
'color' => ['type' => self::TYPE_STRING, 'validate' => 'isColor', 'size' => 32],
|
||||
'percent' => ['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;
|
||||
}
|
||||
}
|
||||
1166
classes/Search.php
Normal file
1166
classes/Search.php
Normal file
File diff suppressed because it is too large
Load Diff
82
classes/SearchEngine.php
Normal file
82
classes/SearchEngine.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class SearchEngineCore.
|
||||
*/
|
||||
class SearchEngineCore extends ObjectModel
|
||||
{
|
||||
public $server;
|
||||
public $getvar;
|
||||
|
||||
/**
|
||||
* @see ObjectModel::$definition
|
||||
*/
|
||||
public static $definition = [
|
||||
'table' => 'search_engine',
|
||||
'primary' => 'id_search_engine',
|
||||
'fields' => [
|
||||
'server' => ['type' => self::TYPE_STRING, 'validate' => 'isUrl', 'required' => true],
|
||||
'getvar' => ['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 = [];
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
131
classes/Smarty/SmartyCacheResourceMysql.php
Normal file
131
classes/Smarty/SmartyCacheResourceMysql.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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 = [];
|
||||
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();
|
||||
}
|
||||
}
|
||||
301
classes/Smarty/SmartyCustom.php
Normal file
301
classes/Smarty/SmartyCustom.php
Normal file
@@ -0,0 +1,301 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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 = [];
|
||||
$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();
|
||||
}
|
||||
}
|
||||
47
classes/Smarty/SmartyCustomTemplate.php
Normal file
47
classes/Smarty/SmartyCustomTemplate.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
43
classes/Smarty/SmartyDev.php
Normal file
43
classes/Smarty/SmartyDev.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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";
|
||||
}
|
||||
}
|
||||
43
classes/Smarty/SmartyDevTemplate.php
Normal file
43
classes/Smarty/SmartyDevTemplate.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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";
|
||||
}
|
||||
}
|
||||
97
classes/Smarty/SmartyLazyRegister.php
Normal file
97
classes/Smarty/SmartyLazyRegister.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
/**
|
||||
* Used to delay loading of external classes with smarty->register_plugin.
|
||||
*/
|
||||
class SmartyLazyRegister
|
||||
{
|
||||
protected $registry = [];
|
||||
protected static $instances = [];
|
||||
|
||||
/**
|
||||
* 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], [$arguments[0], &$arguments[1]]);
|
||||
} else {
|
||||
$args = [];
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
66
classes/Smarty/SmartyResourceModule.php
Normal file
66
classes/Smarty/SmartyResourceModule.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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('', [
|
||||
'<!-- begin ' . $file . ' -->',
|
||||
file_get_contents($file),
|
||||
'<!-- end ' . $file . ' -->',
|
||||
]);
|
||||
} else {
|
||||
$source = file_get_contents($file);
|
||||
}
|
||||
$mtime = filemtime($file);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
65
classes/Smarty/SmartyResourceParent.php
Normal file
65
classes/Smarty/SmartyResourceParent.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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('', [
|
||||
'<!-- begin ' . $file . ' -->',
|
||||
file_get_contents($file),
|
||||
'<!-- end ' . $file . ' -->',
|
||||
]);
|
||||
} else {
|
||||
$source = file_get_contents($file);
|
||||
}
|
||||
$mtime = filemtime($file);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
208
classes/Smarty/TemplateFinder.php
Normal file
208
classes/Smarty/TemplateFinder.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* 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.md.
|
||||
* 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://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determine the best existing template.
|
||||
*
|
||||
* @since 1.7.0.0
|
||||
*/
|
||||
class TemplateFinderCore
|
||||
{
|
||||
private $directories;
|
||||
private $extension;
|
||||
private $productListEntities = ['category', 'manufacturer', 'supplier'];
|
||||
private $productListSearchEntities = ['search', 'price-drop', 'best-sale', 'prices-drop', 'best-sales', 'new-products'];
|
||||
private $productEntities = ['product'];
|
||||
private $brandListEntities = ['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 = [
|
||||
'catalog/listing/' . $entity . '-' . $id,
|
||||
'catalog/listing/' . $entity,
|
||||
$template,
|
||||
'catalog/listing/product-list',
|
||||
];
|
||||
} elseif (in_array($entity, $this->getProductListSearchEntities())) {
|
||||
$templates = [
|
||||
'catalog/listing/' . $entity,
|
||||
$template,
|
||||
'catalog/listing/product-list',
|
||||
];
|
||||
} elseif (in_array($entity, $this->getProductEntities())) {
|
||||
$templates = [
|
||||
'catalog/' . $entity . '-' . $id,
|
||||
$template,
|
||||
'catalog/product',
|
||||
];
|
||||
} elseif (in_array($entity, $this->getBrandListEntities())) {
|
||||
$templates = [
|
||||
$template,
|
||||
'catalog/brands',
|
||||
];
|
||||
} elseif ('cms' === $entity) {
|
||||
$templates = [
|
||||
'cms/page-' . $id,
|
||||
$template,
|
||||
'cms/page',
|
||||
];
|
||||
} else {
|
||||
$templates = [$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
Reference in New Issue
Block a user