1140 lines
36 KiB
PHP
1140 lines
36 KiB
PHP
<?php
|
|
/**
|
|
* NOTICE OF LICENSE
|
|
*
|
|
* This file is licenced under the Software License Agreement.
|
|
* With the purchase or the installation of the software in your application
|
|
* you accept the licence agreement.
|
|
*
|
|
* You must not modify, adapt or create derivative works of this source code
|
|
*
|
|
* @author Doofinder
|
|
* @copyright Doofinder
|
|
* @license GPLv3
|
|
*/
|
|
|
|
define('CATEGORY_SEPARATOR', '%%');
|
|
define('CATEGORY_TREE_SEPARATOR', '>');
|
|
define('TXT_SEPARATOR', '|');
|
|
|
|
class DfTools
|
|
{
|
|
|
|
// http://stackoverflow.com/questions/4224141/php-removing-invalid-utf-8-characters-in-xml-using-filter
|
|
const VALID_UTF8 = '/([\x09\x0A\x0D\x20-\x7E]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF]
|
|
[\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F]
|
|
[\x80-\xBF]{2})|./x';
|
|
|
|
//
|
|
// Validation
|
|
//
|
|
|
|
public static function isBasicValue($v)
|
|
{
|
|
return $v && !empty($v) && Validate::isGenericName($v);
|
|
}
|
|
|
|
//
|
|
// SQL Tools
|
|
//
|
|
|
|
public static function prepareSQL($sql, $args = array())
|
|
{
|
|
$keys = array('_DB_PREFIX_');
|
|
$values = array(_DB_PREFIX_);
|
|
|
|
foreach ($args as $k => $v) {
|
|
$keys[] = $k;
|
|
$values[] = $v;
|
|
}
|
|
|
|
return str_replace($keys, $values, $sql);
|
|
}
|
|
|
|
public static function limitSQL($sql, $limit = false, $offset = false)
|
|
{
|
|
if (false !== $limit && is_numeric($limit)) {
|
|
$sql .= " LIMIT " . intval($limit);
|
|
|
|
if (false !== $offset && is_numeric($offset)) {
|
|
$sql .= " OFFSET " . intval($offset);
|
|
}
|
|
}
|
|
|
|
return $sql;
|
|
}
|
|
|
|
//
|
|
// SQL Queries
|
|
|
|
//
|
|
|
|
/**
|
|
* Returns an array of image size names to be used in a <select> box.
|
|
*
|
|
* @return array (assoc) with the value of each key as value for it.
|
|
*/
|
|
public static function getAvailableImageSizes()
|
|
{
|
|
$sizes = array();
|
|
$sql = "
|
|
SELECT
|
|
`name` AS DF_GS_IMAGE_SIZE,
|
|
`name`
|
|
FROM
|
|
`_DB_PREFIX_image_type`
|
|
WHERE
|
|
`products` = 1
|
|
ORDER BY
|
|
CASE
|
|
WHEN name = 'home_default' THEN '1'
|
|
END DESC;
|
|
";
|
|
|
|
foreach (Db::getInstance()->ExecuteS(self::prepareSQL($sql)) as $size) {
|
|
$sizes[$size['DF_GS_IMAGE_SIZE']] = $size;
|
|
}
|
|
|
|
return $sizes;
|
|
}
|
|
|
|
/**
|
|
* Returns an assoc. array. Keys are currency ISO codes. Values are currency
|
|
* names.
|
|
* @return array.
|
|
*/
|
|
public static function getAvailableCurrencies()
|
|
{
|
|
$currencies = array();
|
|
$sql = "
|
|
SELECT
|
|
`iso_code`,
|
|
`name`
|
|
FROM
|
|
`_DB_PREFIX_currency`
|
|
WHERE
|
|
`active` = 1
|
|
ORDER BY `name`;
|
|
";
|
|
|
|
foreach (Db::getInstance()->ExecuteS(self::prepareSQL($sql)) as $currency) {
|
|
$currencies[$currency['iso_code']] = $currency;
|
|
}
|
|
return $currencies;
|
|
}
|
|
|
|
/**
|
|
* 1.[5].0.13 | 1.5.[0].5 | 1.5.0.[1]
|
|
* 1.[6].0.6 | 1.5.[1].0 | 1.5.0.[5]
|
|
*
|
|
* @param [type] $min_version [description]
|
|
* @return [type] [description]
|
|
*/
|
|
public static function versionGte($min_version)
|
|
{
|
|
$version = explode('.', _PS_VERSION_);
|
|
$min_version = explode('.', $min_version);
|
|
|
|
foreach ($version as $index => $value) {
|
|
if (intval($value) > intval($min_version[$index])) {
|
|
return true;
|
|
} elseif (intval($value) < intval($min_version[$index])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static function getSelectedFeatures($features, $selected_keys)
|
|
{
|
|
/**
|
|
* Returns the features selected by user
|
|
* @param array features
|
|
* @return array of rows (assoc arrays).
|
|
*/
|
|
$selected_features = array();
|
|
|
|
foreach ($features as $key => $value) {
|
|
if (in_array((string) $key, $selected_keys)) {
|
|
$selected_features[] = $value;
|
|
}
|
|
}
|
|
|
|
return $selected_features;
|
|
}
|
|
|
|
/**
|
|
* Returns the features of a product
|
|
* @param int Shop ID.
|
|
* @param int Language ID.
|
|
* @return array of rows (assoc arrays).
|
|
*/
|
|
public static function getFeatureKeysForShopAndLang($id_shop, $id_lang)
|
|
{
|
|
$sql = "
|
|
SELECT fl.name, fl.id_feature
|
|
|
|
FROM
|
|
_DB_PREFIX_feature_shop fs
|
|
LEFT JOIN _DB_PREFIX_feature_lang fl
|
|
ON (fl.id_feature = fs.id_feature AND fl.id_lang = _ID_LANG_)
|
|
|
|
WHERE
|
|
fs.id_shop = _ID_SHOP_
|
|
";
|
|
|
|
$sql = self::prepareSQL($sql, array(
|
|
'_ID_LANG_' => (int)pSQL($id_lang),
|
|
'_ID_SHOP_' => (int)pSQL($id_shop)
|
|
));
|
|
|
|
|
|
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
|
|
$names = array();
|
|
foreach ($result as $elem) {
|
|
$names[$elem["id_feature"]] = $elem["name"];
|
|
}
|
|
return $names;
|
|
}
|
|
|
|
/**
|
|
* Returns the features of a product
|
|
* @param int Shop ID.
|
|
* @param int Language ID.
|
|
* @return array of rows (assoc arrays).
|
|
*/
|
|
public static function getAttributeKeysForShopAndLang($id_shop, $id_lang)
|
|
{
|
|
$sql = "
|
|
SELECT agl.name
|
|
|
|
FROM
|
|
_DB_PREFIX_attribute_group_shop ags
|
|
LEFT JOIN _DB_PREFIX_attribute_group_lang agl
|
|
ON (agl.id_attribute_group = ags.id_attribute_group AND agl.id_lang = _ID_LANG_)
|
|
|
|
WHERE
|
|
ags.id_shop = _ID_SHOP_
|
|
";
|
|
|
|
$sql = self::prepareSQL($sql, array(
|
|
'_ID_LANG_' => (int)pSQL($id_lang),
|
|
'_ID_SHOP_' => (int)pSQL($id_shop)
|
|
));
|
|
|
|
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
|
|
$names = array();
|
|
foreach ($result as $elem) {
|
|
$names[] = $elem["name"];
|
|
}
|
|
return $names;
|
|
}
|
|
|
|
/**
|
|
* Returns the features of a product
|
|
* @param int Shop ID.
|
|
* @param int Language ID.
|
|
* @return array of rows (assoc arrays).
|
|
*/
|
|
public static function getVariationImg($id_product, $id_product_attribute)
|
|
{
|
|
$sql = "
|
|
SELECT i.id_image
|
|
from
|
|
(
|
|
select pa.id_product, pa.id_product_attribute,paic.id_attribute,min(i.position) as min_position
|
|
from _DB_PREFIX_product_attribute pa
|
|
inner join _DB_PREFIX_product_attribute_image pai
|
|
on pai.id_product_attribute = pa.id_product_attribute
|
|
inner join _DB_PREFIX_product_attribute_combination paic
|
|
on pai.id_product_attribute = paic.id_product_attribute
|
|
inner join _DB_PREFIX_image i
|
|
on pai.id_image = i.id_image
|
|
where pa.id_product = " . (int)pSQL($id_product) . "
|
|
and pa.id_product_attribute = " . (int)pSQL($id_product_attribute) . "
|
|
group by pa.id_product, pa.id_product_attribute,paic.id_attribute
|
|
) as P
|
|
inner join _DB_PREFIX_image i
|
|
on i.id_product = P.id_product and i.position = P.min_position
|
|
";
|
|
$sql = self::prepareSQL($sql, array());
|
|
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
|
|
if (isset($result[0])) {
|
|
return $result[0]['id_image'];
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the features of a product
|
|
* @param int Product ID.
|
|
* @param int Language ID.
|
|
* @return array of rows (assoc arrays).
|
|
*/
|
|
public static function getFeaturesForProduct($id_product, $id_lang, $feature_keys)
|
|
{
|
|
$sql = "
|
|
SELECT fl.name,
|
|
fvl.value
|
|
|
|
FROM
|
|
_DB_PREFIX_feature_product fp
|
|
LEFT JOIN _DB_PREFIX_feature_lang fl
|
|
ON (fl.id_feature = fp.id_feature AND fl.id_lang = _ID_LANG_)
|
|
LEFT JOIN _DB_PREFIX_feature_value_lang fvl
|
|
ON (fvl.id_feature_value = fp.id_feature_value AND fvl.id_lang = _ID_LANG_)
|
|
|
|
WHERE
|
|
fp.id_product = _ID_PRODUCT
|
|
";
|
|
|
|
$sql = self::prepareSQL($sql, array(
|
|
'_ID_LANG_' => (int)pSQL($id_lang),
|
|
'_ID_PRODUCT' => (int)pSQL($id_product)
|
|
));
|
|
|
|
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
|
|
|
|
$features = array();
|
|
|
|
|
|
foreach ($result as $elem) {
|
|
if (in_array($elem['name'], $feature_keys)) {
|
|
$features[$elem['name']][] = $elem['value'];
|
|
}
|
|
}
|
|
|
|
return $features;
|
|
}
|
|
|
|
/**
|
|
* Returns the product variation attributes
|
|
* @param int Product Attribute ID.
|
|
* @param int Language ID.
|
|
* @return array of rows (assoc arrays).
|
|
*/
|
|
public static function getAttributesForProductVariation($variation_id, $id_lang, $attribute_keys)
|
|
{
|
|
if (isset($variation_id) && $variation_id > 0) {
|
|
$sql = "
|
|
SELECT pc.id_product_attribute,
|
|
pal.name,
|
|
pagl.name AS group_name
|
|
|
|
FROM
|
|
_DB_PREFIX_product_attribute_combination pc
|
|
LEFT JOIN _DB_PREFIX_attribute pa
|
|
ON pc.id_attribute = pa.id_attribute
|
|
LEFT JOIN _DB_PREFIX_attribute_lang pal
|
|
ON (pc.id_attribute = pal.id_attribute AND pal.id_lang = _ID_LANG_)
|
|
LEFT JOIN _DB_PREFIX_attribute_group_lang pagl
|
|
ON (pagl.id_attribute_group = pa.id_attribute_group AND pagl.id_lang = _ID_LANG_)
|
|
WHERE
|
|
pc.id_product_attribute = _VARIATION_ID
|
|
";
|
|
|
|
$sql = self::prepareSQL($sql, array(
|
|
'_ID_LANG_' => (int)pSQL($id_lang),
|
|
'_VARIATION_ID' => (int)pSQL($variation_id)
|
|
));
|
|
|
|
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
|
|
} else {
|
|
$result = array();
|
|
}
|
|
|
|
if (count($attribute_keys) > 0) {
|
|
$attributes = array_fill(0, count($attribute_keys), "");
|
|
} else {
|
|
$attributes = array();
|
|
}
|
|
|
|
foreach ($result as $elem) {
|
|
if (array_search($elem['group_name'], $attribute_keys) !== false) {
|
|
$attributes[array_search($elem['group_name'], $attribute_keys)] = $elem['name'];
|
|
}
|
|
}
|
|
return $attributes;
|
|
}
|
|
|
|
/**
|
|
* Returns the product combination attributes
|
|
* @param int Product Attribute ID.
|
|
* @param bool Attribute groups IDs.
|
|
* @param int Language ID.
|
|
* @return array of rows (assoc arrays).
|
|
*/
|
|
public static function getAttributesByCombination($variation_id, $id_lang, $attr_limit = false)
|
|
{
|
|
if (isset($variation_id) && $variation_id > 0) {
|
|
$sql = "SELECT pc.id_product_attribute,
|
|
pal.name,
|
|
pagl.name AS group_name
|
|
|
|
FROM
|
|
_DB_PREFIX_product_attribute_combination pc
|
|
LEFT JOIN _DB_PREFIX_attribute pa
|
|
ON pc.id_attribute = pa.id_attribute
|
|
LEFT JOIN _DB_PREFIX_attribute_lang pal
|
|
ON (pc.id_attribute = pal.id_attribute AND pal.id_lang = _ID_LANG_)
|
|
LEFT JOIN _DB_PREFIX_attribute_group_lang pagl
|
|
ON (pagl.id_attribute_group = pa.id_attribute_group AND pagl.id_lang = _ID_LANG_)
|
|
WHERE
|
|
pc.id_product_attribute = _VARIATION_ID";
|
|
|
|
if ($attr_limit) {
|
|
$sql .= " AND pa.id_attribute_group IN (" . pSQL($attr_limit) . ")";
|
|
}
|
|
$sql = self::prepareSQL($sql, array(
|
|
'_ID_LANG_' => (int)pSQL($id_lang),
|
|
'_VARIATION_ID' => (int)pSQL($variation_id)
|
|
));
|
|
|
|
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
|
|
} else {
|
|
return array();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the products available for a language
|
|
* @param int Language ID.
|
|
* @param int Optional. Default false. Number of products to get.
|
|
* @param int Optional. Default false. Offset to start the select from.
|
|
* @param string Optional. Fields to select.
|
|
* @param array Optional. Filter product ids.
|
|
* @return array of rows (assoc arrays).
|
|
*/
|
|
public static function getAvailableProductsForLanguage(
|
|
$id_lang,
|
|
$id_shop,
|
|
$limit = false,
|
|
$offset = false,
|
|
$ids = null
|
|
) {
|
|
$Shop = new Shop($id_shop);
|
|
|
|
$isbn = '';
|
|
if (dfTools::versionGte('1.7.0.0')) {
|
|
$isbn = 'p.isbn,';
|
|
if (dfTools::cfg($id_shop, 'DF_SHOW_PRODUCT_VARIATIONS') == 1) {
|
|
$isbn = 'IF(isnull(pa.id_product), p.isbn , pa.isbn) AS isbn,';
|
|
}
|
|
}
|
|
|
|
$sql = "
|
|
SELECT
|
|
ps.id_product,
|
|
ps.show_price,
|
|
__ID_CATEGORY_DEFAULT__,
|
|
m.name AS manufacturer,
|
|
p.__MPN__ AS mpn,
|
|
p.ean13 AS ean13,
|
|
" . $isbn . "
|
|
p.upc,
|
|
p.reference,
|
|
p.supplier_reference,
|
|
pl.name,
|
|
pl.description,
|
|
pl.description_short,
|
|
pl.meta_title,
|
|
pl.meta_keywords,
|
|
pl.meta_description,
|
|
GROUP_CONCAT(tag.name SEPARATOR ',') AS tags,
|
|
pl.link_rewrite,
|
|
cl.link_rewrite AS cat_link_rew,
|
|
im.id_image,
|
|
ps.available_for_order,
|
|
sa.out_of_stock,
|
|
sa.quantity as stock_quantity
|
|
FROM
|
|
_DB_PREFIX_product p
|
|
INNER JOIN _DB_PREFIX_product_shop ps
|
|
ON (p.id_product = ps.id_product AND ps.id_shop = _ID_SHOP_)
|
|
LEFT JOIN _DB_PREFIX_product_lang pl
|
|
ON (p.id_product = pl.id_product AND pl.id_shop = _ID_SHOP_ AND pl.id_lang = _ID_LANG_)
|
|
LEFT JOIN _DB_PREFIX_manufacturer m
|
|
ON (p.id_manufacturer = m.id_manufacturer)
|
|
LEFT JOIN _DB_PREFIX_category_lang cl
|
|
ON (p.id_category_default = cl.id_category AND cl.id_shop = _ID_SHOP_ AND cl.id_lang = _ID_LANG_)
|
|
LEFT JOIN (_DB_PREFIX_image im INNER JOIN _DB_PREFIX_image_shop ims ON im.id_image = ims.id_image)
|
|
ON (p.id_product = im.id_product AND ims.id_shop = _ID_SHOP_ AND _IMS_COVER_)
|
|
LEFT JOIN (_DB_PREFIX_tag tag
|
|
INNER JOIN _DB_PREFIX_product_tag pt ON tag.id_tag = pt.id_tag AND tag.id_lang = _ID_LANG_)
|
|
ON (pt.id_product = p.id_product)
|
|
LEFT JOIN _DB_PREFIX_stock_available sa
|
|
ON (p.id_product = sa.id_product AND sa.id_product_attribute = 0
|
|
AND (sa.id_shop = _ID_SHOP_ OR
|
|
(sa.id_shop = 0 AND sa.id_shop_group = _ID_SHOPGROUP_)))
|
|
WHERE
|
|
__IS_ACTIVE__
|
|
__VISIBILITY__
|
|
__PRODUCT_IDS__
|
|
GROUP BY
|
|
p.id_product
|
|
ORDER BY
|
|
p.id_product
|
|
";
|
|
|
|
$sql_variations = "
|
|
SELECT
|
|
ps.id_product,
|
|
ps.show_price,
|
|
pa.id_product_attribute,
|
|
pa.reference AS variation_reference,
|
|
pa.supplier_reference AS variation_supplier_reference,
|
|
pa.__MPN__ AS variation_mpn,
|
|
pa.ean13 AS variation_ean13,
|
|
pa.upc AS variation_upc,
|
|
pa_im.id_image AS variation_image_id,
|
|
__ID_CATEGORY_DEFAULT__,
|
|
m.name AS manufacturer,
|
|
p.__MPN__ AS mpn,
|
|
p.ean13 AS ean13,
|
|
" . $isbn . "
|
|
p.upc AS upc,
|
|
p.reference AS reference,
|
|
p.supplier_reference AS supplier_reference,
|
|
IF(isnull(pa.id_product), null , pas.default_on) AS df_group_leader,
|
|
pl.name,
|
|
pl.description,
|
|
pl.description_short,
|
|
pl.meta_title,
|
|
pl.meta_keywords,
|
|
pl.meta_description,
|
|
GROUP_CONCAT(tag.name SEPARATOR ',') AS tags,
|
|
pl.link_rewrite,
|
|
cl.link_rewrite AS cat_link_rew,
|
|
im.id_image,
|
|
ps.available_for_order,
|
|
sa.out_of_stock,
|
|
sa.quantity as stock_quantity
|
|
FROM
|
|
_DB_PREFIX_product p
|
|
INNER JOIN _DB_PREFIX_product_shop ps
|
|
ON (p.id_product = ps.id_product AND ps.id_shop = _ID_SHOP_)
|
|
LEFT JOIN _DB_PREFIX_product_lang pl
|
|
ON (p.id_product = pl.id_product AND pl.id_shop = _ID_SHOP_ AND pl.id_lang = _ID_LANG_)
|
|
LEFT JOIN _DB_PREFIX_manufacturer m
|
|
ON (p.id_manufacturer = m.id_manufacturer)
|
|
LEFT JOIN _DB_PREFIX_category_lang cl
|
|
ON (p.id_category_default = cl.id_category AND cl.id_shop = _ID_SHOP_ AND cl.id_lang = _ID_LANG_)
|
|
LEFT JOIN (_DB_PREFIX_image im INNER JOIN _DB_PREFIX_image_shop ims ON im.id_image = ims.id_image)
|
|
ON (p.id_product = im.id_product AND ims.id_shop = _ID_SHOP_ AND _IMS_COVER_)
|
|
LEFT OUTER JOIN _DB_PREFIX_product_attribute pa
|
|
ON (p.id_product = pa.id_product)
|
|
LEFT JOIN _DB_PREFIX_product_attribute_shop pas
|
|
ON (pas.id_product_attribute = pa.id_product_attribute AND pas.id_shop = _ID_SHOP_)
|
|
LEFT JOIN _DB_PREFIX_product_attribute_image pa_im
|
|
ON (pa_im.id_product_attribute = pa.id_product_attribute)
|
|
LEFT JOIN (_DB_PREFIX_tag tag
|
|
INNER JOIN _DB_PREFIX_product_tag pt ON tag.id_tag = pt.id_tag AND tag.id_lang = _ID_LANG_)
|
|
ON (pt.id_product = p.id_product)
|
|
LEFT JOIN _DB_PREFIX_stock_available sa
|
|
ON (p.id_product = sa.id_product
|
|
AND sa.id_product_attribute = IF(isnull(pa.id_product), 0, pa.id_product_attribute)
|
|
AND (sa.id_shop = _ID_SHOP_ OR
|
|
(sa.id_shop = 0 AND sa.id_shop_group = _ID_SHOPGROUP_)))
|
|
WHERE
|
|
__IS_ACTIVE__
|
|
__VISIBILITY__
|
|
__PRODUCT_IDS__
|
|
GROUP BY pa.id_product_attribute, p.id_product
|
|
ORDER BY
|
|
p.id_product
|
|
";
|
|
|
|
if (dfTools::cfg($id_shop, 'DF_SHOW_PRODUCT_VARIATIONS') == 1) {
|
|
$sql = $sql_variations;
|
|
}
|
|
|
|
$mpn_field = dfTools::cfg($id_shop, 'DF_GS_MPN_FIELD', 'reference');
|
|
|
|
// MIN: 1.5.0.9
|
|
$id_category_default = self::versionGte('1.5.0.9') ? 'ps.id_category_default' : 'p.id_category_default';
|
|
// MIN: 1.5.1.0
|
|
$ims_cover = self::versionGte('1.5.1.0') ? 'ims.cover = 1' : 'im.cover = 1';
|
|
$is_active = self::versionGte('1.5.1.0') ? 'ps.active = 1' : 'p.active = 1';
|
|
|
|
if (self::versionGte('1.5.1.0')) {
|
|
$visibility = "AND ps.visibility IN ('search', 'both')";
|
|
} elseif (self::versionGte('1.5.0.9')) {
|
|
$visibility = "AND p.visibility IN ('search', 'both')";
|
|
} else {
|
|
$visibility = "";
|
|
}
|
|
|
|
if (is_array($ids) && count($ids)) {
|
|
$product_ids = "AND p.id_product IN (" . implode(",", $ids) . ")";
|
|
} else {
|
|
$product_ids = "";
|
|
}
|
|
|
|
$sql = self::limitSQL($sql, $limit, $offset);
|
|
$sql = self::prepareSQL($sql, array(
|
|
'_ID_LANG_' => (int)pSQL($id_lang),
|
|
'_ID_SHOP_' => (int)pSQL($id_shop),
|
|
'_ID_SHOPGROUP_' => (int)pSQL($Shop->id_shop_group),
|
|
'__MPN__' => (string)pSQL($mpn_field),
|
|
'_IMS_COVER_' => (string)pSQL($ims_cover),
|
|
'__ID_CATEGORY_DEFAULT__' => (int)pSQL($id_category_default),
|
|
'__IS_ACTIVE__' => (string)pSQL($is_active),
|
|
'__VISIBILITY__' => (string)pSQL($visibility),
|
|
'__PRODUCT_IDS__' => (string)pSQL($product_ids)
|
|
));
|
|
|
|
$sql = str_replace("\'", "'", $sql);
|
|
|
|
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
|
|
}
|
|
|
|
protected static $root_category_ids = null;
|
|
protected static $cached_category_paths = array();
|
|
|
|
/**
|
|
* Returns an array of "root" categories in Prestashop for a language.
|
|
* The results are cached in a protected, static variable.
|
|
* @return array.
|
|
*/
|
|
public static function getRootCategoryIds($id_lang)
|
|
{
|
|
if (null === self::$root_category_ids) {
|
|
self::$root_category_ids = array();
|
|
foreach (Category::getRootCategories($id_lang) as $category) {
|
|
self::$root_category_ids[] = $category['id_category'];
|
|
}
|
|
}
|
|
|
|
return self::$root_category_ids;
|
|
}
|
|
|
|
/**
|
|
* Returns the path to the first, no root ancestor category for the selected
|
|
* category ID in a language for the selected shop.
|
|
* Results are cached by category ID.
|
|
*
|
|
* @param int Category ID
|
|
* @param int Language ID
|
|
* @param int Shop ID
|
|
* @param bool Return full category path.
|
|
* @return string
|
|
*/
|
|
public static function getCategoryPath($id_category, $id_lang, $id_shop, $full = true)
|
|
{
|
|
if (isset(self::$cached_category_paths[$id_category])) {
|
|
return self::$cached_category_paths[$id_category];
|
|
}
|
|
|
|
$excluded_ids = self::getRootCategoryIds($id_lang);
|
|
|
|
$sql = "
|
|
SELECT
|
|
cl.name
|
|
FROM
|
|
_DB_PREFIX_category_lang cl INNER JOIN _DB_PREFIX_category parent
|
|
ON (parent.id_category = cl.id_category)
|
|
INNER JOIN _DB_PREFIX_category_shop cs ON (cs.id_category = parent.id_category),
|
|
_DB_PREFIX_category node
|
|
WHERE
|
|
node.nleft BETWEEN parent.nleft AND parent.nright
|
|
AND node.id_category = _ID_CATEGORY_
|
|
AND cl.id_shop = _ID_SHOP_
|
|
AND cl.id_lang = _ID_LANG_
|
|
AND cs.id_shop = _ID_SHOP_
|
|
AND parent.level_depth <> 0
|
|
AND parent.active = 1 ";
|
|
|
|
if (count($excluded_ids) > 0 && $excluded_ids[0] != "") {
|
|
$sql .= "AND parent.id_category NOT IN (_EXCLUDED_IDS_) ";
|
|
}
|
|
|
|
$sql .= "ORDER BY
|
|
parent.nleft
|
|
;";
|
|
|
|
$sql = self::prepareSQL($sql, array(
|
|
'_ID_CATEGORY_' => (int)pSQL($id_category),
|
|
'_ID_SHOP_' => (int)pSQL($id_shop),
|
|
'_ID_LANG_' => (int)pSQL($id_lang),
|
|
'_EXCLUDED_IDS_' => (string)pSQL(implode(',', $excluded_ids))
|
|
));
|
|
|
|
$sql = str_replace("\'", "'", $sql);
|
|
|
|
$path = array();
|
|
foreach (Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql) as $row) {
|
|
$path[] = str_replace(array(CATEGORY_TREE_SEPARATOR, CATEGORY_SEPARATOR), "-", $row['name']);
|
|
}
|
|
|
|
if ($full) {
|
|
$path = implode(CATEGORY_TREE_SEPARATOR, $path);
|
|
} else {
|
|
$path = end($path);
|
|
}
|
|
|
|
$path = self::cleanString($path);
|
|
self::$cached_category_paths[$id_category] = $path;
|
|
|
|
return $path;
|
|
}
|
|
|
|
/**
|
|
* Returns a string with all the paths for categories for a product in a language
|
|
* for the selected shop. If $flat == false then returns them as an array.
|
|
* @param int Product ID
|
|
* @param int Language ID
|
|
* @param int Shop ID
|
|
* @param bool Optional implode values.
|
|
* @return string or array
|
|
*/
|
|
public static function getCategoriesForProductIdAndLanguage($id_product, $id_lang, $id_shop, $flat = true)
|
|
{
|
|
|
|
$use_main_category = (bool) dfTools::cfg($id_shop, 'DF_FEED_MAINCATEGORY_PATH', Doofinder::YES);
|
|
|
|
$sql = "
|
|
SELECT DISTINCT
|
|
c.id_category,
|
|
c.id_parent,
|
|
c.level_depth,
|
|
c.nleft,
|
|
c.nright
|
|
FROM
|
|
_DB_PREFIX_category c
|
|
INNER JOIN _DB_PREFIX_category_product cp
|
|
ON (c.id_category = cp.id_category AND cp.id_product = _ID_PRODUCT_)
|
|
INNER JOIN _DB_PREFIX_category_shop cs
|
|
ON (c.id_category = cs.id_category AND cs.id_shop = _ID_SHOP_)
|
|
_MAIN_CATEGORY_INNER_
|
|
WHERE
|
|
c.active = 1
|
|
_MAIN_CATEGORY_WHERE_
|
|
ORDER BY
|
|
c.nleft DESC,
|
|
c.nright ASC;
|
|
";
|
|
$mainCategoryInner = '';
|
|
$mainCategoryWhere = '';
|
|
|
|
if ($use_main_category) {
|
|
$mainInnerSql = 'INNER JOIN _DB_PREFIX_product_shop ps '
|
|
. 'ON (ps.id_product = _ID_PRODUCT_ AND ps.id_shop = _ID_SHOP_)';
|
|
$mainCategoryInner = self::prepareSQL(
|
|
$mainInnerSql,
|
|
array('_ID_PRODUCT_' => (int)pSQL($id_product), '_ID_SHOP_' => (int)pSQL($id_shop))
|
|
);
|
|
$mainCategoryWhere = 'AND ps.id_category_default = cp.id_category';
|
|
}
|
|
|
|
$sql = self::prepareSQL($sql, array(
|
|
'_ID_PRODUCT_' => (int)pSQL($id_product),
|
|
'_MAIN_CATEGORY_INNER_' => (string)pSQL($mainCategoryInner),
|
|
'_MAIN_CATEGORY_WHERE_' => (string)pSQL($mainCategoryWhere),
|
|
'_ID_SHOP_' => (int)pSQL($id_shop)
|
|
));
|
|
|
|
$sql = str_replace("\'", "'", $sql);
|
|
|
|
$categories = array();
|
|
$last_saved = 0;
|
|
$id_category0 = 0;
|
|
$nleft0 = 0;
|
|
$nright0 = 0;
|
|
$use_full_path = (bool) dfTools::cfg($id_shop, 'DF_FEED_FULL_PATH', Doofinder::YES);
|
|
|
|
foreach (Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql) as $i => $row) {
|
|
if (!$i) {
|
|
$id_category0 = intval($row['id_category']);
|
|
$nleft0 = intval($row['nleft']);
|
|
$nright0 = intval($row['nright']);
|
|
} else {
|
|
$id_category1 = intval($row['id_category']);
|
|
$nleft1 = intval($row['nleft']);
|
|
$nright1 = intval($row['nright']);
|
|
|
|
if ($nleft1 < $nleft0 && $nright1 > $nright0) {
|
|
// $id_category1 is an ancestor of $id_category0
|
|
} elseif ($nleft1 < $nleft0 && $nright1 > $nright0) {
|
|
// $id_category1 is a child of $id_category0 so be replace $id_category0
|
|
$id_category0 = $id_category1;
|
|
$nleft0 = $nleft1;
|
|
$nright0 = $nright1;
|
|
} else {
|
|
// $id_category1 is not a relative of $id_category0 so we save
|
|
// $id_category0 now and make $id_category1 the current category.
|
|
$categories[] = self::getCategoryPath($id_category0, $id_lang, $id_shop, $use_full_path);
|
|
$last_saved = $id_category0;
|
|
|
|
$id_category0 = $id_category1;
|
|
$nleft0 = $nleft1;
|
|
$nright0 = $nright1;
|
|
}
|
|
}
|
|
} // endforeach
|
|
|
|
if ($last_saved != $id_category0) {
|
|
// The last item in loop didn't trigger the $id_category0 saving event.
|
|
$categories[] = self::getCategoryPath($id_category0, $id_lang, $id_shop, $use_full_path);
|
|
}
|
|
|
|
return $flat ? implode(CATEGORY_SEPARATOR, $categories) : $categories;
|
|
}
|
|
|
|
//
|
|
// Text Tools
|
|
//
|
|
|
|
public static function truncateText($text, $length)
|
|
{
|
|
$l = intval($length);
|
|
$c = trim(preg_replace('/\s+/', ' ', $text));
|
|
|
|
if (strlen($c) <= $l) {
|
|
return $c;
|
|
}
|
|
|
|
$n = 0;
|
|
$r = "";
|
|
foreach (explode(' ', $c) as $p) {
|
|
if (($tmp = $n + strlen($p) + 1) <= $l) {
|
|
$n = $tmp;
|
|
$r .= " $p";
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $r;
|
|
}
|
|
|
|
public static function stripHtml($text)
|
|
{
|
|
if (!function_exists('cb1')) {
|
|
|
|
function cb1($matches)
|
|
{
|
|
return chr($matches[1]);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('cb2')) {
|
|
|
|
function cb2($matches)
|
|
{
|
|
return chr('0x' . $matches[1]);
|
|
}
|
|
}
|
|
html_entity_decode($text, ENT_QUOTES, "ISO-8859-1");
|
|
$text = preg_replace_callback(
|
|
'/&#(\d+);/mu',
|
|
'cb1',
|
|
$text
|
|
); // decimal notation
|
|
$text = preg_replace_callback(
|
|
'/&#x([a-f0-9]+);/miu',
|
|
'cb2',
|
|
$text
|
|
); // hex notation
|
|
$text = str_replace("><", "> <", $text);
|
|
$text = preg_replace('/\<br(\s*)?\/?\>/i', " ", $text);
|
|
$text = strip_tags($text);
|
|
|
|
return $text;
|
|
}
|
|
|
|
public static function cleanURL($text)
|
|
{
|
|
$text = trim($text);
|
|
$text = preg_replace("/\r|\n/", "", $text);
|
|
$text = explode("?", $text);
|
|
|
|
$baseUrl = array();
|
|
foreach (explode("/", $text[0]) as $part) {
|
|
if (in_array(strtolower($part), array('http:', 'https:', ''))) {
|
|
$baseUrl[] = $part;
|
|
} else {
|
|
$baseUrl[] = rawurlencode($part);
|
|
}
|
|
}
|
|
$text[0] = implode("/", $baseUrl);
|
|
|
|
if (stristr($text[0], 'http://') === false && stristr($text[0], 'https://') === false) {
|
|
$text[0] = 'http://' . $text[0];
|
|
}
|
|
|
|
if (isset($text[1])) {
|
|
$params = array();
|
|
foreach (explode("&", $text[1]) as $param) {
|
|
$param = explode("=", $param);
|
|
foreach ($param as $idx => $part) {
|
|
$param[$idx] = urlencode($part);
|
|
}
|
|
$params[] = implode("=", $param);
|
|
}
|
|
$text[1] = implode('&', $params);
|
|
}
|
|
|
|
$text = implode('?', $text);
|
|
|
|
$text = preg_replace(self::VALID_UTF8, '$1', $text);
|
|
return urldecode($text);
|
|
}
|
|
|
|
public static function cleanString($text)
|
|
{
|
|
$text = preg_replace('/[^\P{C}]+/u', ' ', $text);
|
|
$text = str_replace(TXT_SEPARATOR, "-", $text);
|
|
$text = str_replace(array("\t", "\r", "\n"), ' ', $text);
|
|
$text = self::stripHtml($text);
|
|
$text = preg_replace('/\s+/', " ", $text);
|
|
$text = trim($text);
|
|
$text = preg_replace('/^["\']+/', '', $text); // remove first quotes
|
|
|
|
return preg_replace(self::VALID_UTF8, '$1', $text);
|
|
}
|
|
|
|
/**
|
|
* Cleans a string in an extreme way to deal with conflictive strings like
|
|
* titles that contains references that can be searched with or without
|
|
* certain characters.
|
|
*
|
|
* TODO: Make it configurable from the admin.
|
|
*/
|
|
public static function cleanReferences($text)
|
|
{
|
|
$forbidden = array('-');
|
|
return str_replace($forbidden, "", $text);
|
|
}
|
|
|
|
public static function splitReferences($text)
|
|
{
|
|
return preg_replace("/([^\d\s])([\d])/", "$1 $2", $text);
|
|
}
|
|
|
|
//
|
|
// Things from request / URL Tools
|
|
|
|
//
|
|
|
|
/**
|
|
* Returns a boolean value for the $parameter specified. If the parameter does
|
|
* not exist (is NULL) then $default is returned instead.
|
|
*
|
|
* This method supports multiple ways of saying YES or NO.
|
|
*/
|
|
public static function getBooleanFromRequest($parameter, $default = false)
|
|
{
|
|
$v = Tools::getValue($parameter, null);
|
|
|
|
if ($v === null) {
|
|
return $default;
|
|
}
|
|
|
|
switch (strtolower($v)) {
|
|
case 'false':
|
|
case 'off':
|
|
case 'no':
|
|
return false;
|
|
case 'true':
|
|
case 'on':
|
|
case 'yes':
|
|
case 'si':
|
|
return true;
|
|
default:
|
|
return (bool) $v;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a Language object based on the 'language' parameter from the
|
|
* request. If no language is found then the default one from the current
|
|
* context is used.
|
|
*
|
|
* @return Language
|
|
*/
|
|
public static function getLanguageFromRequest()
|
|
{
|
|
$context = Context::getContext();
|
|
$id_lang = Tools::getValue('language', $context->language->id);
|
|
|
|
if (!is_numeric($id_lang)) {
|
|
$id_lang = Language::getIdByIso($id_lang);
|
|
}
|
|
|
|
return new Language($id_lang);
|
|
}
|
|
|
|
/**
|
|
* Returns a Currency object with the currency configured in the plugin for
|
|
* the given ISO language $code parameter. If no currency is found the method
|
|
* returns the default one for the current context.
|
|
*
|
|
* @param string $code ISO language code.
|
|
* @return Currency
|
|
*/
|
|
public static function getCurrencyForLanguage($code)
|
|
{
|
|
$optname = 'DF_GS_CURRENCY_' . strtoupper($code);
|
|
$id_currency = Configuration::get($optname);
|
|
|
|
if ($id_currency) {
|
|
return new Currency(Currency::getIdByIsoCode($id_currency));
|
|
}
|
|
|
|
return new Currency(Context::getContext()->currency->id);
|
|
}
|
|
|
|
/**
|
|
* Returns a Currency object based on the 'currency' parameter from the
|
|
* request. If no currency is found then the function searches one in the
|
|
* plugin configuration based on the $lang parameter. If none is configured
|
|
* then the default one from the current context is used.
|
|
*
|
|
* @param Language $lang
|
|
* @return Currency
|
|
*/
|
|
public static function getCurrencyForLanguageFromRequest(Language $lang)
|
|
{
|
|
if ($id_currency = Tools::getValue('currency')) {
|
|
if (is_numeric($id_currency)) {
|
|
$id_currency = intval($id_currency);
|
|
} else {
|
|
$id_currency = Currency::getIdByIsoCode(strtoupper($id_currency));
|
|
}
|
|
} else {
|
|
$optname = 'DF_GS_CURRENCY_' . strtoupper($lang->iso_code);
|
|
$id_currency = Currency::getIdByIsoCode(Configuration::get($optname));
|
|
}
|
|
|
|
if (!$id_currency) {
|
|
$context = Context::getContext();
|
|
$id_currency = $context->currency->id;
|
|
}
|
|
|
|
return new Currency($id_currency);
|
|
}
|
|
|
|
/**
|
|
* Returns a HTTP(S) link for a file from this module.
|
|
* @param string $path file path relative to this module's root.
|
|
* @param boolean $ssl Return a secure URL.
|
|
* @return string URL
|
|
*/
|
|
public static function getModuleLink($path, $ssl = false)
|
|
{
|
|
$context = Context::getContext();
|
|
$shop = new Shop($context->shop->id);
|
|
$base = (($ssl && $context->link->ssl_enable) ? 'https://' : 'http://') . $shop->domain;
|
|
|
|
return $base . _MODULE_DIR_ . basename(dirname(__FILE__)) . "/" . $path;
|
|
}
|
|
|
|
public static function fixURL($url)
|
|
{
|
|
if (preg_match('~^https?://~', $url) === 0) {
|
|
$url = "http://$url";
|
|
}
|
|
return $url;
|
|
}
|
|
|
|
public static function getImageLink($id_product, $id_image, $link_rewrite, $image_size)
|
|
{
|
|
$context = Context::getContext();
|
|
$url = $context->link->getImageLink($link_rewrite, "$id_product-$id_image", $image_size);
|
|
return self::fixURL($url);
|
|
}
|
|
|
|
/**
|
|
* Returns a data feed link for a given language ISO code. The link declares
|
|
* the usage of the currency configured in the plugin by default.
|
|
* @param string $langIsoCode ISO language code
|
|
* @return string URL
|
|
*/
|
|
public static function getFeedURL($langIsoCode)
|
|
{
|
|
$currency = self::getCurrencyForLanguage($langIsoCode);
|
|
$feedUrl = self::getModuleLink('feed.php') . "?language=" . strtoupper($langIsoCode);
|
|
$feedUrl .= "¤cy=" . strtoupper($currency->iso_code);
|
|
return $feedUrl;
|
|
}
|
|
|
|
/**
|
|
* Wraps a Javascript piece of code if no script tag is found.
|
|
* @param string $jsCode Javascript code.
|
|
* @return string
|
|
*/
|
|
public static function fixScriptTag($jsCode)
|
|
{
|
|
$result = trim(preg_replace('/<!--(.*?)-->/', '', $jsCode));
|
|
if (strlen($result) && !preg_match('/<script([^>]*?)>/', $result)) {
|
|
$result = "<script type=\"text/javascript\">\n$result\n</script>";
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Wraps a CSS piece of code if no <style> tag is found.
|
|
* @param string $cssCode CSS code.
|
|
* @return string
|
|
*/
|
|
public static function fixStyleTag($cssCode)
|
|
{
|
|
$result = trim(preg_replace('/<!--(.*?)-->/', '', $cssCode));
|
|
if (strlen($result) && !preg_match('/<style([^>]*?)>/', $result)) {
|
|
$result = "<style type=\"text/css\">\n$result\n</style>";
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Flush buffers
|
|
* @return void
|
|
*/
|
|
public static function flush()
|
|
{
|
|
if (function_exists('flush')) {
|
|
@flush();
|
|
}
|
|
if (function_exists('ob_flush')) {
|
|
@ob_flush();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a configuration value for a $key and a $id_shop. If the value is
|
|
* not found (or it's false) then returns a $default value.
|
|
* @param integer $id_shop Shop id.
|
|
* @param string $key Configuration variable name.
|
|
* @param mixed $default Default value.
|
|
* @return mixed
|
|
*/
|
|
public static function cfg($id_shop, $key, $default = false)
|
|
{
|
|
$v = Configuration::get($key, null, null, $id_shop);
|
|
if ($v === false) {
|
|
return $default;
|
|
}
|
|
return $v;
|
|
}
|
|
|
|
public static function walkApplyHtmlEntities(&$item, $key)
|
|
{
|
|
if (is_string($item)) {
|
|
$item = htmlentities($item);
|
|
}
|
|
}
|
|
|
|
public static function jsonEncode($data)
|
|
{
|
|
array_walk_recursive($data, array(get_class(), 'walkApplyHtmlEntities'));
|
|
return str_replace("\\/", "/", html_entity_decode(json_encode($data)));
|
|
}
|
|
|
|
public static function escapeSlashes($string)
|
|
{
|
|
return $string = str_replace("/", "//", $string);
|
|
}
|
|
}
|