//Merge branch release
This commit is contained in:
+36
-11
@@ -830,6 +830,13 @@ class CartCore extends ObjectModel
|
||||
$id_product_attribute = (int)$id_product_attribute;
|
||||
$product = new Product($id_product, false, Configuration::get('PS_LANG_DEFAULT'), $shop->id);
|
||||
|
||||
if ($id_product_attribute)
|
||||
{
|
||||
$combination = new Combination((int)$id_product_attribute);
|
||||
if ($combination->id_product != $id_product)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If we have a product combination, the minimal quantity is set with the one of this combination */
|
||||
if (!empty($id_product_attribute))
|
||||
$minimal_quantity = (int)Attribute::getAttributeMinimalQty($id_product_attribute);
|
||||
@@ -1447,15 +1454,7 @@ class CartCore extends ObjectModel
|
||||
// Wrapping Fees
|
||||
$wrapping_fees = 0;
|
||||
if ($this->gift)
|
||||
{
|
||||
$wrapping_fees = (float)Configuration::get('PS_GIFT_WRAPPING_PRICE');
|
||||
if ($with_taxes)
|
||||
{
|
||||
$wrapping_fees_tax = new Tax(Configuration::get('PS_GIFT_WRAPPING_TAX'));
|
||||
$wrapping_fees *= 1 + ((float)$wrapping_fees_tax->rate / 100);
|
||||
}
|
||||
$wrapping_fees = Tools::convertPrice(Tools::ps_round($wrapping_fees, 2), Currency::getCurrencyInstance((int)$this->id_currency));
|
||||
}
|
||||
$wrapping_fees = Tools::convertPrice(Tools::ps_round($this->getGiftWrappingPrice($with_taxes), 2), Currency::getCurrencyInstance((int)$this->id_currency));
|
||||
|
||||
$order_total_discount = 0;
|
||||
if (!in_array($type, array(Cart::ONLY_SHIPPING, Cart::ONLY_PRODUCTS)) && CartRule::isFeatureActive())
|
||||
@@ -1532,6 +1531,32 @@ class CartCore extends ObjectModel
|
||||
|
||||
return Tools::ps_round((float)$order_total, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the gift wrapping price
|
||||
* @param boolean $with_taxes With or without taxes
|
||||
* @return gift wrapping price
|
||||
*/
|
||||
public function getGiftWrappingPrice($with_taxes = true, $id_address = null)
|
||||
{
|
||||
static $address = null;
|
||||
|
||||
if ($id_address === null)
|
||||
$id_address = (int)$this->{Configuration::get('PS_TAX_ADDRESS_TYPE')};
|
||||
|
||||
if ($address === null)
|
||||
$address = Address::initialize($id_address);
|
||||
|
||||
$wrapping_fees = (float)Configuration::get('PS_GIFT_WRAPPING_PRICE');
|
||||
if ($with_taxes && $wrapping_fees > 0)
|
||||
{
|
||||
$tax_manager = TaxManagerFactory::getManager($address, (int)Configuration::get('PS_GIFT_WRAPPING_TAX_RULES_GROUP'));
|
||||
$tax_calculator = $tax_manager->getTaxCalculator();
|
||||
$wrapping_fees = $tax_calculator->addTaxes($wrapping_fees);
|
||||
}
|
||||
|
||||
return $wrapping_fees;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of packages
|
||||
@@ -2412,9 +2437,9 @@ class CartCore extends ObjectModel
|
||||
if (isset($delivery_option_list[$id_address][$key]['carrier_list'][$id_carrier]))
|
||||
{
|
||||
if ($useTax)
|
||||
$total_shipping += $delivery_option_list[$id_address][$key]['carrier_list'][$id_carrier]['total_price_with_tax'];
|
||||
$total_shipping += $delivery_option_list[$id_address][$key]['carrier_list'][$id_carrier]['price_with_tax'];
|
||||
else
|
||||
$total_shipping += $delivery_option_list[$id_address][$key]['carrier_list'][$id_carrier]['total_price_without_tax'];
|
||||
$total_shipping += $delivery_option_list[$id_address][$key]['carrier_list'][$id_carrier]['price_without_tax'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ class CartRuleCore extends ObjectModel
|
||||
public $reduction_product;
|
||||
public $gift_product;
|
||||
public $gift_product_attribute;
|
||||
public $highlight;
|
||||
public $active = 1;
|
||||
public $date_add;
|
||||
public $date_upd;
|
||||
@@ -103,6 +104,7 @@ class CartRuleCore extends ObjectModel
|
||||
'reduction_product' => array('type' => self::TYPE_INT, 'validate' => 'isInt'),
|
||||
'gift_product' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
|
||||
'gift_product_attribute' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
|
||||
'highlight' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
|
||||
'active' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
|
||||
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
|
||||
'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
|
||||
@@ -215,6 +217,7 @@ class CartRuleCore extends ObjectModel
|
||||
cr.`id_customer` = '.(int)$id_customer.'
|
||||
'.($includeGeneric ? 'OR cr.`id_customer` = 0' : '').'
|
||||
)
|
||||
AND highlight = 1
|
||||
'.($active ? 'AND cr.`active` = 1' : '').'
|
||||
'.($inStock ? 'AND cr.`quantity` > 0' : ''));
|
||||
|
||||
|
||||
@@ -657,7 +657,7 @@ class CategoryCore extends ObjectModel
|
||||
Tools::orderbyPrice($result, $order_way);
|
||||
|
||||
if (!$result)
|
||||
return false;
|
||||
return array();
|
||||
|
||||
/* Modify SQL result */
|
||||
return Product::getProductsProperties($id_lang, $result);
|
||||
|
||||
@@ -73,7 +73,7 @@ class CurrencyCore extends ObjectModel
|
||||
'sign' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 8),
|
||||
'format' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
|
||||
'decimals' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true),
|
||||
'conversion_rate' =>array('type' => self::TYPE_FLOAT, 'validate' => 'isFloat', 'required' => true),
|
||||
'conversion_rate' =>array('type' => self::TYPE_FLOAT, 'validate' => 'isFloat', 'required' => true, 'shop' => true),
|
||||
'deleted' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
|
||||
'active' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
|
||||
),
|
||||
|
||||
@@ -412,4 +412,33 @@ class ImageManagerCore
|
||||
@chmod($filename, 0664);
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mime type by the file extension
|
||||
*
|
||||
* @param string $file_name
|
||||
* @return string
|
||||
*/
|
||||
public static function getMimeTypeByExtension($file_name)
|
||||
{
|
||||
$types = array(
|
||||
'image/gif' => array('gif'),
|
||||
'image/jpeg' => array('jpg', 'jpeg'),
|
||||
'image/png' => array('png')
|
||||
);
|
||||
$extension = substr($file_name, strrpos($file_name, '.') + 1);
|
||||
|
||||
$mime_type = null;
|
||||
foreach ($types as $mime => $exts)
|
||||
if (in_array($extension, $exts))
|
||||
{
|
||||
$mime_type = $mime;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($mime_type === null)
|
||||
$mime_type = 'image/jpeg';
|
||||
|
||||
return $mime_type;
|
||||
}
|
||||
}
|
||||
|
||||
+31
-12
@@ -78,6 +78,8 @@ class ImageTypeCore extends ObjectModel
|
||||
* @var array Image types cache
|
||||
*/
|
||||
protected static $images_types_cache = array();
|
||||
|
||||
protected static $images_types_name_cache = array();
|
||||
|
||||
protected $webserviceParameters = array();
|
||||
|
||||
@@ -87,20 +89,17 @@ class ImageTypeCore extends ObjectModel
|
||||
* @param string|null Image type
|
||||
* @return array Image type definitions
|
||||
*/
|
||||
public static function getImagesTypes($type = null, $id_theme = false)
|
||||
public static function getImagesTypes($type = null)
|
||||
{
|
||||
if (!isset(self::$images_types_cache[$type.($id_theme ? '-'.$id_theme : '')]))
|
||||
if (!isset(self::$images_types_cache[$type]))
|
||||
{
|
||||
$where = 'WHERE 1';
|
||||
if ($id_theme)
|
||||
$where .= ' AND id_theme='.(int)$id_theme;
|
||||
if (!empty($type))
|
||||
$where .= ' AND '.pSQL($type).' = 1 ';
|
||||
|
||||
$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];
|
||||
}
|
||||
|
||||
@@ -116,9 +115,9 @@ class ImageTypeCore extends ObjectModel
|
||||
die(Tools::displayError());
|
||||
|
||||
Db::getInstance()->executeS('
|
||||
SELECT `id_image_type`
|
||||
FROM `'._DB_PREFIX_.'image_type`
|
||||
WHERE `name` = \''.pSQL($typeName).'\'');
|
||||
SELECT `id_image_type`
|
||||
FROM `'._DB_PREFIX_.'image_type`
|
||||
WHERE `name` = \''.pSQL($typeName).'\'');
|
||||
|
||||
return Db::getInstance()->NumRows();
|
||||
}
|
||||
@@ -128,9 +127,29 @@ class ImageTypeCore extends ObjectModel
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
*/
|
||||
public static function getByNameNType($name, $type)
|
||||
public static function getByNameNType($name, $type = null)
|
||||
{
|
||||
return Db::getInstance()->getRow('SELECT `id_image_type`, `name`, `width`, `height`, `products`, `categories`, `manufacturers`, `suppliers`, `scenes` FROM `'._DB_PREFIX_.'image_type` WHERE `name` = \''.pSQL($name).'\' AND `'.pSQL($type).'` = 1');
|
||||
if (!isset(self::$images_types_name_cache[$name.'_'.$type]))
|
||||
{
|
||||
self::$images_types_name_cache[$name.'_'.$type] = Db::getInstance()->getRow('
|
||||
SELECT `id_image_type`, `name`, `width`, `height`, `products`, `categories`, `manufacturers`, `suppliers`, `scenes`
|
||||
FROM `'._DB_PREFIX_.'image_type`
|
||||
WHERE `name` = \''.pSQL($name).'\' '.(!is_null($type) ? 'AND `'.pSQL($type).'` = 1' : ''));
|
||||
}
|
||||
return self::$images_types_name_cache[$name.'_'.$type];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function getFormatedName($name)
|
||||
{
|
||||
$theme_name = Context::getContext()->shop->theme_name;
|
||||
$name_without_theme_name = str_replace(array('_'.$theme_name, $theme_name.'_'), '', $name);
|
||||
//check if the theme name is already in $name if yes only return $name
|
||||
if (strstr($name, $theme_name) && self::getByNameNType($name))
|
||||
return $name;
|
||||
else if (self::getByNameNType($name_without_theme_name.'_'.$theme_name))
|
||||
return $name_without_theme_name.'_'.$theme_name;
|
||||
else
|
||||
return $theme_name.'_'.$name_without_theme_name;
|
||||
}
|
||||
|
||||
}
|
||||
+11
-11
@@ -516,11 +516,11 @@ class LanguageCore extends ObjectModel
|
||||
// delete images
|
||||
$files_copy = array(
|
||||
'/en.jpg',
|
||||
'/en-default-thickbox_default.jpg',
|
||||
'/en-default-home_default.jpg',
|
||||
'/en-default-large_default.jpg',
|
||||
'/en-default-medium_default.jpg',
|
||||
'/en-default-small_default.jpg'
|
||||
'/en-default-'.ImageType::getFormatedName('thickbox').'.jpg',
|
||||
'/en-default-'.ImageType::getFormatedName('home').'.jpg',
|
||||
'/en-default-'.ImageType::getFormatedName('large').'.jpg',
|
||||
'/en-default-'.ImageType::getFormatedName('medium').'.jpg',
|
||||
'/en-default-'.ImageType::getFormatedName('small').'.jpg'
|
||||
);
|
||||
$tos = array(_PS_CAT_IMG_DIR_, _PS_MANU_IMG_DIR_, _PS_PROD_IMG_DIR_, _PS_SUPP_IMG_DIR_);
|
||||
foreach ($tos as $to)
|
||||
@@ -726,12 +726,12 @@ class LanguageCore extends ObjectModel
|
||||
|
||||
$files_copy = array(
|
||||
'/en.jpg',
|
||||
'/en-default-thickbox_default.jpg',
|
||||
'/en-default-home_default.jpg',
|
||||
'/en-default-large_default.jpg',
|
||||
'/en-default-medium_default.jpg',
|
||||
'/en-default-small_default.jpg',
|
||||
'/en-default-scene_default.jpg'
|
||||
'/en-default-'.ImageType::getFormatedName('thickbox').'.jpg',
|
||||
'/en-default-'.ImageType::getFormatedName('home').'.jpg',
|
||||
'/en-default-'.ImageType::getFormatedName('large').'.jpg',
|
||||
'/en-default-'.ImageType::getFormatedName('medium').'.jpg',
|
||||
'/en-default-'.ImageType::getFormatedName('small').'.jpg',
|
||||
'/en-default-'.ImageType::getFormatedName('scene').'.jpg'
|
||||
);
|
||||
foreach (array(_PS_CAT_IMG_DIR_, _PS_MANU_IMG_DIR_, _PS_PROD_IMG_DIR_, _PS_SUPP_IMG_DIR_) as $to)
|
||||
foreach ($files_copy as $file)
|
||||
|
||||
+9
-3
@@ -233,12 +233,18 @@ class MailCore
|
||||
$message->headers->setEncoding('Q');
|
||||
|
||||
if (Configuration::get('PS_LOGO_MAIL') !== false && file_exists(_PS_IMG_DIR_.Configuration::get('PS_LOGO_MAIL')))
|
||||
$template_vars['{shop_logo}'] = $message->attach(new Swift_Message_Image(new Swift_File(_PS_IMG_DIR_.Configuration::get('PS_LOGO_MAIL'))));
|
||||
$logo = _PS_IMG_DIR_.Configuration::get('PS_LOGO_MAIL');
|
||||
else
|
||||
if (file_exists(_PS_IMG_DIR_.'logo.jpg'))
|
||||
$template_vars['{shop_logo}'] = $message->attach(new Swift_Message_Image(new Swift_File(_PS_IMG_DIR_.Configuration::get('PS_LOGO'))));
|
||||
{
|
||||
if (file_exists(_PS_IMG_DIR_.Configuration::get('PS_LOGO')))
|
||||
$logo = _PS_IMG_DIR_.Configuration::get('PS_LOGO');
|
||||
else
|
||||
$template_vars['{shop_logo}'] = '';
|
||||
}
|
||||
|
||||
/* don't attach the logo as */
|
||||
if (isset($logo))
|
||||
$template_vars['{shop_logo}'] = $message->attach(new Swift_Message_EmbeddedFile(new Swift_File($logo), null, ImageManager::getMimeTypeByExtension($logo)));
|
||||
|
||||
$template_vars['{shop_name}'] = Tools::safeOutput(Configuration::get('PS_SHOP_NAME'));
|
||||
$template_vars['{shop_url}'] = Tools::getShopDomain(true, true).__PS_BASE_URI__.'index.php';
|
||||
|
||||
+4
-4
@@ -346,9 +346,9 @@ class MediaCore
|
||||
$file = 'jquery.'.$name.'.js';
|
||||
$url_data = parse_url($folder);
|
||||
$file_uri = _PS_ROOT_DIR_.Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $url_data['path']);
|
||||
if (@filemtime($file_uri.$file))
|
||||
if (@file_exists($file_uri.$file))
|
||||
$plugin_path['js'] = Media::getJSPath($folder.$file);
|
||||
elseif (@filemtime($file_uri.$name.'/'.$file))
|
||||
elseif (@file_exists($file_uri.$name.'/'.$file))
|
||||
$plugin_path['js'] = Media::getJSPath($folder.$name.'/'.$file);
|
||||
else
|
||||
return false;
|
||||
@@ -369,9 +369,9 @@ class MediaCore
|
||||
$file = 'jquery.'.$name.'.css';
|
||||
$url_data = parse_url($folder);
|
||||
$file_uri = _PS_ROOT_DIR_.Tools::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $url_data['path']);
|
||||
if (@filemtime($file_uri.$file))
|
||||
if (@file_exists($file_uri.$file))
|
||||
return Media::getCSSPath($folder.$file);
|
||||
elseif (@filemtime($file_uri.$name.'/'.$file))
|
||||
elseif (@file_exists($file_uri.$name.'/'.$file))
|
||||
return Media::getCSSPath($folder.$name.'/'.$file);
|
||||
else
|
||||
return false;
|
||||
|
||||
+2
-2
@@ -3219,8 +3219,8 @@ class ProductCore extends ObjectModel
|
||||
{
|
||||
if (!array_key_exists($row['id_product'].'-'.$id_lang, self::$_frontFeaturesCache))
|
||||
self::$_frontFeaturesCache[$row['id_product'].'-'.$id_lang] = array();
|
||||
if (!isset(self::$_frontFeaturesCache[$row['id_product'].'-'.$id_lang][$row['id_product']]))
|
||||
self::$_frontFeaturesCache[$row['id_product'].'-'.$id_lang][$row['id_product']] = $row;
|
||||
if (!isset(self::$_frontFeaturesCache[$row['id_product'].'-'.$id_lang][$row['id_feature']]))
|
||||
self::$_frontFeaturesCache[$row['id_product'].'-'.$id_lang][$row['id_feature']] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+42
-2
@@ -669,8 +669,7 @@ class ToolsCore
|
||||
public static function deleteDirectory($dirname, $delete_self = true)
|
||||
{
|
||||
$dirname = rtrim($dirname, '/').'/';
|
||||
$files = scandir($dirname);
|
||||
if (is_dir($dirname))
|
||||
if ($files = scandir($dirname))
|
||||
{
|
||||
foreach ($files as $file)
|
||||
if ($file != '.' && $file != '..' && $file != '.svn')
|
||||
@@ -1683,6 +1682,47 @@ FileETag INode MTime Size
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function getDefaultIndexContent()
|
||||
{
|
||||
return '<?php
|
||||
/*
|
||||
* 2007-'.date('Y').' PrestaShop
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Open Software License (OSL 3.0)
|
||||
* that is bundled with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://opensource.org/licenses/osl-3.0.php
|
||||
* 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 http://www.prestashop.com for more information.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright 2007-'.date('Y').' PrestaShop SA
|
||||
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
|
||||
* International Registered Trademark & Property of PrestaShop SA
|
||||
*/
|
||||
|
||||
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
|
||||
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
|
||||
|
||||
header("Cache-Control: no-store, no-cache, must-revalidate");
|
||||
header("Cache-Control: post-check=0, pre-check=0", false);
|
||||
header("Pragma: no-cache");
|
||||
|
||||
header("Location: ../");
|
||||
exit;
|
||||
';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* jsonDecode convert json string to php array / object
|
||||
|
||||
@@ -187,7 +187,7 @@ abstract class ControllerCore
|
||||
else
|
||||
{
|
||||
$this->initCursedPage();
|
||||
$this->display();
|
||||
$this->smartyOutputContent($this->layout);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -867,7 +867,7 @@ class FrontControllerCore extends Controller
|
||||
$this->context->cookie->nb_item_per_page = $this->n;
|
||||
|
||||
$pages_nb = ceil($nbProducts / (int)$this->n);
|
||||
if ($this->p > $pages_nb)
|
||||
if ($this->p > $pages_nb && $nbProducts <> 0)
|
||||
Tools::redirect(self::$link->getPaginationLink(false, false, $this->n, false, $pages_nb, false));
|
||||
|
||||
$start = (int)($this->p - $range);
|
||||
|
||||
@@ -51,7 +51,12 @@ class PrestaShopExceptionCore extends Exception
|
||||
</style>';
|
||||
echo '<div id="psException">';
|
||||
echo '<h2>['.get_class($this).']</h2>';
|
||||
echo $this->getExentedMessage();
|
||||
printf(
|
||||
'<p><b>%s</b><br /><i>at line </i><b>%d</b><i> in file </i><b>%s</b></p>',
|
||||
Tools::safeOutput($this->getMessage()),
|
||||
$this->getLine(),
|
||||
ltrim(str_replace(array(_PS_ROOT_DIR_, '\\'), array('', '/'), $this->getFile()), '/')
|
||||
);
|
||||
|
||||
$this->displayFileDebug($this->getFile(), $this->getLine());
|
||||
|
||||
|
||||
@@ -1641,7 +1641,7 @@ abstract class ModuleCore
|
||||
protected function _clearCache($template, $cache_id = null, $compile_id = null)
|
||||
{
|
||||
Tools::enableCache();
|
||||
Tools::clearCache(Context::getContext()->smarty, $template, $cache_id, $compile_id);
|
||||
Tools::clearCache(Context::getContext()->smarty, $this->getTemplatePath($template), $cache_id, $compile_id);
|
||||
Tools::restoreCacheSettings();
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ class TaxRulesTaxManagerCore implements TaxManagerInterface
|
||||
WHERE `id_country` = '.(int)$this->address->id_country.'
|
||||
AND `id_tax_rules_group` = '.(int)$this->type.'
|
||||
AND `id_state` IN (0, '.(int)$this->address->id_state.')
|
||||
AND (\''.pSQL($postcode).'\' BETWEEN `zipcode_from` AND `zipcode_to` OR `zipcode_from` = 0 OR `zipcode_from` = \''.pSQL($postcode).'\')
|
||||
AND (\''.pSQL($postcode).'\' BETWEEN `zipcode_from` AND `zipcode_to` OR (`zipcode_to` = 0 AND `zipcode_from` IN(0, \''.pSQL($postcode).'\')))
|
||||
ORDER BY `zipcode_from` DESC, `zipcode_to` DESC, `id_state` DESC, `id_country` DESC');
|
||||
|
||||
$behavior = 0;
|
||||
|
||||
Reference in New Issue
Block a user