Subida del módulo y tema de PrestaShop

This commit is contained in:
Kaloyan
2026-04-09 18:31:51 +02:00
parent 12c253296f
commit 16b3ff9424
39262 changed files with 7418797 additions and 0 deletions

View File

@@ -0,0 +1,172 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class AddressControllerCore extends FrontController
{
/** @var bool */
public $auth = true;
/** @var bool */
public $guestAllowed = true;
/** @var string */
public $php_self = 'address';
/** @var string */
public $authRedirection = 'addresses';
/** @var bool */
public $ssl = true;
protected $address_form;
protected $should_redirect = false;
/**
* Initialize address controller.
*
* @see FrontController::init()
*/
public function init(): void
{
parent::init();
$this->address_form = $this->makeAddressForm();
$this->context->smarty->assign('address_form', $this->address_form->getProxy());
}
/**
* Start forms process.
*
* @see FrontController::postProcess()
*/
public function postProcess(): void
{
$this->context->smarty->assign('editing', false);
$id_address = (int) Tools::getValue('id_address');
// Initialize address if an id exists
if ($id_address) {
$this->address_form->loadAddressById($id_address);
}
// Fill the form with data
$this->address_form->fillWith(Tools::getAllValues());
// Submit the address, don't care if it's an edit or add
if (Tools::isSubmit('submitAddress')) {
if (!$this->address_form->submit()) {
$this->errors[] = $this->trans('Please fix the error below.', [], 'Shop.Notifications.Error');
} else {
if ($id_address) {
$this->success[] = $this->trans('Address successfully updated.', [], 'Shop.Notifications.Success');
} else {
$this->success[] = $this->trans('Address successfully added.', [], 'Shop.Notifications.Success');
}
$this->should_redirect = true;
}
}
// There is no id_adress, no need to continue
if (!$id_address) {
return;
}
if (Tools::getValue('delete')) {
if (
Validate::isLoadedObject($this->context->cart)
&& ($this->context->cart->id_address_invoice == $id_address
|| $this->context->cart->id_address_delivery == $id_address)
) {
$this->errors[] = $this->trans(
'Could not delete the address since it is used in the shopping cart.',
[],
'Shop.Notifications.Error'
);
return;
}
$ok = $this->makeAddressPersister()->delete(
new Address($id_address, $this->context->language->id),
Tools::getValue('token')
);
if ($ok) {
$this->success[] = $this->trans('Address successfully deleted.', [], 'Shop.Notifications.Success');
$this->should_redirect = true;
} else {
$this->errors[] = $this->trans('Could not delete address.', [], 'Shop.Notifications.Error');
}
} else {
$this->context->smarty->assign('editing', true);
}
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
if (!$this->ajax && $this->should_redirect) {
if (($back = Tools::getValue('back')) && Tools::urlBelongsToShop($back)) {
$mod = Tools::getValue('mod');
$this->redirectWithNotifications('index.php?controller=' . $back . ($mod ? '&back=' . $mod : ''));
} else {
$this->redirectWithNotifications($this->context->link->getPageLink('addresses'));
}
}
parent::initContent();
$this->setTemplate(
'customer/address',
[
'entity' => 'address',
'id' => (int) Tools::getValue('id_address'),
]
);
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = $this->addMyAccountToBreadcrumb();
$breadcrumb['links'][] = [
'title' => $this->trans('Addresses', [], 'Shop.Theme.Global'),
'url' => $this->context->link->getPageLink('addresses'),
];
$id_address = Tools::getValue('id_address');
$title = $id_address
? $this->trans('Update your address', [], 'Shop.Theme.Customeraccount')
: $this->trans('New address', [], 'Shop.Theme.Customeraccount');
$breadcrumb['links'][] = [
'title' => $title,
'url' => '#',
];
return $breadcrumb;
}
public function displayAjaxAddressForm(): void
{
$addressForm = $this->makeAddressForm();
if (Tools::getIsset('id_address') && ($id_address = (int) Tools::getValue('id_address'))) {
$addressForm->loadAddressById($id_address);
}
if (Tools::getIsset('id_country')) {
$addressForm->fillWith(['id_country' => Tools::getValue('id_country')]);
}
ob_end_clean();
header('Content-Type: application/json');
$this->ajaxRender(json_encode([
'address_form' => $this->render(
'customer/_partials/address-form',
$addressForm->getTemplateVariables()
),
]));
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class AddressesControllerCore extends FrontController
{
/** @var bool */
public $auth = true;
/** @var string */
public $php_self = 'addresses';
/** @var string */
public $authRedirection = 'addresses';
/** @var bool */
public $ssl = true;
/**
* Initialize addresses controller.
*
* @see FrontController::init()
*/
public function init(): void
{
parent::init();
if (!Validate::isLoadedObject($this->context->customer)) {
throw new PrestaShopException($this->trans('The customer could not be found.', [], 'Shop.Notifications.Error'));
}
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
parent::initContent();
$this->setTemplate('customer/addresses');
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = $this->addMyAccountToBreadcrumb();
$breadcrumb['links'][] = [
'title' => $this->trans('Addresses', [], 'Shop.Theme.Global'),
'url' => $this->context->link->getPageLink('addresses'),
];
return $breadcrumb;
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class AttachmentControllerCore extends FrontController
{
public function postProcess(): void
{
$attachment = new Attachment(Tools::getValue('id_attachment'), $this->context->language->id);
if (!$attachment->id) {
Tools::redirect('index.php');
}
Hook::exec('actionDownloadAttachment', ['attachment' => &$attachment]);
if (ob_get_level() && ob_get_length() > 0) {
ob_end_clean();
}
header('Content-Transfer-Encoding: binary');
header('Content-Type: ' . $attachment->mime);
header('Content-Length: ' . filesize(_PS_DOWNLOAD_DIR_ . $attachment->file));
header('Content-Disposition: attachment; filename="' . utf8_decode($attachment->file_name) . '"');
@set_time_limit(0);
$this->readfileChunked(_PS_DOWNLOAD_DIR_ . $attachment->file);
exit;
}
/**
* @see http://ca2.php.net/manual/en/function.readfile.php#54295
*/
public function readfileChunked(string $filename, bool $retbytes = true)
{
// how many bytes per chunk
$chunksize = 1 * (1024 * 1024);
$buffer = '';
$totalBytes = 0;
$handle = fopen($filename, 'rb');
if ($handle === false) {
return false;
}
while (!feof($handle)) {
$buffer = fread($handle, $chunksize);
echo $buffer;
ob_flush();
flush();
if ($retbytes) {
$totalBytes += strlen($buffer);
}
}
$status = fclose($handle);
if ($retbytes && $status) {
// return num. bytes delivered like readfile() does.
return $totalBytes;
}
return $status;
}
}

View File

@@ -0,0 +1,102 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class AuthControllerCore extends FrontController
{
/** @var bool */
public $ssl = true;
/** @var string */
public $php_self = 'authentication';
/** @var bool */
public $auth = false;
/**
* Check if the controller is available for the current user/visitor.
*
* @see Controller::checkAccess()
*
* @return bool
*/
public function checkAccess(): bool
{
if ($this->context->customer->isLogged() && !$this->ajax) {
$this->redirect_after = $this->authRedirection ? urlencode($this->authRedirection) : 'my-account';
$this->redirect();
}
return parent::checkAccess();
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
if (Tools::isSubmit('create_account')) {
$this->redirectWithNotifications('registration');
}
$should_redirect = false;
$login_form = $this->makeLoginForm()->fillWith(
Tools::getAllValues()
);
if (Tools::isSubmit('submitLogin')) {
if ($login_form->submit()) {
$should_redirect = true;
}
}
$this->context->smarty->assign([
'login_form' => $login_form->getProxy(),
]);
$this->setTemplate('customer/authentication');
parent::initContent();
if ($should_redirect && !$this->ajax) {
$back = rawurldecode(Tools::getValue('back'));
if (Tools::urlBelongsToShop($back)) {
// Checks to see if "back" is a fully qualified
// URL that is on OUR domain, with the right protocol
$this->redirectWithNotifications($back);
}
// Well we're not redirecting to a URL,
// so...
if ($this->authRedirection) {
// We may need to go there if defined
$this->redirectWithNotifications($this->authRedirection);
}
// go home
$this->redirectWithNotifications(__PS_BASE_URI__);
}
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = [
'title' => $this->trans('Log in to your account', [], 'Shop.Theme.Customeraccount'),
'url' => $this->context->link->getPageLink('authentication'),
];
return $breadcrumb;
}
/**
* {@inheritdoc}
*/
public function getCanonicalURL(): string
{
return $this->context->link->getPageLink('authentication');
}
}

View File

@@ -0,0 +1,713 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Core\Domain\Product\Stock\ValueObject\OutOfStockType;
class CartControllerCore extends FrontController
{
/** @var string */
public $php_self = 'cart';
protected $id_product;
protected $id_product_attribute;
protected $id_address_delivery;
protected $customization_id;
protected $qty;
/**
* To specify if you are in the preview mode or not.
*
* @var bool
*/
protected $preview;
/** @var bool */
public $ssl = true;
/**
* An array of errors, in case the update action of product is wrong.
*
* @var string[]
*/
protected $updateOperationError = [];
/**
* This is not a public page, so the canonical redirection is disabled.
*
* @param string $canonicalURL
*/
public function canonicalRedirection(string $canonicalURL = ''): void
{
}
/**
* Initialize cart controller.
*
* @see FrontController::init()
*/
public function init(): void
{
parent::init();
// Send noindex to avoid ghost carts by bots
header('X-Robots-Tag: noindex, nofollow', true);
// Get page main parameters
$this->id_product = (int) Tools::getValue('id_product', null);
$this->id_product_attribute = (int) Tools::getValue('id_product_attribute', Tools::getValue('ipa'));
$this->customization_id = (int) Tools::getValue('id_customization');
$this->qty = abs((int) Tools::getValue('qty', 1));
$this->id_address_delivery = (int) Tools::getValue('id_address_delivery');
$this->preview = ('1' === Tools::getValue('preview'));
if ('show' === Tools::getValue('action')) {
/* Check if the products in the cart are available */
$isAvailable = $this->areProductsAvailable();
if (Tools::getIsset('checkout')) {
Tools::redirect($this->context->link->getPageLink('order'));
}
if (true !== $isAvailable) {
$this->errors[] = $isAvailable;
}
/* Check if countries used in the cart are enabled */
if (true !== $this->context->cart->checkCountriesAreEnabled()) {
$this->errors[] = $this->trans(
'Some of the countries used in your cart are not available and cannot be used.',
[],
'Shop.Notifications.Error'
);
}
}
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
if (Configuration::isCatalogMode() && Tools::getValue('action') === 'show') {
Tools::redirect('index.php');
}
/*
* Check that minimal quantity conditions are respected for each product in the cart
* (this is to be applied only on page load, not for ajax calls)
*/
if (!Tools::getValue('ajax')) {
$this->checkCartProductsMinimalQuantities();
}
if ($this->context->cart->hasProducts()) {
$this->setTemplate('checkout/cart');
} else {
$this->context->smarty->assign([
'allProductsLink' => $this->context->link->getCategoryLink(
(int) Configuration::get('PS_HOME_CATEGORY')
),
]);
$this->setTemplate('checkout/cart-empty');
}
parent::initContent();
}
public function displayAjaxUpdate(): void
{
if (Configuration::isCatalogMode()) {
return;
}
$productsInCart = $this->context->cart->getProducts();
$updatedProducts = array_filter($productsInCart, [$this, 'productInCartMatchesCriteria']);
$updatedProduct = reset($updatedProducts);
$productQuantity = $updatedProduct['quantity'] ?? 0;
if (!$this->errors) {
$presentedCart = $this->cart_presenter->present($this->context->cart, true);
// filter product output
$presentedCart['products'] = $this->get('prestashop.core.filter.front_end_object.product_collection')
->filter($presentedCart['products']);
$this->ajaxRender(json_encode([
'success' => true,
'id_product' => $this->id_product,
'id_product_attribute' => $this->id_product_attribute,
'id_customization' => $this->customization_id,
'quantity' => $productQuantity,
'cart' => $presentedCart,
'errors' => empty($this->updateOperationError) ? '' : reset($this->updateOperationError),
]));
return;
} else {
$this->ajaxRender(json_encode([
'hasError' => true,
'errors' => $this->errors,
'quantity' => $productQuantity,
]));
return;
}
}
public function displayAjaxRefresh(): void
{
if (Configuration::isCatalogMode()) {
return;
}
ob_end_clean();
header('Content-Type: application/json');
$this->ajaxRender(json_encode([
'cart_detailed' => $this->render('checkout/_partials/cart-detailed'),
'cart_detailed_totals' => $this->render('checkout/_partials/cart-detailed-totals'),
'cart_summary_items_subtotal' => $this->render('checkout/_partials/cart-summary-items-subtotal'),
'cart_summary_products' => $this->render('checkout/_partials/cart-summary-products'),
'cart_summary_subtotals_container' => $this->render('checkout/_partials/cart-summary-subtotals'),
'cart_summary_totals' => $this->render('checkout/_partials/cart-summary-totals'),
'cart_detailed_actions' => $this->render('checkout/_partials/cart-detailed-actions'),
'cart_voucher' => $this->render('checkout/_partials/cart-voucher'),
'cart_summary_top' => $this->render('checkout/_partials/cart-summary-top'),
]));
}
/**
* @deprecated 1.7.3.1 the product link is now accessible
* in #quantity_wanted[data-url-update]
*/
public function displayAjaxProductRefresh(): void
{
if ($this->id_product) {
$idProductAttribute = 0;
$groups = Tools::getValue('group');
if (!empty($groups)) {
$idProductAttribute = (int) Product::getIdProductAttributeByIdAttributes(
$this->id_product,
$groups,
true
);
}
$url = $this->context->link->getProductLink(
$this->id_product,
null,
null,
null,
$this->context->language->id,
null,
$idProductAttribute,
false,
false,
true,
[
'quantity_wanted' => (int) $this->qty,
'preview' => $this->preview,
]
);
} else {
$url = false;
}
ob_end_clean();
header('Content-Type: application/json');
$this->ajaxRender(json_encode([
'success' => true,
'productUrl' => $url,
]));
}
public function postProcess(): void
{
$this->updateCart();
}
protected function updateCart(): void
{
// Update the cart ONLY if it's not a bot, in order to avoid ghost carts
if (!Connection::isBot()
&& !$this->errors
&& !($this->context->customer->isLogged() && !$this->isTokenValid())
) {
if (Tools::getIsset('add') || Tools::getIsset('update')) {
$this->processChangeProductInCart();
} elseif (Tools::getIsset('delete')) {
$this->processDeleteProductInCart();
} elseif (CartRule::isFeatureActive()) {
if (Tools::getIsset('addDiscount')) {
if (!($code = trim(Tools::getValue('discount_name')))) {
$this->errors[] = $this->trans(
'You must enter a voucher code.',
[],
'Shop.Notifications.Error'
);
} elseif (!Validate::isCleanHtml($code)) {
$this->errors[] = $this->trans(
'The voucher code is invalid.',
[],
'Shop.Notifications.Error'
);
} else {
$cartRule = new CartRule(CartRule::getIdByCode($code));
if (Validate::isLoadedObject($cartRule)) {
if ($error = $cartRule->checkValidity($this->context)) {
$this->errors[] = $error;
} else {
$result = $this->context->cart->addCartRule($cartRule->id);
if ($result !== true) {
// we have an incompatibility with another cart rule
$this->errors[] = $result;
}
}
} else {
$this->errors[] = $this->trans(
'This voucher does not exist.',
[],
'Shop.Notifications.Error'
);
}
}
} elseif (($id_cart_rule = (int) Tools::getValue('deleteDiscount'))
&& Validate::isUnsignedId($id_cart_rule)
) {
$this->context->cart->removeCartRule($id_cart_rule);
CartRule::autoAddToCart($this->context);
}
}
} elseif (!$this->isTokenValid() && Tools::getValue('action') !== 'show' && !Tools::getValue('ajax')) {
Tools::redirect('index.php');
}
}
/**
* This process delete a product from the cart.
*/
protected function processDeleteProductInCart(): void
{
$customization_product = Db::getInstance()->executeS(
'SELECT * FROM `' . _DB_PREFIX_ . 'customization`'
. ' WHERE `id_cart` = ' . (int) $this->context->cart->id
. ' AND `id_product` = ' . (int) $this->id_product
. ' AND `id_customization` != ' . (int) $this->customization_id
. ' AND `in_cart` = 1'
. ' AND `quantity` > 0'
);
if (count($customization_product)) {
$product = new Product((int) $this->id_product);
if ($this->id_product_attribute > 0) {
$minimal_quantity = (int) ProductAttribute::getAttributeMinimalQty($this->id_product_attribute);
} else {
$minimal_quantity = (int) $product->minimal_quantity;
}
$total_quantity = 0;
foreach ($customization_product as $custom) {
$total_quantity += $custom['quantity'];
}
if ($total_quantity < $minimal_quantity) {
$this->errors[] = $this->trans(
'You must add %quantity% minimum quantity',
['%quantity%' => $minimal_quantity],
'Shop.Notifications.Error'
);
return;
}
}
$data = [
'id_cart' => (int) $this->context->cart->id,
'id_product' => (int) $this->id_product,
'id_product_attribute' => (int) $this->id_product_attribute,
'customization_id' => (int) $this->customization_id,
'id_address_delivery' => (int) $this->id_address_delivery,
];
// An array [module_name => module_output] will be returned (no effect)
Hook::exec('actionObjectProductInCartDeleteBefore', $data, null, true);
if ($this->context->cart->deleteProduct(
$this->id_product,
$this->id_product_attribute,
$this->customization_id
)) {
Hook::exec('actionObjectProductInCartDeleteAfter', $data);
if (!Cart::getNbProducts((int) $this->context->cart->id)) {
$this->context->cart->setDeliveryOption(null);
$this->context->cart->gift = 0;
$this->context->cart->gift_message = '';
$this->context->cart->update();
}
$isAvailable = $this->areProductsAvailable();
if (true !== $isAvailable) {
$this->updateOperationError[] = $isAvailable;
}
}
CartRule::autoRemoveFromCart();
CartRule::autoAddToCart();
}
/**
* This process add or update a product in the cart.
*/
protected function processChangeProductInCart(): void
{
$mode = (Tools::getIsset('update') && $this->id_product) ? 'update' : 'add';
$ErrorKey = ('update' === $mode) ? 'updateOperationError' : 'errors';
if (Tools::getIsset('group')) {
$this->id_product_attribute = (int) Product::getIdProductAttributeByIdAttributes(
$this->id_product,
Tools::getValue('group')
);
}
if ($this->qty == 0) {
$this->{$ErrorKey}[] = $this->trans(
'Null quantity.',
[],
'Shop.Notifications.Error'
);
} elseif (!$this->id_product) {
$this->{$ErrorKey}[] = $this->trans(
'Product not found',
[],
'Shop.Notifications.Error'
);
}
$product = new Product($this->id_product, true, $this->context->language->id);
if (!$product->id || !$product->active || !$product->checkAccess($this->context->cart->id_customer)) {
$this->{$ErrorKey}[] = $this->trans(
'This product (%product%) is no longer available.',
['%product%' => $product->name],
'Shop.Notifications.Error'
);
return;
}
if (!$this->id_product_attribute && $product->hasAttributes()) {
$minimum_quantity = ($product->out_of_stock == OutOfStockType::OUT_OF_STOCK_DEFAULT)
? !Configuration::get('PS_ORDER_OUT_OF_STOCK')
: !$product->out_of_stock;
$this->id_product_attribute = Product::getDefaultAttribute($product->id, (int) $minimum_quantity);
// @todo do something better than a redirect admin !!
if (!$this->id_product_attribute) {
Tools::redirectAdmin($this->context->link->getProductLink($product));
}
}
$qty_to_check = $this->qty;
$cart_products = $this->context->cart->getProducts();
if (is_array($cart_products)) {
foreach ($cart_products as $cart_product) {
if ($this->productInCartMatchesCriteria($cart_product)) {
$qty_to_check = $cart_product['cart_quantity'];
if (Tools::getValue('op', 'up') == 'down') {
$qty_to_check -= $this->qty;
} else {
$qty_to_check += $this->qty;
}
break;
}
}
}
// Check product quantity availability
if ('update' !== $mode && $this->shouldAvailabilityErrorBeRaised($product, $qty_to_check)) {
/*
* If the product can't be in the cart in this quantity, we raise an error.
* For the purpose of this error message, we must get the real quantity in stock.
* No subtracting of quantity in the cart here.
*/
$availableProductQuantity = Product::getQuantity($this->id_product, $this->id_product_attribute);
$productName = Product::getProductName($this->id_product, $this->id_product_attribute);
if ($availableProductQuantity > 0) {
$this->errors[] = $this->trans(
'You can only buy %quantity% "%product%". Please adjust the quantity in your cart to continue.',
[
'%product%' => $productName,
'%quantity%' => $availableProductQuantity,
],
'Shop.Notifications.Error'
);
} else {
$this->errors[] = $this->trans(
'This product (%product%) is no longer available.',
['%product%' => $productName],
'Shop.Notifications.Error'
);
}
return;
}
// Check minimal_quantity
if (!$this->id_product_attribute) {
if ($qty_to_check < $product->minimal_quantity) {
$this->errors[] = $this->trans(
'The minimum purchase order quantity for the product %product% is %quantity%.',
['%product%' => $product->name, '%quantity%' => $product->minimal_quantity],
'Shop.Notifications.Error'
);
return;
}
} else {
$combination = new Combination($this->id_product_attribute);
if ($qty_to_check < $combination->minimal_quantity) {
$this->errors[] = $this->trans(
'The minimum purchase order quantity for the product %product% is %quantity%.',
['%product%' => $product->name, '%quantity%' => $combination->minimal_quantity],
'Shop.Notifications.Error'
);
return;
}
}
// If no errors, process product addition
if (!$this->errors) {
// Add cart if no cart found
if (!$this->context->cart->id) {
$this->context->cart->add();
if (Validate::isLoadedObject($this->context->cart)) {
$this->context->cookie->id_cart = (int) $this->context->cart->id;
}
}
// Check customizable fields
if (!$product->hasAllRequiredCustomizableFields() && !$this->customization_id) {
$this->{$ErrorKey}[] = $this->trans(
'Please fill in all of the required fields, and then save your customizations.',
[],
'Shop.Notifications.Error'
);
}
$update_quantity = $this->context->cart->updateQty(
$this->qty,
$this->id_product,
$this->id_product_attribute,
$this->customization_id,
Tools::getValue('op', 'up'),
0,
null,
true,
true
);
if ($update_quantity < 0) {
// If product has attribute, minimal quantity is set with minimal quantity of attribute
$minimal_quantity = ($this->id_product_attribute)
? ProductAttribute::getAttributeMinimalQty($this->id_product_attribute)
: $product->minimal_quantity;
$this->{$ErrorKey}[] = $this->trans(
'You must add %quantity% minimum quantity',
['%quantity%' => $minimal_quantity],
'Shop.Notifications.Error'
);
} elseif (!$update_quantity) {
$this->errors[] = $this->trans(
'You already have the maximum quantity available for this product.',
[],
'Shop.Notifications.Error'
);
} elseif ($this->shouldAvailabilityErrorBeRaised($product, $qty_to_check)) {
/*
* If the product can't be in the cart in this quantity, we raise an error.
* For the purpose of this error message, we must get the real quantity in stock.
* No subtracting of quantity in the cart here.
*/
$availableProductQuantity = Product::getQuantity($this->id_product, $this->id_product_attribute);
$productName = Product::getProductName($this->id_product, $this->id_product_attribute);
if ($availableProductQuantity > 0) {
$this->{$ErrorKey}[] = $this->trans(
'You can only buy %quantity% "%product%". Please adjust the quantity in your cart to continue.',
[
'%product%' => $productName,
'%quantity%' => $availableProductQuantity,
],
'Shop.Notifications.Error'
);
} else {
$this->{$ErrorKey}[] = $this->trans(
'This product (%product%) is no longer available.',
['%product%' => $productName],
'Shop.Notifications.Error'
);
}
}
}
// Check validity of all cart rules in cart and check if there are some automatic ones that should be applied
CartRule::autoRemoveFromCart();
CartRule::autoAddToCart();
// Finally check that all other products are also available, but only if there was no previous error
if ('add' !== $mode && empty($this->{$ErrorKey})) {
$areProductsAvailable = $this->areProductsAvailable();
if (true !== $areProductsAvailable) {
$this->{$ErrorKey}[] = $areProductsAvailable;
}
}
}
/**
* @param array $productInCart
*
* @return bool
*/
public function productInCartMatchesCriteria(array $productInCart)
{
return (
!isset($this->id_product_attribute)
|| (
$productInCart['id_product_attribute'] == $this->id_product_attribute
&& $productInCart['id_customization'] == $this->customization_id
)
) && isset($this->id_product) && $productInCart['id_product'] == $this->id_product;
}
/**
* Initializes a set of commonly used variables related to the current page, available for use
* in the template. @see FrontController::assignGeneralPurposeVariables for more information.
*
* @return array
*/
public function getTemplateVarPage(): array
{
$page = parent::getTemplateVarPage();
if (!$this->context->cart->hasProducts()) {
$page['body_classes']['cart-empty'] = true;
}
return $page;
}
/**
* Check product quantity availability to acknowledge whether
* an availability error should be raised.
*
* If shop has been configured to oversell, answer is no.
* If there is no items available (no stock), answer is yes.
* If there is items available, but the Cart already contains more than the quantity,
* answer is yes.
*
* @param Product $product
* @param int $qtyToCheck
*
* @return bool
*/
protected function shouldAvailabilityErrorBeRaised(Product $product, int $qtyToCheck)
{
if ($this->id_product_attribute) {
return !Product::isAvailableWhenOutOfStock($product->out_of_stock)
&& !ProductAttribute::checkAttributeQty($this->id_product_attribute, $qtyToCheck);
} elseif (Product::isAvailableWhenOutOfStock($product->out_of_stock)) {
return false;
}
/*
* We check if this product is out-of-stock.
*/
$availableProductQuantity = Product::getQuantity($this->id_product, $this->id_product_attribute);
if ($availableProductQuantity < $qtyToCheck) {
return true;
}
// Check if this product is out-of-stock after cart quantities have been removed from stock
// Be aware that Product::getQuantity() returns the available quantity after decreasing products in cart
$productQuantityAvailableAfterCartItemsHaveBeenRemovedFromStock = Product::getQuantity(
$this->id_product,
$this->id_product_attribute,
null,
$this->context->cart,
false
);
return $productQuantityAvailableAfterCartItemsHaveBeenRemovedFromStock < 0;
}
/**
* Check if the products in the cart are available.
* This is a general check that is handy when you want to check whole cart,
* for example when loading the cart or order page.
*
* @return bool|string
*/
protected function areProductsAvailable(): bool|string
{
$products = $this->context->cart->getProducts();
foreach ($products as $product) {
$currentProduct = new Product();
$currentProduct->hydrate($product);
if ($currentProduct->hasAttributes() && $product['id_product_attribute'] === '0') {
return $this->trans(
'The item %product% in your cart is now a product with attributes. Please delete it and choose one of its combinations to proceed with your order.',
['%product%' => $product['name']],
'Shop.Notifications.Error'
);
}
}
$product = $this->context->cart->checkQuantities(true);
if (true === $product || !is_array($product)) {
return true;
}
$productName = !empty($product['attributes']) ? $product['name'] . ' ' . $product['attributes'] : $product['name'];
if ($product['active'] && $product['quantity_available'] > 0) {
return $this->trans(
'You can only buy %quantity% "%product%". Please adjust the quantity in your cart to continue.',
[
'%product%' => $productName,
'%quantity%' => $product['quantity_available'],
],
'Shop.Notifications.Error'
);
}
return $this->trans(
'This product (%product%) is no longer available.',
['%product%' => $productName],
'Shop.Notifications.Error'
);
}
/**
* Check that minimal quantity conditions are respected for each product in the cart
*/
private function checkCartProductsMinimalQuantities(): void
{
$productList = $this->context->cart->getProducts();
foreach ($productList as $product) {
if ($product['minimal_quantity'] > $product['cart_quantity']) {
// display minimal quantity warning error message
$this->errors[] = $this->trans(
'The minimum purchase order quantity for the product %product% is %quantity%.',
[
'%product%' => $product['name'],
'%quantity%' => $product['minimal_quantity'],
],
'Shop.Notifications.Error'
);
}
}
}
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class ChangeCurrencyControllerCore extends FrontController
{
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
$currency = new Currency((int) Tools::getValue('id_currency'));
if (Validate::isLoadedObject($currency) && !$currency->deleted) {
$this->context->cookie->id_currency = (int) $currency->id;
$this->ajaxRender('1');
return;
}
$this->ajaxRender('0');
}
}

View File

@@ -0,0 +1,260 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShopBundle\Security\Admin\LegacyAdminTokenValidator;
class CmsControllerCore extends FrontController
{
public const CMS_CASE_PAGE = 1;
public const CMS_CASE_CATEGORY = 2;
/** @var string */
public $php_self = 'cms';
public $assignCase;
/**
* @var CMS|null
*/
protected $cms;
/**
* @var CMSCategory|null
*/
protected $cms_category;
/** @var bool */
public $ssl = false;
public function canonicalRedirection(string $canonicalURL = ''): void
{
if (Validate::isLoadedObject($this->cms) && ($canonicalURL = $this->context->link->getCMSLink($this->cms, $this->cms->link_rewrite))) {
parent::canonicalRedirection($canonicalURL);
} elseif (Validate::isLoadedObject($this->cms_category) && ($canonicalURL = $this->context->link->getCMSCategoryLink($this->cms_category))) {
parent::canonicalRedirection($canonicalURL);
}
}
/**
* Initialize cms controller.
*
* @see FrontController::init()
*/
public function init(): void
{
if ($id_cms = (int) Tools::getValue('id_cms')) {
$this->cms = new CMS($id_cms, $this->context->language->id, $this->context->shop->id);
} elseif ($id_cms_category = (int) Tools::getValue('id_cms_category')) {
$this->cms_category = new CMSCategory($id_cms_category, $this->context->language->id, $this->context->shop->id);
}
if (Configuration::get('PS_SSL_ENABLED') && Tools::getValue('content_only') && $id_cms && Validate::isLoadedObject($this->cms)
&& in_array($id_cms, $this->getSSLCMSPageIds())) {
$this->ssl = true;
}
parent::init();
$this->canonicalRedirection();
if (Validate::isLoadedObject($this->cms)) {
if (!$this->cms->isAssociatedToShop()) {
$this->redirect_after = '404';
$this->redirect();
} elseif (!$this->cms->active) {
$adminTokenValidator = $this->getContainer()->get(LegacyAdminTokenValidator::class);
$isAdminTokenValid = $adminTokenValidator->isTokenValid((int) Tools::getValue('id_employee'), Tools::getValue('adtoken'));
if (!$isAdminTokenValid) {
$this->redirect_after = '404';
$this->redirect();
}
} else {
$this->assignCase = self::CMS_CASE_PAGE;
}
} elseif (Validate::isLoadedObject($this->cms_category) && $this->cms_category->active) {
$this->assignCase = self::CMS_CASE_CATEGORY;
} else {
$this->redirect_after = '404';
$this->redirect();
}
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
if ($this->assignCase == self::CMS_CASE_PAGE) {
$cmsVar = $this->objectPresenter->present($this->cms);
// Chained hook call - if multiple modules are hooked here, they will receive the result of the previous one as a parameter
$filteredCmsContent = Hook::exec(
'filterCmsContent',
['object' => $cmsVar],
null,
false,
true,
false,
null,
true
);
if (!empty($filteredCmsContent['object'])) {
$cmsVar = $filteredCmsContent['object'];
}
$this->context->smarty->assign([
'cms' => $cmsVar,
]);
if ($this->cms->indexation == 0) {
$this->context->smarty->assign('nobots', true);
}
$this->setTemplate(
'cms/page',
['entity' => 'cms', 'id' => $this->cms->id]
);
} elseif ($this->assignCase == self::CMS_CASE_CATEGORY) {
$cmsCategoryVar = $this->getTemplateVarCategoryCms();
// Chained hook call - if multiple modules are hooked here, they will receive the result of the previous one as a parameter
$filteredCmsCategoryContent = Hook::exec(
'filterCmsCategoryContent',
['object' => $cmsCategoryVar],
null,
false,
true,
false,
null,
true
);
if (!empty($filteredCmsCategoryContent['object'])) {
$cmsCategoryVar = $filteredCmsCategoryContent['object'];
}
$this->context->smarty->assign($cmsCategoryVar);
$this->setTemplate(
'cms/category',
['entity' => 'cms_category', 'id' => $this->cms_category->id]
);
}
parent::initContent();
}
/**
* Return an array of IDs of CMS pages, which shouldn't be forwared to their canonical URLs in SSL environment.
* Required for pages which are shown in iframes.
*/
protected function getSSLCMSPageIds(): array
{
return [(int) Configuration::get('PS_CONDITIONS_CMS_ID'), (int) Configuration::get('LEGAL_CMS_ID_REVOCATION')];
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
if ($this->assignCase == self::CMS_CASE_CATEGORY) {
$cmsCategory = new CMSCategory($this->cms_category->id_cms_category);
} else {
$cmsCategory = new CMSCategory($this->cms->id_cms_category);
}
if ($cmsCategory->id_parent != 0) {
foreach (array_reverse($cmsCategory->getParentsCategories()) as $category) {
if ($category['active']) {
$cmsSubCategory = new CMSCategory($category['id_cms_category']);
$breadcrumb['links'][] = [
'title' => $cmsSubCategory->getName(),
'url' => $this->context->link->getCMSCategoryLink($cmsSubCategory),
];
}
}
}
if ($this->assignCase == self::CMS_CASE_PAGE && $this->context->controller instanceof CmsControllerCore) {
$breadcrumb['links'][] = [
'title' => $this->context->controller->cms->meta_title,
'url' => $this->context->link->getCMSLink($this->context->controller->cms),
];
}
return $breadcrumb;
}
/**
* Initializes a set of commonly used variables related to the current page, available for use
* in the template. @see FrontController::assignGeneralPurposeVariables for more information.
*
* @return array
*/
public function getTemplateVarPage(): array
{
$page = parent::getTemplateVarPage();
if ($this->assignCase == self::CMS_CASE_CATEGORY) {
$page['body_classes']['cms-id-' . $this->cms_category->id] = true;
} else {
$page['body_classes']['cms-id-' . $this->cms->id] = true;
if (!$this->cms->indexation) {
$page['meta']['robots'] = 'noindex';
}
}
return $page;
}
public function getTemplateVarCategoryCms(): array
{
$categoryCms = [];
$categoryCms['cms_category'] = $this->objectPresenter->present($this->cms_category);
$categoryCms['sub_categories'] = [];
$categoryCms['cms_pages'] = [];
foreach ($this->cms_category->getSubCategories($this->context->language->id) as $subCategory) {
$categoryCms['sub_categories'][$subCategory['id_cms_category']] = $subCategory;
$categoryCms['sub_categories'][$subCategory['id_cms_category']]['link'] = $this->context->link->getCMSCategoryLink($subCategory['id_cms_category'], $subCategory['link_rewrite']);
}
foreach (CMS::getCMSPages($this->context->language->id, (int) $this->cms_category->id, true, (int) $this->context->shop->id) as $cmsPages) {
$categoryCms['cms_pages'][$cmsPages['id_cms']] = $cmsPages;
$categoryCms['cms_pages'][$cmsPages['id_cms']]['link'] = $this->context->link->getCMSLink($cmsPages['id_cms'], $cmsPages['link_rewrite']);
}
return $categoryCms;
}
/**
* @return CMS|null
*/
public function getCms(): ?CMS
{
return $this->cms;
}
/**
* @return CMSCategory|null
*/
public function getCmsCategory(): ?CMSCategory
{
return $this->cms_category;
}
/**
* {@inheritdoc}
*/
public function getCanonicalURL(): string
{
if (Validate::isLoadedObject($this->cms)) {
return $this->context->link->getCMSLink($this->cms, $this->cms->link_rewrite);
} elseif (Validate::isLoadedObject($this->cms_category)) {
return $this->context->link->getCMSCategoryLink($this->cms_category);
}
return '';
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class ContactControllerCore extends FrontController
{
/** @var string */
public $php_self = 'contact';
/** @var bool */
public $ssl = true;
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
parent::initContent();
$this->setTemplate('contact');
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = [
'title' => $this->getTranslator()->trans('Contact us', [], 'Shop.Theme.Global'),
'url' => $this->context->link->getPageLink('contact'),
];
return $breadcrumb;
}
/**
* {@inheritdoc}
*/
public function getCanonicalURL(): string
{
return $this->context->link->getPageLink('contact');
}
}

View File

@@ -0,0 +1,205 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class DiscountControllerCore extends FrontController
{
/** @var bool */
public $auth = true;
/** @var string */
public $php_self = 'discount';
/** @var string */
public $authRedirection = 'discount';
/** @var bool */
public $ssl = true;
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
if (Configuration::isCatalogMode()) {
Tools::redirect('index.php');
}
$this->context->smarty->assign([
'cart_rules' => $this->getTemplateVarCartRules(),
]);
parent::initContent();
$this->setTemplate('customer/discount');
}
public function getTemplateVarCartRules(): array
{
$cart_rules = [];
$customerId = $this->context->customer->id;
$languageId = $this->context->language->id;
$vouchers = CartRule::getCustomerCartRules(
$languageId,
$customerId,
true,
false
);
foreach ($vouchers as $key => $voucher) {
$voucherCustomerId = (int) $voucher['id_customer'];
$voucherIsRestrictedToASingleCustomer = ($voucherCustomerId !== 0);
if ($voucherIsRestrictedToASingleCustomer && $customerId !== $voucherCustomerId) {
continue;
}
if ($voucher['quantity'] === 0 || $voucher['quantity_for_user'] === 0) {
continue;
}
$cart_rule = $this->buildCartRuleFromVoucher($voucher);
$cart_rules[$key] = $cart_rule;
}
return $cart_rules;
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = $this->addMyAccountToBreadcrumb();
$breadcrumb['links'][] = [
'title' => $this->trans('Your vouchers', [], 'Shop.Theme.Customeraccount'),
'url' => $this->context->link->getPageLink('discount'),
];
return $breadcrumb;
}
/**
* @param array $voucher
*
* @return mixed
*/
protected function getCombinableVoucherTranslation(array $voucher)
{
if ($voucher['cart_rule_restriction']) {
$combinableVoucherTranslation = $this->trans('No', [], 'Shop.Theme.Global');
} else {
$combinableVoucherTranslation = $this->trans('Yes', [], 'Shop.Theme.Global');
}
return $combinableVoucherTranslation;
}
/**
* Formats a value of a voucher with fixed reduction
*
* @param bool $hasTaxIncluded
* @param float $amount
* @param int $currencyId
*
* @return string
*/
protected function formatReductionAmount(bool $hasTaxIncluded, float $amount, int $currencyId)
{
if ($hasTaxIncluded) {
$taxTranslation = $this->trans('Tax included', [], 'Shop.Theme.Checkout');
} else {
$taxTranslation = $this->trans('Tax excluded', [], 'Shop.Theme.Checkout');
}
return sprintf(
'%s ' . $taxTranslation,
$this->context->getCurrentLocale()->formatPrice($amount, Currency::getIsoCodeById((int) $currencyId))
);
}
/**
* Formats a value of a voucher with percentage reduction
*
* @param float $percentage
*
* @return string
*/
protected function formatReductionInPercentage(float $percentage)
{
return sprintf('%s%%', $this->context->getCurrentLocale()->formatNumber($percentage));
}
/**
* Formats all reductions and benefits of a voucher. (One voucher can provide a discount and gift at the same time.)
*
* @param array $voucher
*
* @return array
*/
protected function accumulateCartRuleValue(array $voucher)
{
$cartRuleValue = [];
if ($voucher['reduction_percent'] > 0) {
$cartRuleValue[] = $this->formatReductionInPercentage((float) $voucher['reduction_percent']);
}
if ($voucher['reduction_amount'] > 0) {
$cartRuleValue[] = $this->formatReductionAmount(
(bool) $voucher['reduction_tax'],
(float) $voucher['reduction_amount'],
$voucher['reduction_currency']
);
}
if ($voucher['free_shipping']) {
$cartRuleValue[] = $this->trans('Free shipping', [], 'Shop.Theme.Checkout');
}
if ($voucher['gift_product'] > 0) {
$cartRuleValue[] = Product::getProductName(
$voucher['gift_product'],
$voucher['gift_product_attribute']
);
}
return $cartRuleValue;
}
/**
* Prepares a single row of voucher table to show it to customer.
*
* @param array $voucher
*
* @return array
*/
protected function buildCartRuleFromVoucher(array $voucher): array
{
$voucher['voucher_date'] = Tools::displayDate($voucher['date_to'], false);
if ((int) $voucher['minimum_amount'] === 0) {
$voucher['voucher_minimal'] = $this->trans('None', [], 'Shop.Theme.Global');
} else {
$voucher['voucher_minimal'] = $this->context->getCurrentLocale()->formatPrice(
$voucher['minimum_amount'],
Currency::getIsoCodeById((int) $voucher['minimum_amount_currency'])
);
}
$voucher['voucher_cumulable'] = $this->getCombinableVoucherTranslation($voucher);
// Get all benefits of this voucher into array (discount, gift, free shipping etc.)
$cartRuleValues = $this->accumulateCartRuleValue($voucher);
// And combine them into a string
// If for some magical reason the voucher has no benefit (should not be achievable in BO), we display a dash
if (0 === count($cartRuleValues)) {
$voucher['value'] = '-';
} else {
$voucher['value'] = implode(' + ', $cartRuleValues);
}
return $voucher;
}
}

View File

@@ -0,0 +1,343 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class GetFileControllerCore extends FrontController
{
protected const MIME_TYPES = [
'ez' => 'application/andrew-inset',
'hqx' => 'application/mac-binhex40',
'cpt' => 'application/mac-compactpro',
'doc' => 'application/msword',
'oda' => 'application/oda',
'pdf' => 'application/pdf',
'ai' => 'application/postscript',
'eps' => 'application/postscript',
'ps' => 'application/postscript',
'smi' => 'application/smil',
'smil' => 'application/smil',
'wbxml' => 'application/vnd.wap.wbxml',
'wmlc' => 'application/vnd.wap.wmlc',
'wmlsc' => 'application/vnd.wap.wmlscriptc',
'bcpio' => 'application/x-bcpio',
'vcd' => 'application/x-cdlink',
'pgn' => 'application/x-chess-pgn',
'cpio' => 'application/x-cpio',
'csh' => 'application/x-csh',
'dcr' => 'application/x-director',
'dir' => 'application/x-director',
'dxr' => 'application/x-director',
'dvi' => 'application/x-dvi',
'spl' => 'application/x-futuresplash',
'gtar' => 'application/x-gtar',
'hdf' => 'application/x-hdf',
'js' => 'application/x-javascript',
'skp' => 'application/x-koan',
'skd' => 'application/x-koan',
'skt' => 'application/x-koan',
'skm' => 'application/x-koan',
'latex' => 'application/x-latex',
'nc' => 'application/x-netcdf',
'cdf' => 'application/x-netcdf',
'sh' => 'application/x-sh',
'shar' => 'application/x-shar',
'swf' => 'application/x-shockwave-flash',
'sit' => 'application/x-stuffit',
'sv4cpio' => 'application/x-sv4cpio',
'sv4crc' => 'application/x-sv4crc',
'tar' => 'application/x-tar',
'tcl' => 'application/x-tcl',
'tex' => 'application/x-tex',
'texinfo' => 'application/x-texinfo',
'texi' => 'application/x-texinfo',
't' => 'application/x-troff',
'tr' => 'application/x-troff',
'roff' => 'application/x-troff',
'man' => 'application/x-troff-man',
'me' => 'application/x-troff-me',
'ms' => 'application/x-troff-ms',
'ustar' => 'application/x-ustar',
'src' => 'application/x-wais-source',
'xhtml' => 'application/xhtml+xml',
'xht' => 'application/xhtml+xml',
'zip' => 'application/zip',
'au' => 'audio/basic',
'snd' => 'audio/basic',
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'kar' => 'audio/midi',
'mpga' => 'audio/mpeg',
'mp2' => 'audio/mpeg',
'mp3' => 'audio/mpeg',
'aif' => 'audio/x-aiff',
'aiff' => 'audio/x-aiff',
'aifc' => 'audio/x-aiff',
'm3u' => 'audio/x-mpegurl',
'ram' => 'audio/x-pn-realaudio',
'rm' => 'audio/x-pn-realaudio',
'rpm' => 'audio/x-pn-realaudio-plugin',
'ra' => 'audio/x-realaudio',
'wav' => 'audio/x-wav',
'pdb' => 'chemical/x-pdb',
'xyz' => 'chemical/x-xyz',
'bmp' => 'image/bmp',
'gif' => 'image/gif',
'ief' => 'image/ief',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'jpe' => 'image/jpeg',
'png' => 'image/png',
'tiff' => 'image/tiff',
'tif' => 'image/tif',
'djvu' => 'image/vnd.djvu',
'djv' => 'image/vnd.djvu',
'wbmp' => 'image/vnd.wap.wbmp',
'ras' => 'image/x-cmu-raster',
'pnm' => 'image/x-portable-anymap',
'pbm' => 'image/x-portable-bitmap',
'pgm' => 'image/x-portable-graymap',
'ppm' => 'image/x-portable-pixmap',
'rgb' => 'image/x-rgb',
'xbm' => 'image/x-xbitmap',
'xpm' => 'image/x-xpixmap',
'xwd' => 'image/x-windowdump',
'igs' => 'model/iges',
'iges' => 'model/iges',
'msh' => 'model/mesh',
'mesh' => 'model/mesh',
'silo' => 'model/mesh',
'wrl' => 'model/vrml',
'vrml' => 'model/vrml',
'css' => 'text/css',
'html' => 'text/html',
'htm' => 'text/html',
'asc' => 'text/plain',
'txt' => 'text/plain',
'rtx' => 'text/richtext',
'rtf' => 'text/rtf',
'sgml' => 'text/sgml',
'sgm' => 'text/sgml',
'tsv' => 'text/tab-seperated-values',
'wml' => 'text/vnd.wap.wml',
'wmls' => 'text/vnd.wap.wmlscript',
'etx' => 'text/x-setext',
'xml' => 'text/xml',
'xsl' => 'text/xml',
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpe' => 'video/mpeg',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',
'mxu' => 'video/vnd.mpegurl',
'avi' => 'video/x-msvideo',
'movie' => 'video/x-sgi-movie',
'ice' => 'x-conference-xcooltalk',
];
/** @var bool */
protected $display_header = false;
/** @var bool */
protected $display_footer = false;
/**
* Initialize the controller.
*
* @see FrontController::init()
*/
public function init(): void
{
if (isset($this->context->employee) && $this->context->employee->isLoggedBack() && Tools::getValue('file')) {
// Admin can directly access to file
$filename = Tools::getValue('file');
if (!Validate::isSha1($filename)) {
throw new PrestaShopException('Filename is not a valid SHA1 checksum.');
}
$file = _PS_DOWNLOAD_DIR_ . (string) preg_replace('/\.{2,}/', '.', $filename);
$filename = ProductDownload::getFilenameFromFilename(Tools::getValue('file'));
if (empty($filename)) {
$newFileName = Tools::getValue('filename');
if (!empty($newFileName)) {
$filename = Tools::getValue('filename');
} else {
$filename = 'file';
}
}
if (!file_exists($file)) {
Tools::redirect('index.php');
}
} else {
if (!($key = Tools::getValue('key'))) {
$this->displayCustomError('Invalid key.');
}
Tools::setCookieLanguage();
if (!$this->context->customer->isLogged()) {
if (!Tools::getValue('secure_key') && !Tools::getValue('id_order')) {
Tools::redirect('index.php?controller=authentication&back=get-file.php%26key=' . $key);
} elseif (Tools::getValue('secure_key') && Tools::getValue('id_order')) {
$order = new Order((int) Tools::getValue('id_order'));
if (!Validate::isLoadedObject($order)) {
$this->displayCustomError('Invalid key.');
}
if ($order->secure_key != Tools::getValue('secure_key')) {
$this->displayCustomError('Invalid key.');
}
} else {
$this->displayCustomError('Invalid key.');
}
}
/* Key format: <sha1-filename>-<hashOrder> */
$tmp = explode('-', $key);
if (count($tmp) != 2) {
$this->displayCustomError('Invalid key.');
}
$hash = $tmp[1];
if (!($info = OrderDetail::getDownloadFromHash($hash))) {
$this->displayCustomError('This product does not exist in our store.');
}
/* check whether order has been paid, which is required to download the product */
$order = new Order((int) $info['id_order']);
$state = $order->getCurrentOrderState();
if (!$state || !$state->paid) {
$this->displayCustomError('This order has not been paid.');
}
// Check whether the order was made by the current user
// If the order was made by a guest, skip this step
$customer = new Customer((int) $order->id_customer);
if (!$customer->is_guest && $order->secure_key !== $this->context->customer->secure_key) {
Tools::redirect('index.php?controller=authentication&back=get-file.php%26key=' . $key);
}
/* Product no more present in catalog */
if (!isset($info['id_product_download']) || empty($info['id_product_download'])) {
$this->displayCustomError('This product has been deleted.');
}
if (!Validate::isFileName($info['filename']) || !file_exists(_PS_DOWNLOAD_DIR_ . $info['filename'])) {
$this->displayCustomError('This file no longer exists.');
}
if (isset($info['product_quantity_refunded'], $info['product_quantity_return'])
&& ($info['product_quantity_refunded'] > 0 || $info['product_quantity_return'] > 0)) {
$this->displayCustomError('This product has been refunded.');
}
$now = time();
$product_deadline = (int) strtotime($info['download_deadline']);
if ($now > $product_deadline && $info['download_deadline'] != '0000-00-00 00:00:00') {
$this->displayCustomError('The product deadline is in the past.');
}
if ($info['date_expiration'] !== '0000-00-00 00:00:00') {
$customer_deadline = (int) strtotime($info['date_expiration']);
if ($now > $customer_deadline) {
$this->displayCustomError('Expiration date has passed, you cannot download this product');
}
}
if ($info['download_nb'] >= $info['nb_downloadable'] && $info['nb_downloadable']) {
$this->displayCustomError('You have reached the maximum number of allowed downloads.');
}
/* Access is authorized -> increment download value for the customer */
OrderDetail::incrementDownload($info['id_order_detail']);
$file = _PS_DOWNLOAD_DIR_ . $info['filename'];
$filename = $info['display_filename'];
}
$this->sendFile($file, $filename);
}
protected function sendFile(string $file, string $filename, bool $forceDownload = true): void
{
if (ob_get_level() && ob_get_length() > 0) {
ob_end_clean();
}
/* Set headers for download */
header('Content-Transfer-Encoding: binary');
header('Content-Type: ' . $this->getMimeType($file, $filename));
header('Content-Length: ' . filesize($file));
if ($forceDownload) {
header('Content-Disposition: attachment; filename="' . $filename . '"');
}
// prevents max execution timeout, when reading large files
@set_time_limit(0);
$fp = fopen($file, 'rb');
if ($fp && is_resource($fp)) {
while (!feof($fp)) {
echo fgets($fp, 16384);
}
}
exit;
}
private function getMimeType(string $file, string $filename): string
{
if (function_exists('finfo_open')) {
$finfo = @finfo_open(FILEINFO_MIME);
$mimeType = @finfo_file($finfo, $file);
@finfo_close($finfo);
} elseif (function_exists('mime_content_type')) {
$mimeType = @mime_content_type($file);
} elseif (function_exists('exec')) {
$mimeType = trim(@exec('file -b --mime-type ' . escapeshellarg($file)));
if (!$mimeType) {
$mimeType = trim(@exec('file --mime ' . escapeshellarg($file)));
}
if (!$mimeType) {
$mimeType = trim(@exec('file -bi ' . escapeshellarg($file)));
}
}
if (empty($mimeType)) {
$bName = basename($filename);
$bName = explode('.', $bName);
$bName = strtolower($bName[count($bName) - 1]);
$mimeType = static::MIME_TYPES[$bName] ?? 'application/octet-stream';
}
return $mimeType;
}
/**
* Display an error message with js
* and redirect using js function.
*
* @param string $msg
*/
protected function displayCustomError(string $msg)
{
$translations = [
'Invalid key.' => $this->trans('Invalid key.', [], 'Shop.Notifications.Error'),
'This product does not exist in our store.' => $this->trans('This product does not exist in our store.', [], 'Shop.Notifications.Error'),
'This product has been deleted.' => $this->trans('This product has been deleted.', [], 'Shop.Notifications.Error'),
'This file no longer exists.' => $this->trans('This file no longer exists.', [], 'Shop.Notifications.Error'),
'This product has been refunded.' => $this->trans('This product has been refunded.', [], 'Shop.Notifications.Error'),
'The product deadline is in the past.' => $this->trans('The product deadline is in the past.', [], 'Shop.Notifications.Error'),
'Expiration date exceeded' => $this->trans('The product expiration date has passed, preventing you from download this product.', [], 'Shop.Notifications.Error'),
'Expiration date has passed, you cannot download this product' => $this->trans('Expiration date has passed, you cannot download this product.', [], 'Shop.Notifications.Error'),
'You have reached the maximum number of allowed downloads.' => $this->trans('You have reached the maximum number of downloads allowed.', [], 'Shop.Notifications.Error'),
]; ?>
<script type="text/javascript">
//<![CDATA[
alert("<?php echo isset($translations[$msg]) ? html_entity_decode($translations[$msg], ENT_QUOTES, 'utf-8') : html_entity_decode($msg, ENT_QUOTES, 'utf-8'); ?>");
window.location.href = '<?php echo __PS_BASE_URI__; ?>';
//]]>
</script>
<?php
exit;
}
}

View File

@@ -0,0 +1,183 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\Presenter\Order\OrderPresenter;
use PrestaShop\PrestaShop\Core\Security\PasswordPolicyConfiguration;
class GuestTrackingControllerCore extends FrontController
{
/** @var bool */
public $ssl = true;
/** @var bool */
public $auth = false;
/** @var string */
public $php_self = 'guest-tracking';
protected $order;
/**
* Initialize guest tracking controller.
*
* @see FrontController::init()
*/
public function init(): void
{
if ($this->context->customer->isLogged()) {
Tools::redirect('history.php');
}
parent::init();
}
/**
* Start forms process.
*
* @see FrontController::postProcess()
*/
public function postProcess(): void
{
$order_reference = current(explode('#', Tools::getValue('order_reference')));
$email = Tools::getValue('email');
if (!$email && !$order_reference) {
return;
} elseif (!$email || !$order_reference) {
$this->errors[] = $this->getTranslator()->trans(
'Please provide the required information',
[],
'Shop.Notifications.Error'
);
return;
}
$this->order = Order::getByReferenceAndEmail($order_reference, $email);
if (!Validate::isLoadedObject($this->order)) {
$this->errors[] = $this->getTranslator()->trans(
'We couldn\'t find your order with the information provided, please try again',
[],
'Shop.Notifications.Error'
);
}
if (Tools::isSubmit('submitTransformGuestToCustomer') && Tools::getValue('password')) {
$customer = new Customer((int) $this->order->id_customer);
/** @var string $password */
$password = Tools::getValue('password');
if (empty($password)) {
$this->errors[] = $this->trans(
'Enter a password to transform your guest account into a customer account.',
[],
'Shop.Forms.Help'
);
} elseif (!Validate::isAcceptablePasswordLength($password)) {
$this->errors[] = $this->trans(
'Your password length must be between %d and %d',
[Configuration::get(PasswordPolicyConfiguration::CONFIGURATION_MINIMUM_LENGTH), Configuration::get(PasswordPolicyConfiguration::CONFIGURATION_MAXIMUM_LENGTH)],
'Shop.Forms.Help'
);
} elseif (!Validate::isAcceptablePasswordScore($password)) {
$this->errors[] = $this->trans(
'Customer password is too weak',
[],
'Shop.Forms.Help'
);
// Prevent error
// A) either on page refresh
// B) if we already transformed him in other window or through backoffice
} elseif ($customer->is_guest == 0) {
$this->errors[] = $this->trans(
'A customer account has already been created from this guest account. Please sign in.',
[],
'Shop.Notifications.Error'
);
// Check if a different customer with the same email was not already created in a different window or through backoffice
} elseif (Customer::customerExists($customer->email)) {
$this->errors[] = $this->trans(
'You can\'t transform your account into a customer account, because a registered customer with the same email already exists.',
[],
'Shop.Notifications.Error'
);
// Attempt to convert the customer
} elseif ($customer->transformToCustomer($this->context->language->id, $password)) {
$this->success[] = $this->trans(
'Your guest account has been successfully transformed into a customer account. You can now log in as a registered shopper.',
[],
'Shop.Notifications.Success'
);
} else {
$this->errors[] = $this->trans(
'An unexpected error occurred while creating your account.',
[],
'Shop.Notifications.Error'
);
}
}
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
parent::initContent();
if (!Validate::isLoadedObject($this->order)) {
$this->setTemplate('customer/guest-login');
return;
}
if ((int) $this->order->isReturnable()) {
$this->info[] = $this->trans(
'You cannot return merchandise with a guest account.',
[],
'Shop.Notifications.Warning'
);
}
// Kept for backwards compatibility (is_customer), inline it in later versions
$registered_customer_exists = Customer::customerExists(Tools::getValue('email'));
$this->context->smarty->assign([
'order' => (new OrderPresenter())->present($this->order),
'guest_email' => Tools::getValue('email'),
'registered_customer_exists' => $registered_customer_exists,
'is_customer' => $registered_customer_exists, // Kept for backwards compatibility
'HOOK_DISPLAYORDERDETAIL' => Hook::exec('displayOrderDetail', ['order' => $this->order]),
]);
$this->setTemplate('customer/guest-tracking');
}
public function getBreadcrumbLinks(): array
{
$breadcrumbLinks = parent::getBreadcrumbLinks();
$breadcrumbLinks['links'][] = [
'title' => $this->getTranslator()->trans('Guest order tracking', [], 'Shop.Theme.Checkout'),
'url' => $this->context->link->getPageLink('guest-tracking'),
];
if (Validate::isLoadedObject($this->order)) {
$breadcrumbLinks['links'][] = [
'title' => $this->order->reference,
'url' => '#',
];
}
return $breadcrumbLinks;
}
/**
* {@inheritdoc}
*/
public function getCanonicalURL(): string
{
return $this->context->link->getPageLink('guest-tracking');
}
}

View File

@@ -0,0 +1,119 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\Presenter\Order\OrderPresenter;
class HistoryControllerCore extends FrontController
{
/** @var bool */
public $auth = true;
/** @var string */
public $php_self = 'history';
/** @var string */
public $authRedirection = 'history';
/** @var bool */
public $ssl = true;
/** @var OrderPresenter|null */
public $order_presenter;
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
if (Configuration::isCatalogMode()) {
Tools::redirect('index.php');
}
if ($this->order_presenter === null) {
$this->order_presenter = new OrderPresenter();
}
if (Tools::isSubmit('slowvalidation')) {
$this->warning[] = $this->trans('If you have just placed an order, it may take a few minutes for it to be validated. Please refresh this page if your order is missing.', [], 'Shop.Notifications.Warning');
}
$this->context->smarty->assign([
'orders' => $this->getTemplateVarOrders(),
]);
parent::initContent();
$this->setTemplate('customer/history');
}
public function getTemplateVarOrders(): array
{
$orders = [];
$customer_orders = Order::getCustomerOrders($this->context->customer->id);
foreach ($customer_orders as $customer_order) {
$order = new Order((int) $customer_order['id_order']);
$orders[$customer_order['id_order']] = $this->order_presenter->present($order);
}
return $orders;
}
/**
* Generates a URL to download the PDF invoice of a given order
*
* @param Order $order
* @param Context $context
*
* @return string
*/
public static function getUrlToInvoice(Order $order, Context $context)
{
$url_to_invoice = '';
if ((bool) Configuration::get('PS_INVOICE') && OrderState::invoiceAvailable($order->current_state) && count($order->getInvoicesCollection())) {
$params = [
'id_order' => (int) $order->id,
'secure_key' => (!$context->customer->isLogged()) ? $order->secure_key : null,
];
$url_to_invoice = $context->link->getPageLink('pdf-invoice', null, null, $params);
}
return $url_to_invoice;
}
/**
* Generates a URL to reorder a given order
*
* @param int $id_order
* @param Context $context
*
* @return string
*/
public static function getUrlToReorder(int $id_order, Context $context)
{
$url_to_reorder = '';
if (!(bool) Configuration::get('PS_DISALLOW_HISTORY_REORDERING')) {
$params = [
'submitReorder' => 1,
'id_order' => (int) $id_order,
];
$url_to_reorder = $context->link->getPageLink('order', null, null, $params);
}
return $url_to_reorder;
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = $this->addMyAccountToBreadcrumb();
$breadcrumb['links'][] = [
'title' => $this->trans('Order history', [], 'Shop.Theme.Customeraccount'),
'url' => $this->context->link->getPageLink('history'),
];
return $breadcrumb;
}
}

View File

@@ -0,0 +1,76 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class IdentityControllerCore extends FrontController
{
/** @var bool */
public $auth = true;
/** @var string */
public $php_self = 'identity';
/** @var string */
public $authRedirection = 'identity';
/** @var bool */
public $ssl = true;
public $passwordRequired = true;
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
$should_redirect = false;
$customer_form = $this->makeCustomerForm()->setPasswordRequired($this->passwordRequired);
$customer = new Customer();
$customer_form->getFormatter()
->setAskForNewPassword(true)
->setAskForPassword($this->passwordRequired)
->setPasswordRequired($this->passwordRequired)
->setPartnerOptinRequired($customer->isFieldRequired('optin'));
if (Tools::isSubmit('submitCreate')) {
$customer_form->fillWith(Tools::getAllValues());
if ($customer_form->submit()) {
$this->success[] = $this->trans('Information successfully updated.', [], 'Shop.Notifications.Success');
$should_redirect = true;
} else {
$this->errors[] = $this->trans('Could not update your information, please check your data.', [], 'Shop.Notifications.Error');
}
} else {
$customer_form->fillFromCustomer(
$this->context->customer
);
}
$this->context->smarty->assign([
'customer_form' => $customer_form->getProxy(),
]);
if ($should_redirect) {
$this->redirectWithNotifications($this->getCurrentURL());
}
parent::initContent();
$this->setTemplate('customer/identity');
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = $this->addMyAccountToBreadcrumb();
$breadcrumb['links'][] = [
'title' => $this->trans('Your personal information', [], 'Shop.Theme.Customeraccount'),
'url' => $this->context->link->getPageLink('identity'),
];
return $breadcrumb;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class IndexControllerCore extends FrontController
{
/** @var string */
public $php_self = 'index';
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
parent::initContent();
$this->context->smarty->assign([
'HOOK_HOME' => Hook::exec('displayHome'),
]);
$this->setTemplate('index');
}
/**
* {@inheritdoc}
*/
public function getCanonicalURL(): string
{
return $this->context->link->getPageLink('index');
}
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class MyAccountControllerCore extends FrontController
{
/** @var bool */
public $auth = true;
/** @var string */
public $php_self = 'my-account';
/** @var string */
public $authRedirection = 'my-account';
/** @var bool */
public $ssl = true;
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
parent::initContent();
$this->setTemplate('customer/my-account');
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = $this->addMyAccountToBreadcrumb();
return $breadcrumb;
}
}

View File

@@ -0,0 +1,340 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\Presenter\Order\OrderPresenter;
use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings;
use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface;
use PrestaShop\PrestaShop\Core\Security\PasswordPolicyConfiguration;
use ZxcvbnPhp\Zxcvbn;
class OrderConfirmationControllerCore extends FrontController
{
/** @var bool */
public $ssl = true;
/** @var string */
public $php_self = 'order-confirmation';
/** @var int Cart ID */
public $id_cart;
public $id_module;
public $id_order;
public $secure_key;
/** @var Order Order object we found by cart ID */
protected $order;
/** @var Customer Customer object related to the cart */
protected $customer;
public $reference; // Deprecated
public $order_presenter; // Deprecated
/**
* Initialize order confirmation controller.
*
* @see FrontController::init()
*/
public function init(): void
{
// Test below to prevent unnecessary logs from "parent::init()"
$this->id_cart = (int) Tools::getValue('id_cart', 0);
if (!empty($this->context->cookie->id_cart) && $this->context->cookie->id_cart == $this->id_cart) {
$cart = new Cart($this->id_cart);
if ($cart->orderExists()) {
unset($this->context->cookie->id_cart);
}
}
parent::init();
/*
* There is a special case for free orders, when this page does more than just display
* the confirmation. It also creates the order if it is free. This is done if free_order
* parameter is passed to this page.
*
* After the order is created, we redirect to the same page without free_order parameter
* and display the confirmation as usual.
*/
if ((bool) Tools::getValue('free_order') === true) {
$this->checkFreeOrder();
}
/*
* Because of order splitting scenarios, we must get the data by id_cart parameter (not id_order),
* so we can display all orders made from this cart.
*
* It's not implemented yet, however, and probably won't be, because we are switching to a new
* logic of multiple shipments per order, which doesn't require splitting orders anymore.
*/
$this->id_order = Order::getIdByCartId((int) $this->id_cart);
if (empty($this->id_order)) {
Tools::redirect('pagenotfound');
}
// Now, load the order object and check validity
$this->order = new Order((int) $this->id_order);
if (!Validate::isLoadedObject($this->order)) {
Tools::redirect('pagenotfound');
}
/*
* Now, to prevent users from seeing other customers' order confirmations, we are using
* a secure key mechanism. The confirmation link contains a unique key which is also saved
* in database when the order is created. If the key from the URL doesn't match the one
* in database, we redirect to "page not found".
*/
$this->secure_key = Tools::getValue('key', false);
if (empty($this->secure_key) || $this->secure_key != $this->order->secure_key) {
Tools::redirect('pagenotfound');
}
// Last step, initialize some other data
$this->id_module = $this->order->module == 'free_order' ? -1 : Module::getModuleIdByName($this->order->module);
// This data is kept only for backward compatibility purposes
$this->reference = (string) $this->order->reference;
// If checks passed, initialize customer, we will need him anyway
$this->customer = new Customer((int) $this->order->id_customer);
}
/**
* Logic after submitting forms
*
* @see FrontController::postProcess()
*/
public function postProcess(): void
{
if (Tools::isSubmit('submitTransformGuestToCustomer')) {
// Only variable we need is the password
// There is no need to check other variables, because hacker would be kicked out in init(), if he tried to convert another customer
$password = Tools::getValue('password');
if (empty($password)) {
$this->errors[] = $this->trans(
'Enter a password to transform your guest account into a customer account.',
[],
'Shop.Forms.Help'
);
} else {
if (Validate::isAcceptablePasswordLength($password) === false) {
$this->errors[] = $this->translator->trans(
'Password must be between %d and %d characters long',
[
Configuration::get(PasswordPolicyConfiguration::CONFIGURATION_MINIMUM_LENGTH),
Configuration::get(PasswordPolicyConfiguration::CONFIGURATION_MAXIMUM_LENGTH),
],
'Shop.Notifications.Error'
);
}
if (Validate::isAcceptablePasswordScore($password) === false) {
$wordingsForScore = [
$this->translator->trans('Very weak', [], 'Shop.Theme.Global'),
$this->translator->trans('Weak', [], 'Shop.Theme.Global'),
$this->translator->trans('Average', [], 'Shop.Theme.Global'),
$this->translator->trans('Strong', [], 'Shop.Theme.Global'),
$this->translator->trans('Very strong', [], 'Shop.Theme.Global'),
];
$globalErrorMessage = $this->translator->trans(
'The minimum score must be: %s',
[
$wordingsForScore[(int) Configuration::get(PasswordPolicyConfiguration::CONFIGURATION_MINIMUM_SCORE)],
],
'Shop.Notifications.Error'
);
if ($this->context->shop->theme->get('global_settings.new_password_policy_feature') !== true) {
$zxcvbn = new Zxcvbn();
$result = $zxcvbn->passwordStrength($password);
if (!empty($result['feedback']['warning'])) {
$this->errors[] = $this->translator->trans(
$result['feedback']['warning'], [], 'Shop.Theme.Global'
);
} else {
$this->errors[] = $globalErrorMessage;
}
foreach ($result['feedback']['suggestions'] as $suggestion) {
$this->errors[] = $this->translator->trans($suggestion, [], 'Shop.Theme.Global');
}
} else {
$this->errors[] = $globalErrorMessage;
}
}
}
if (!empty($this->errors)) {
return;
}
// Prevent error
// A) either on page refresh
// B) if we already transformed him in other window or through backoffice
if ($this->customer->is_guest == 0) {
$this->errors[] = $this->trans(
'A customer account has already been created from this guest account. Please sign in.',
[],
'Shop.Notifications.Error'
);
// Check if a different customer with the same email was not already created in a different window or through backoffice
} elseif (Customer::customerExists($this->customer->email)) {
$this->errors[] = $this->trans(
'You can\'t transform your account into a customer account, because a registered customer with the same email already exists.',
[],
'Shop.Notifications.Error'
);
// Attempt to convert the customer
} elseif ($this->customer->transformToCustomer($this->context->language->id, $password)) {
$this->success[] = $this->trans(
'Your guest account has been successfully transformed into a customer account. You can now log in as a registered shopper.',
[],
'Shop.Notifications.Success'
);
} else {
$this->errors[] = $this->trans(
'An unexpected error occurred while creating your account.',
[],
'Shop.Notifications.Error'
);
}
}
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
parent::initContent();
/** @var FeatureFlagStateCheckerInterface $featureFlagManager */
$featureFlagManager = $this->get(FeatureFlagStateCheckerInterface::class);
$this->context->smarty->assign([
'HOOK_ORDER_CONFIRMATION' => $this->displayOrderConfirmation($this->order),
'HOOK_PAYMENT_RETURN' => $this->displayPaymentReturn($this->order),
'order' => (new OrderPresenter())->present($this->order),
'order_customer' => $this->objectPresenter->present($this->customer),
'is_multishipment_enabled' => $featureFlagManager->isEnabled(FeatureFlagSettings::FEATURE_FLAG_IMPROVED_SHIPMENT),
'registered_customer_exists' => Customer::customerExists($this->customer->email),
]);
$this->setTemplate('checkout/order-confirmation');
// If logged in guest we clear the cookie for security reasons
if ($this->context->customer->is_guest) {
$this->context->customer->mylogout();
}
}
/**
* Execute the hook displayPaymentReturn. This hook should be used to display payment
* information on the order confirmation page. Payment status, instructions, QR code etc.
*/
public function displayPaymentReturn(Order $order)
{
// Check if we have a sensible module ID. Free orders have -1 as module ID
if (!Validate::isUnsignedId($this->id_module)) {
return false;
}
// Hook called only for the module concerned
return Hook::exec('displayPaymentReturn', ['order' => $order], $this->id_module);
}
/**
* Execute the hook displayOrderConfirmation.
*/
public function displayOrderConfirmation(Order $order)
{
return Hook::exec('displayOrderConfirmation', ['order' => $order]);
}
/**
* Check if an order is free and create it. After creation, we redirect to the same page
* which will display the order confirmation as usual.
*/
protected function checkFreeOrder(): void
{
/*
* Verify if this is not a faulty or duplicate call. If an order
* already exists for this cart, we do not create another one.
*/
if (!empty(Order::getIdByCartId((int) $this->id_cart))) {
return;
}
$cart = $this->context->cart;
if ($cart->id_customer == 0 || $cart->id_address_delivery == 0 || $cart->id_address_invoice == 0) {
Tools::redirect($this->context->link->getPageLink('order'));
}
$customer = new Customer($cart->id_customer);
if (!Validate::isLoadedObject($customer)) {
Tools::redirect($this->context->link->getPageLink('order'));
}
$total = (float) $cart->getOrderTotal(true, Cart::BOTH);
if ($total > 0) {
Tools::redirect($this->context->link->getPageLink('order'));
}
$order = new PaymentFree();
$order->validateOrder(
$cart->id,
(int) Configuration::get('PS_OS_PAYMENT'),
0,
$this->trans('Free order', [], 'Admin.Orderscustomers.Feature'),
null,
[],
null,
false,
$cart->secure_key
);
/*
* Redirect back to this page to display the order confirmation.
* Note the id_module parameter with value -1, it's only kept for
* backward compatibility, but not used anymore.
*/
Tools::redirect($this->context->link->getPageLink(
'order-confirmation',
null,
null,
[
'id_cart' => (int) $cart->id,
'id_module' => '-1',
'id_order' => (int) $order->currentOrder,
'key' => $cart->secure_key,
]
));
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = [
'title' => $this->trans('Order confirmation', [], 'Shop.Theme.Checkout'),
'url' => $this->context->link->getPageLink('order-confirmation'),
];
return $breadcrumb;
}
/**
* @return Order
*/
public function getOrder(): Order
{
return $this->order;
}
/**
* @return Customer
*/
public function getCustomer(): Customer
{
return $this->customer;
}
}

View File

@@ -0,0 +1,434 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\Product\PriceFormatter;
use PrestaShop\PrestaShop\Adapter\Shipment\DeliveryOptionsProvider;
use PrestaShop\PrestaShop\Core\Checkout\TermsAndConditions;
use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings;
use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface;
use PrestaShop\PrestaShop\Core\Foundation\Templating\RenderableProxy;
use PrestaShopBundle\Translation\TranslatorComponent;
class OrderControllerCore extends FrontController
{
/** @var bool */
public $ssl = true;
/** @var string */
public $php_self = 'order';
/** @var string */
public $page_name = 'checkout';
public $checkoutWarning = [];
/**
* @var CheckoutProcess
*/
protected $checkoutProcess;
/**
* @var CartChecksum
*/
protected $cartChecksum;
/**
* Overrides the same parameter in FrontController
*
* @var bool automaticallyAllocateInvoiceAddress
*/
protected $automaticallyAllocateInvoiceAddress = false;
/**
* Overrides the same parameter in FrontController
*
* @var bool
*/
protected $automaticallyAllocateDeliveryAddress = false;
/**
* Initialize order controller.
*
* @see FrontController::init()
*/
public function init(): void
{
parent::init();
$this->cartChecksum = new CartChecksum(new AddressChecksum());
}
public function postProcess(): void
{
parent::postProcess();
if (Tools::isSubmit('submitReorder')
&& $this->context->customer->isLogged()
&& $id_order = (int) Tools::getValue('id_order')
) {
$oldCart = new Cart(Order::getCartIdStatic($id_order, $this->context->customer->id));
$duplication = $oldCart->duplicate();
if (!$duplication || !Validate::isLoadedObject($duplication['cart'])) {
$this->errors[] = $this->trans('Sorry. We cannot renew your order.', [], 'Shop.Notifications.Error');
} elseif (!$duplication['success']) {
$this->errors[] = $this->trans(
'Some items are no longer available, and we are unable to renew your order.',
[],
'Shop.Notifications.Error'
);
} else {
$this->context->cookie->id_cart = $duplication['cart']->id;
$context = $this->context;
$context->cart = $duplication['cart'];
CartRule::autoAddToCart($context);
$this->context->cookie->write();
Tools::redirect($this->context->link->getPageLink('order'));
}
}
$this->bootstrap();
}
/**
* @return CheckoutProcess
*/
public function getCheckoutProcess(): CheckoutProcess
{
return $this->checkoutProcess;
}
/**
* @return CheckoutSession
*/
public function getCheckoutSession(): CheckoutSession
{
/** @var FeatureFlagStateCheckerInterface $featureFlagManager */
$featureFlagManager = $this->get(FeatureFlagStateCheckerInterface::class);
if ($featureFlagManager->isEnabled(FeatureFlagSettings::FEATURE_FLAG_IMPROVED_SHIPMENT)) {
$deliveryOptionsFinder = new DeliveryOptionsProvider(
$this->context,
$this->getTranslator(),
$this->objectPresenter,
new PriceFormatter(),
$this->cart_presenter,
);
} else {
$deliveryOptionsFinder = new DeliveryOptionsFinder(
$this->context,
$this->getTranslator(),
$this->objectPresenter,
new PriceFormatter()
);
}
$session = new CheckoutSession(
$this->context,
$deliveryOptionsFinder
);
return $session;
}
protected function bootstrap(): void
{
$translator = $this->getTranslator();
$session = $this->getCheckoutSession();
$this->checkoutProcess = $this->buildCheckoutProcess($session, $translator);
Hook::exec('actionCheckoutRender', ['checkoutProcess' => &$this->checkoutProcess]);
}
/**
* Persists cart-related data in checkout session.
*
* @param CheckoutProcess $process
*/
protected function saveDataToPersist(CheckoutProcess $process)
{
$data = $process->getDataToPersist();
$cart = $this->context->cart;
$data['checksum'] = $this->cartChecksum->generateChecksum($cart);
Db::getInstance()->execute(
'UPDATE ' . _DB_PREFIX_ . 'cart SET checkout_session_data = "' . pSQL(json_encode($data)) . '"
WHERE id_cart = ' . (int) $cart->id
);
}
/**
* Restores from checkout session some previously persisted cart-related data.
*
* @param CheckoutProcess $process
*/
protected function restorePersistedData(CheckoutProcess $process)
{
$cart = $this->context->cart;
$customer = $this->context->customer;
$rawData = Db::getInstance()->getValue(
'SELECT checkout_session_data FROM ' . _DB_PREFIX_ . 'cart WHERE id_cart = ' . (int) $cart->id
);
$data = json_decode($rawData ?? '', true);
if (!is_array($data)) {
$data = [];
}
$addressValidator = new AddressValidator();
$invalidAddressIds = $addressValidator->validateCartAddresses($cart);
// Build the currently selected address' warning message (if relevant)
if (!$customer->isGuest() && !empty($invalidAddressIds)) {
$this->checkoutWarning['address'] = [
'id_address' => (int) reset($invalidAddressIds),
'exception' => $this->trans(
'Your address is incomplete, please update it.',
[],
'Shop.Notifications.Error'
),
];
}
// Prevent check for guests
if ($customer->id) {
// Prepare all other addresses' warning messages (if relevant).
// These messages are displayed when changing the selected address.
$allInvalidAddressIds = $addressValidator->validateCustomerAddresses($customer, $this->context->language);
$this->checkoutWarning['invalid_addresses'] = $allInvalidAddressIds;
}
if (isset($data['checksum']) && $data['checksum'] === $this->cartChecksum->generateChecksum($cart)) {
$process->restorePersistedData($data);
}
}
public function displayAjaxselectDeliveryOption(): void
{
$cart = $this->cart_presenter->present(
$this->context->cart,
true
);
ob_end_clean();
header('Content-Type: application/json');
$this->ajaxRender(json_encode([
'preview' => $this->render('checkout/_partials/cart-summary', [
'cart' => $cart,
'static_token' => Tools::getToken(false),
]),
]));
}
public function displayAjaxCheckCartStillOrderable(): void
{
$responseData = [
'errors' => false,
'cartUrl' => '',
];
if ($this->context->cart->isAllProductsInStock() !== true
|| $this->context->cart->checkAllProductsAreStillAvailableInThisState() !== true
|| $this->context->cart->checkAllProductsHaveMinimalQuantities() !== true
|| $this->context->cart->checkCountriesAreEnabled() !== true) {
$responseData['errors'] = true;
$responseData['cartUrl'] = $this->context->link->getPageLink('cart', null, null, ['action' => 'show']);
}
header('Content-Type: application/json');
$this->ajaxRender(json_encode($responseData));
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
if (Configuration::isCatalogMode()) {
Tools::redirect('index.php');
}
$this->restorePersistedData($this->checkoutProcess);
$this->checkoutProcess->handleRequest(
Tools::getAllValues()
);
$presentedCart = $this->cart_presenter->present($this->context->cart, true);
$shouldRedirectToCart = false;
// Check the cart meets minimal order amount treshold
// Check that the cart is not empty
if (count($presentedCart['products']) <= 0 || $presentedCart['minimalPurchaseRequired']) {
$shouldRedirectToCart = true;
}
// Check that products are still orderable, at any point in checkout
if ($this->context->cart->isAllProductsInStock() !== true
|| $this->context->cart->checkAllProductsAreStillAvailableInThisState() !== true
|| $this->context->cart->checkAllProductsHaveMinimalQuantities() !== true) {
$shouldRedirectToCart = true;
}
// Additionally, check that the addresses are valid
if ($this->context->cart->checkCountriesAreEnabled() !== true) {
$shouldRedirectToCart = true;
}
// If there was a problem, we redirect the user to cart, CartController deals with display of detailed errors
// We don't redirect in case of ajax requests, so we can get our response
if ($shouldRedirectToCart === true && !$this->ajax) {
$cartLink = $this->context->link->getPageLink('cart', null, null, ['action' => 'show']);
$this->redirectWithNotifications($cartLink);
}
$this->checkoutProcess
->setNextStepReachable()
->markCurrentStep()
->invalidateAllStepsAfterCurrent();
$this->saveDataToPersist($this->checkoutProcess);
if (!$this->checkoutProcess->hasErrors()) {
if ($_SERVER['REQUEST_METHOD'] !== 'GET' && !$this->ajax) {
$this->redirectWithNotifications(
$this->checkoutProcess->getCheckoutSession()->getCheckoutURL()
);
}
}
$this->context->smarty->assign([
'checkout_process' => new RenderableProxy($this->checkoutProcess),
'display_transaction_updated_info' => Tools::getIsset('updatedTransaction'),
'tos_cms' => $this->getDefaultTermsAndConditions(),
]);
parent::initContent();
$this->setTemplate('checkout/checkout');
}
public function displayAjaxAddressForm(): void
{
$addressForm = $this->makeAddressForm();
if (Tools::getIsset('id_address') && ($id_address = (int) Tools::getValue('id_address'))) {
$addressForm->loadAddressById($id_address);
}
if (Tools::getIsset('id_country')) {
$addressForm->fillWith(['id_country' => Tools::getValue('id_country')]);
}
$stepTemplateParameters = [];
foreach ($this->checkoutProcess->getSteps() as $step) {
if ($step instanceof CheckoutAddressesStep) {
$stepTemplateParameters = $step->getTemplateParameters();
}
}
$templateParams = array_merge(
$addressForm->getTemplateVariables(),
$stepTemplateParameters,
['type' => 'delivery']
);
ob_end_clean();
header('Content-Type: application/json');
$this->ajaxRender(json_encode([
'address_form' => $this->render(
'checkout/_partials/address-form',
$templateParams
),
]));
}
/**
* Return default TOS link for checkout footer
*
* @return string|bool
*/
protected function getDefaultTermsAndConditions(): string|bool
{
$cms = new CMS((int) Configuration::get('PS_CONDITIONS_CMS_ID'), $this->context->language->id);
if (!Validate::isLoadedObject($cms)) {
return false;
}
$link = $this->context->link->getCMSLink($cms, $cms->link_rewrite);
$termsAndConditions = new TermsAndConditions();
$termsAndConditions
->setText(
'[' . $cms->meta_title . ']',
$link
)
->setIdentifier('terms-and-conditions-footer');
return $termsAndConditions->format();
}
/**
* @param CheckoutSession $session
* @param TranslatorComponent $translator
*
* @return CheckoutProcess
*/
protected function buildCheckoutProcess(CheckoutSession $session, $translator)
{
$checkoutProcess = new CheckoutProcess(
$this->context,
$session
);
$checkoutProcess
->addStep(new CheckoutPersonalInformationStep(
$this->context,
$translator,
$this->makeLoginForm(),
$this->makeCustomerForm()
))
->addStep(new CheckoutAddressesStep(
$this->context,
$translator,
$this->makeAddressForm()
));
if (!$this->context->cart->isVirtualCart()) {
$checkoutDeliveryStep = new CheckoutDeliveryStep(
$this->context,
$translator
);
$checkoutDeliveryStep
->setRecyclablePackAllowed((bool) Configuration::get('PS_RECYCLABLE_PACK'))
->setGiftAllowed((bool) Configuration::get('PS_GIFT_WRAPPING'))
->setIncludeTaxes(
!Product::getTaxCalculationMethod((int) $this->context->cart->id_customer)
&& (int) Configuration::get('PS_TAX')
)
->setDisplayTaxesLabel(Configuration::get('PS_TAX'))
->setGiftCost(
$this->context->cart->getGiftWrappingPrice(
$checkoutDeliveryStep->getIncludeTaxes()
)
);
$checkoutProcess->addStep($checkoutDeliveryStep);
}
$checkoutProcess
->addStep(new CheckoutPaymentStep(
$this->context,
$translator,
new PaymentOptionsFinder(),
new ConditionsToApproveFinder(
$this->context,
$translator
),
));
return $checkoutProcess;
}
}

View File

@@ -0,0 +1,233 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\Presenter\Order\OrderPresenter;
use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings;
use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface;
class OrderDetailControllerCore extends FrontController
{
/** @var string */
public $php_self = 'order-detail';
/** @var bool */
public $auth = true;
/** @var string */
public $authRedirection = 'history';
/** @var bool */
public $ssl = true;
protected $order_to_display;
protected $reference;
/**
* Start forms process.
*
* @see FrontController::postProcess()
*/
public function postProcess(): void
{
if (Tools::isSubmit('submitMessage')) {
$idOrder = (int) Tools::getValue('id_order');
$msgText = Tools::getValue('msgText');
if (!$idOrder || !Validate::isUnsignedId($idOrder)) {
$this->errors[] = $this->trans('The order is no longer valid.', [], 'Shop.Notifications.Error');
} elseif (empty(trim($msgText))) {
$this->errors[] = $this->trans('The message cannot be blank.', [], 'Shop.Notifications.Error');
}
if (!count($this->errors)) {
$order = new Order($idOrder);
if (Validate::isLoadedObject($order) && $order->id_customer == $this->context->customer->id) {
// check if a thread already exist
$id_customer_thread = CustomerThread::getIdCustomerThreadByEmailAndIdOrder($this->context->customer->email, $order->id);
$id_product = (int) Tools::getValue('id_product');
$cm = new CustomerMessage();
if (!$id_customer_thread) {
$ct = new CustomerThread();
$ct->id_contact = 0;
$ct->id_customer = (int) $order->id_customer;
$ct->id_shop = (int) $this->context->shop->id;
if ($id_product && $order->orderContainProduct($id_product)) {
$ct->id_product = $id_product;
}
$ct->id_order = (int) $order->id;
$ct->id_lang = (int) $this->context->language->id;
$ct->email = $this->context->customer->email;
$ct->status = 'open';
$ct->token = Tools::passwdGen(12);
$ct->add();
} else {
$ct = new CustomerThread((int) $id_customer_thread);
$ct->status = 'open';
$ct->update();
}
$cm->id_customer_thread = $ct->id;
$cm->message = $msgText;
$cm->id_product = $id_product;
$client_ip_address = Tools::getRemoteAddr();
$cm->ip_address = (string) ip2long($client_ip_address);
$cm->add();
if (!Configuration::get('PS_MAIL_EMAIL_MESSAGE')) {
$to = (string) Configuration::get('PS_SHOP_EMAIL');
} else {
$to = new Contact((int) Configuration::get('PS_MAIL_EMAIL_MESSAGE'));
$to = (string) $to->email;
}
$toName = (string) Configuration::get('PS_SHOP_NAME');
$customer = $this->context->customer;
$product = new Product($id_product);
$product_name = '';
if (Validate::isLoadedObject($product) && isset($product->name[(int) $this->context->language->id])) {
$product_name = $product->name[(int) $this->context->language->id];
}
if (Validate::isLoadedObject($customer)) {
Mail::Send(
$this->context->language->id,
'order_customer_comment',
$this->trans(
'Message from a customer',
[],
'Emails.Subject'
),
[
'{lastname}' => $customer->lastname,
'{firstname}' => $customer->firstname,
'{email}' => $customer->email,
'{id_order}' => (int) $order->id,
'{order_name}' => $order->getUniqReference(),
'{message}' => Tools::nl2br(Tools::htmlentitiesUTF8($msgText)),
'{product_name}' => $product_name,
],
$to,
$toName,
(string) Configuration::get('PS_SHOP_EMAIL'),
$customer->firstname . ' ' . $customer->lastname,
null,
null,
_PS_MAIL_DIR_,
false,
null,
null,
$customer->email
);
}
Tools::redirect($this->context->link->getPageLink(
'order-detail',
null,
null,
[
'id_order' => $idOrder,
'messagesent' => 1,
]
));
} else {
$this->redirect_after = '404';
$this->redirect();
}
}
}
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
parent::initContent();
if (Configuration::isCatalogMode()) {
Tools::redirect('index.php');
}
$id_order = (int) Tools::getValue('id_order');
$id_order = $id_order && Validate::isUnsignedId($id_order) ? $id_order : false;
if (!$id_order) {
$reference = Tools::getValue('reference');
$reference = $reference && Validate::isReference($reference) ? $reference : false;
$order = $reference ? Order::getByReference($reference)->getFirst() : false;
$id_order = $order ? $order->id : false;
}
if (!$id_order) {
$this->redirect_after = '404';
$this->redirect();
} else {
if (Tools::getIsset('errorQuantity')) {
$this->errors[] = $this->trans('You do not have enough products to request an additional merchandise return.', [], 'Shop.Notifications.Error');
} elseif (Tools::getIsset('errorMsg')) {
$this->errors[] = $this->trans('Please provide an explanation for your RMA.', [], 'Shop.Notifications.Error');
} elseif (Tools::getIsset('errorDetail1')) {
$this->errors[] = $this->trans('Please check at least one product you would like to return.', [], 'Shop.Notifications.Error');
} elseif (Tools::getIsset('errorDetail2')) {
$this->errors[] = $this->trans('For each product you wish to add, please specify the desired quantity.', [], 'Shop.Notifications.Error');
} elseif (Tools::getIsset('errorNotReturnable')) {
$this->errors[] = $this->trans('This order cannot be returned', [], 'Shop.Notifications.Error');
} elseif (Tools::getIsset('messagesent')) {
$this->success[] = $this->trans('Message successfully sent', [], 'Shop.Notifications.Success');
}
$order = new Order($id_order);
if (Validate::isLoadedObject($order) && $order->id_customer == $this->context->customer->id) {
if ($order->id_shop != $this->context->shop->id && $this->context->customer->id_shop_group == $this->context->shop->id_shop_group) {
$shopGroup = new ShopGroup($this->context->customer->id_shop_group);
if (!$shopGroup->share_order) {
$this->redirect_after = '404';
$this->redirect();
}
}
$this->order_to_display = (new OrderPresenter())->present($order);
$this->reference = $order->reference;
/** @var FeatureFlagStateCheckerInterface $featureFlagManager */
$featureFlagManager = $this->get(FeatureFlagStateCheckerInterface::class);
$this->context->smarty->assign([
'order' => $this->order_to_display,
'orderIsVirtual' => $order->isVirtual(),
'HOOK_DISPLAYORDERDETAIL' => Hook::exec('displayOrderDetail', ['order' => $order]),
'is_multishipment_enabled' => $featureFlagManager->isEnabled(FeatureFlagSettings::FEATURE_FLAG_IMPROVED_SHIPMENT),
]);
} else {
$this->redirect_after = '404';
$this->redirect();
}
unset($order);
}
$this->setTemplate('customer/order-detail');
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = $this->addMyAccountToBreadcrumb();
$breadcrumb['links'][] = [
'title' => $this->trans('Order history', [], 'Shop.Theme.Customeraccount'),
'url' => $this->context->link->getPageLink('history'),
];
if (!empty($this->reference)) {
$breadcrumb['links'][] = [
'title' => $this->reference,
'url' => '#',
];
}
return $breadcrumb;
}
}

View File

@@ -0,0 +1,167 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\Presenter\Order\OrderReturnPresenter;
class OrderFollowControllerCore extends FrontController
{
/** @var bool */
public $auth = true;
/** @var string */
public $php_self = 'order-follow';
/** @var string */
public $authRedirection = 'order-follow';
/** @var bool */
public $ssl = true;
/**
* Start forms process.
*
* @see FrontController::postProcess()
*/
public function postProcess(): void
{
if (Tools::isSubmit('submitReturnMerchandise')) {
$order_qte_input = Tools::getValue('order_qte_input');
if (!$id_order = (int) Tools::getValue('id_order')) {
Tools::redirect($this->context->link->getPageLink('history'));
}
if (!($ids_order_detail = Tools::getValue('ids_order_detail'))) {
Tools::redirect($this->context->link->getPageLink(
'order-detail',
null,
null,
[
'id_order' => $id_order,
'errorDetail1' => 1,
]
));
}
if (!$order_qte_input) {
Tools::redirect($this->context->link->getPageLink(
'order-detail',
null,
null,
[
'id_order' => $id_order,
'errorDetail2' => 1,
]
));
}
$order = new Order((int) $id_order);
if (!$order->isReturnable()) {
Tools::redirect($this->context->link->getPageLink(
'order-detail',
null,
null,
[
'id_order' => $id_order,
'errorNotReturnable' => 1,
]
));
}
if ($order->id_customer != $this->context->customer->id) {
Tools::redirect($this->context->link->getPageLink(
'order-detail',
null,
null,
[
'id_order' => $id_order,
'errorNotReturnable' => 1,
]
));
}
$orderReturn = new OrderReturn();
$orderReturn->id_customer = (int) $this->context->customer->id;
$orderReturn->id_order = $id_order;
$orderReturn->question = htmlspecialchars(Tools::getValue('returnText'));
if (empty($orderReturn->question)) {
Tools::redirect($this->context->link->getPageLink(
'order-detail',
null,
null,
[
'id_order' => $id_order,
'errorMsg' => 1,
'ids_order_detail' => $ids_order_detail,
'order_qte_input' => $order_qte_input,
]
));
}
if (!$orderReturn->checkEnoughProduct($ids_order_detail, $order_qte_input)) {
Tools::redirect($this->context->link->getPageLink(
'order-detail',
null,
null,
[
'id_order' => $id_order,
'errorQuantity' => 1,
]
));
}
$orderReturn->state = 1;
$orderReturn->add();
$orderReturn->addReturnDetail($ids_order_detail, $order_qte_input);
Hook::exec('actionOrderReturn', ['orderReturn' => $orderReturn]);
Tools::redirect($this->context->link->getPageLink('order-follow'));
}
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
if ((bool) Configuration::get('PS_ORDER_RETURN') === false) {
$this->redirect_after = '404';
$this->redirect();
}
if (Configuration::isCatalogMode()) {
Tools::redirect('index.php');
}
$this->context->smarty->assign('ordersReturn', $this->getTemplateVarOrdersReturns());
parent::initContent();
$this->setTemplate('customer/order-follow');
}
public function getTemplateVarOrdersReturns(): array
{
$orders_returns = [];
$orders_return = OrderReturn::getOrdersReturn($this->context->customer->id);
$orderReturnPresenter = new OrderReturnPresenter(
Configuration::get('PS_RETURN_PREFIX', $this->context->language->id),
$this->context->link
);
foreach ($orders_return as $id_order_return => $order_return) {
$orders_returns[$id_order_return] = $orderReturnPresenter->present($order_return);
}
return $orders_returns;
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = $this->addMyAccountToBreadcrumb();
$breadcrumb['links'][] = [
'title' => $this->trans('Merchandise returns', [], 'Shop.Theme.Global'),
'url' => $this->context->link->getPageLink('order-follow'),
];
return $breadcrumb;
}
}

View File

@@ -0,0 +1,178 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\Image\ImageRetriever;
use PrestaShop\PrestaShop\Adapter\Presenter\Order\OrderReturnLazyArray;
use PrestaShop\PrestaShop\Adapter\Presenter\Order\OrderReturnPresenter;
class OrderReturnControllerCore extends FrontController
{
/** @var bool */
public $auth = true;
/** @var string */
public $php_self = 'order-return';
/** @var string */
public $authRedirection = 'order-follow';
/** @var bool */
public $ssl = true;
/**
* Initialize order return controller.
*
* @see FrontController::init()
*/
public function init(): void
{
parent::init();
$id_order_return = (int) Tools::getValue('id_order_return');
if (!Validate::isUnsignedId($id_order_return)) {
$this->redirect_after = '404';
$this->redirect();
} else {
$order_return = new OrderReturn((int) $id_order_return);
if (Validate::isLoadedObject($order_return) && $order_return->id_customer == $this->context->cookie->id_customer) {
$order = new Order((int) $order_return->id_order);
if (Validate::isLoadedObject($order)) {
if ($order_return->state == 1) {
$this->warning[] = $this->trans('You must wait for confirmation before returning any merchandise.', [], 'Shop.Notifications.Warning');
}
// StarterTheme: Use presenters!
$this->context->smarty->assign([
'return' => $this->getTemplateVarOrderReturn($order_return),
'products' => $this->getTemplateVarProducts((int) $order_return->id, $order),
]);
} else {
$this->redirect_after = '404';
$this->redirect();
}
} else {
$this->redirect_after = '404';
$this->redirect();
}
}
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
if (Configuration::isCatalogMode()) {
Tools::redirect('index.php');
}
parent::initContent();
$this->setTemplate('customer/order-return');
}
public function getTemplateVarOrderReturn(OrderReturn $orderReturn)
{
$orderReturns = OrderReturn::getOrdersReturn($orderReturn->id_customer, $orderReturn->id_order, false, null, $orderReturn->id);
if (empty($orderReturns)) {
return [];
}
$orderReturnPresenter = new OrderReturnPresenter(
Configuration::get('PS_RETURN_PREFIX', $this->context->language->id),
$this->context->link
);
return $orderReturnPresenter->present(array_shift($orderReturns));
}
public function getTemplateVarProducts(int $order_return_id, Order $order)
{
$products = [];
$return_products = OrderReturn::getOrdersReturnProducts((int) $order_return_id, $order);
foreach ($return_products as $id_return_product => $return_product) {
if (!isset($return_product['deleted'])) {
$products[$id_return_product] = $return_product;
$products[$id_return_product]['customizations'] = ($return_product['customizedDatas']) ? $this->getTemplateVarCustomization($return_product) : [];
}
}
return $products;
}
public function getTemplateVarCustomization(array $product)
{
$product_customizations = [];
$imageRetriever = new ImageRetriever($this->context->link);
foreach ($product['customizedDatas'] as $byAddress) {
foreach ($byAddress as $customization) {
$presentedCustomization = [
'quantity' => $customization['quantity'],
'fields' => [],
'id_customization' => null,
];
foreach ($customization['datas'] as $byType) {
$field = [];
foreach ($byType as $data) {
switch ($data['type']) {
case Product::CUSTOMIZE_FILE:
$field['type'] = 'image';
$field['image'] = $imageRetriever->getCustomizationImage(
$data['value']
);
break;
case Product::CUSTOMIZE_TEXTFIELD:
$field['type'] = 'text';
$field['text'] = $data['value'];
break;
default:
$field['type'] = null;
}
$field['label'] = $data['name'];
$field['id_module'] = $data['id_module'];
$presentedCustomization['id_customization'] = $data['id_customization'];
}
$presentedCustomization['fields'][] = $field;
}
$product_customizations[] = $presentedCustomization;
}
}
return $product_customizations;
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = $this->addMyAccountToBreadcrumb();
if (($id_order_return = (int) Tools::getValue('id_order_return')) && Validate::isUnsignedId($id_order_return)) {
$breadcrumb['links'][] = [
'title' => $this->trans('Merchandise returns', [], 'Shop.Theme.Global'),
'url' => $this->context->link->getPageLink('order-follow'),
];
$prefix = Configuration::get('PS_RETURN_PREFIX', $this->context->language->id);
$orderReturn = new OrderReturn($id_order_return);
$orderReturn->id_order_return = $id_order_return;
$orderReturnLazyArray = new OrderReturnLazyArray($prefix, $this->context->link, (array) $orderReturn);
$orderReturnNumber = $orderReturnLazyArray->getReturnNumber();
$breadcrumb['links'][] = [
'title' => $orderReturnNumber,
'url' => '#',
];
}
return $breadcrumb;
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class OrderSlipControllerCore extends FrontController
{
/** @var bool */
public $auth = true;
/** @var string */
public $php_self = 'order-slip';
/** @var string */
public $authRedirection = 'order-slip';
/** @var bool */
public $ssl = true;
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
if (Configuration::isCatalogMode()) {
Tools::redirect('index.php');
}
$this->context->smarty->assign([
'credit_slips' => $this->getTemplateVarCreditSlips(),
]);
parent::initContent();
$this->setTemplate('customer/order-slip');
}
public function getTemplateVarCreditSlips(): array
{
$credit_slips = [];
$orders_slip = OrderSlip::getOrdersSlip((int) $this->context->cookie->id_customer);
foreach ($orders_slip as $order_slip) {
$order = new Order($order_slip['id_order']);
$credit_slips[$order_slip['id_order_slip']] = $order_slip;
$credit_slips[$order_slip['id_order_slip']]['credit_slip_number'] = $this->trans('#%id%', ['%id%' => $order_slip['id_order_slip']], 'Shop.Theme.Customeraccount');
$credit_slips[$order_slip['id_order_slip']]['order_number'] = $this->trans('#%id%', ['%id%' => $order_slip['id_order']], 'Shop.Theme.Customeraccount');
$credit_slips[$order_slip['id_order_slip']]['order_reference'] = $order->reference;
$credit_slips[$order_slip['id_order_slip']]['credit_slip_date'] = Tools::displayDate($order_slip['date_add'], false);
$credit_slips[$order_slip['id_order_slip']]['url'] = $this->context->link->getPageLink('pdf-order-slip', null, null, 'id_order_slip=' . (int) $order_slip['id_order_slip']);
$credit_slips[$order_slip['id_order_slip']]['order_url_details'] = $this->context->link->getPageLink('order-detail', null, null, 'id_order=' . (int) $order_slip['id_order']);
}
return $credit_slips;
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = $this->addMyAccountToBreadcrumb();
$breadcrumb['links'][] = [
'title' => $this->trans('Credit slips', [], 'Shop.Theme.Customeraccount'),
'url' => $this->context->link->getPageLink('order-slip'),
];
return $breadcrumb;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class PageNotFoundControllerCore extends FrontController
{
/** @var string */
public $php_self = 'pagenotfound';
/** @var string */
public $page_name = 'pagenotfound';
/** @var bool */
public $ssl = true;
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
header('HTTP/1.1 404 Not Found');
header('Status: 404 Not Found');
$this->context->cookie->disallowWriting();
parent::initContent();
$this->setTemplate('errors/404');
}
protected function canonicalRedirection(string $canonical_url = ''): void
{
// 404 - no need to redirect to the canonical url
}
protected function sslRedirection(): void
{
// 404 - no need to redirect
}
/**
* Initializes a set of commonly used variables related to the current page, available for use
* in the template. @see FrontController::assignGeneralPurposeVariables for more information.
*
* @return array
*/
public function getTemplateVarPage(): array
{
$page = parent::getTemplateVarPage();
$page['title'] = $this->trans('The page you are looking for was not found.', [], 'Shop.Theme.Global');
return $page;
}
public function displayAjax(): void
{
header('Content-Type: application/json');
echo json_encode($this->trans('The page you are looking for was not found.', [], 'Shop.Theme.Global'));
}
}

View File

@@ -0,0 +1,314 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Core\Security\PasswordPolicyConfiguration;
use PrestaShop\PrestaShop\Core\Util\InternationalizedDomainNameConverter;
class PasswordControllerCore extends FrontController
{
/** @var string */
public $php_self = 'password';
/** @var bool */
public $auth = false;
/** @var bool */
public $ssl = true;
/**
* @var InternationalizedDomainNameConverter
*/
private $IDNConverter;
public function __construct()
{
parent::__construct();
$this->IDNConverter = new InternationalizedDomainNameConverter();
}
/**
* Start forms process.
*
* @see FrontController::postProcess()
*/
public function postProcess(): void
{
$this->setTemplate('customer/password-email');
if (Tools::isSubmit('email')) {
$this->sendRenewPasswordLink();
} elseif (Tools::getValue('token') && ($id_customer = (int) Tools::getValue('id_customer'))) {
$this->changePassword();
} elseif (Tools::getValue('token') || Tools::getValue('id_customer')) {
$this->errors[] = $this->trans('We cannot regenerate your password with the data you\'ve submitted', [], 'Shop.Notifications.Error');
}
}
protected function sendRenewPasswordLink(): void
{
if (!($email = $this->IDNConverter->emailToUtf8(trim(Tools::getValue('email')))) || !Validate::isEmail($email)) {
$this->errors[] = $this->trans('Invalid email address.', [], 'Shop.Notifications.Error');
} else {
$customer = new Customer();
$customer->getByEmail($email);
if (null === $customer->email) {
$customer->email = Tools::getValue('email');
}
if (!Validate::isLoadedObject($customer)) {
$this->success[] = $this->trans(
'If this email address has been registered in our store, you will receive a link to reset your password at %email%.',
['%email%' => $customer->email],
'Shop.Notifications.Success'
);
$this->setTemplate('customer/password-infos');
} elseif (!$customer->active) {
$this->errors[] = $this->trans('You cannot regenerate the password for this account.', [], 'Shop.Notifications.Error');
} elseif ((strtotime($customer->last_passwd_gen . '+' . ($minTime = (int) Configuration::get('PS_PASSWD_TIME_FRONT')) . ' minutes') - time()) > 0) {
$this->errors[] = $this->trans('You can regenerate your password only every %d minute(s)', [(int) $minTime], 'Shop.Notifications.Error');
} else {
if (!$customer->hasRecentResetPasswordToken()) {
$customer->stampResetPasswordToken();
$customer->update();
}
$mailParams = [
'{email}' => $customer->email,
'{lastname}' => $customer->lastname,
'{firstname}' => $customer->firstname,
'{url}' => $this->context->link->getPageLink('password', null, null, 'token=' . $customer->secure_key . '&id_customer=' . (int) $customer->id . '&reset_token=' . $customer->reset_password_token),
];
if (
Mail::Send(
$this->context->language->id,
'password_query',
$this->trans(
'Password query confirmation',
[],
'Emails.Subject'
),
$mailParams,
$customer->email,
$customer->firstname . ' ' . $customer->lastname
)
) {
$this->success[] = $this->trans('If this email address has been registered in our store, you will receive a link to reset your password at %email%.', ['%email%' => $customer->email], 'Shop.Notifications.Success');
$this->setTemplate('customer/password-infos');
} else {
$this->errors[] = $this->trans('An error occurred while sending the email.', [], 'Shop.Notifications.Error');
}
}
}
}
protected function changePassword(): void
{
$token = Tools::getValue('token');
$id_customer = (int) Tools::getValue('id_customer');
$reset_token = Tools::getValue('reset_token');
$email = Db::getInstance()->getValue(
'SELECT `email` FROM ' . _DB_PREFIX_ . 'customer c WHERE c.`secure_key` = \'' . pSQL($token) . '\' AND c.id_customer = ' . $id_customer
);
if ($email) {
$customer = new Customer();
$customer->getByEmail($email);
if (!Validate::isLoadedObject($customer)) {
$this->errors[] = $this->trans('Customer account not found', [], 'Shop.Notifications.Error');
} elseif (!$customer->active) {
$this->errors[] = $this->trans('You cannot regenerate the password for this account.', [], 'Shop.Notifications.Error');
} elseif ($customer->getValidResetPasswordToken() !== $reset_token) {
$this->errors[] = $this->trans('The password change request expired. You should ask for a new one.', [], 'Shop.Notifications.Error');
}
if ($this->errors) {
return;
}
if ($isSubmit = Tools::isSubmit('passwd')) {
// If password is submitted validate pass and confirmation
if (!$passwd = Tools::getValue('passwd')) {
$this->errors[] = $this->trans('The password is missing: please enter your new password.', [], 'Shop.Notifications.Error');
}
if (!$confirmation = Tools::getValue('confirmation')) {
$this->errors[] = $this->trans('The confirmation is empty: please fill in the password confirmation as well', [], 'Shop.Notifications.Error');
}
if ($passwd && $confirmation) {
if ($passwd !== $confirmation) {
$this->errors[] = $this->trans('The confirmation password doesn\'t match.', [], 'Shop.Notifications.Error');
}
if (!Validate::isAcceptablePasswordLength($passwd)) {
$this->errors[] = $this->trans('The password is not in a valid format.', [], 'Shop.Notifications.Error');
}
}
if (Validate::isAcceptablePasswordLength($passwd) === false) {
$this->errors[] = $this->translator->trans(
'Password must be between %d and %d characters long',
[
Configuration::get(PasswordPolicyConfiguration::CONFIGURATION_MINIMUM_LENGTH),
Configuration::get(PasswordPolicyConfiguration::CONFIGURATION_MAXIMUM_LENGTH),
],
'Shop.Notifications.Error'
);
}
if (Validate::isAcceptablePasswordScore($passwd) === false) {
$wordingsForScore = [
$this->translator->trans('Very weak', [], 'Shop.Theme.Global'),
$this->translator->trans('Weak', [], 'Shop.Theme.Global'),
$this->translator->trans('Average', [], 'Shop.Theme.Global'),
$this->translator->trans('Strong', [], 'Shop.Theme.Global'),
$this->translator->trans('Very strong', [], 'Shop.Theme.Global'),
];
$this->errors[] = $this->translator->trans(
'The minimum score must be: %s',
[
$wordingsForScore[(int) Configuration::get(PasswordPolicyConfiguration::CONFIGURATION_MINIMUM_SCORE)],
],
'Shop.Notifications.Error'
);
}
}
if (!$isSubmit || $this->errors) {
// If password is NOT submitted OR there are errors, shows the form (and errors)
$this->context->smarty->assign([
'customer_email' => $customer->email,
'customer_token' => $token,
'id_customer' => $id_customer,
'reset_token' => $reset_token,
]);
$this->setTemplate('customer/password-new');
} else {
// Both password fields posted. Check if all is right and store new password properly.
if (!$reset_token || (strtotime($customer->last_passwd_gen . '+' . (int) Configuration::get('PS_PASSWD_TIME_FRONT') . ' minutes') - time()) > 0) {
Tools::redirect($this->context->link->getPageLink(
'authentication',
null,
null,
['error_regen_pwd' => 1]
));
} else {
$customer->passwd = $this->get('hashing')->hash($password = Tools::getValue('passwd'), _COOKIE_KEY_);
$customer->last_passwd_gen = date('Y-m-d H:i:s', time());
if ($customer->update()) {
Hook::exec('actionPasswordRenew', ['customer' => $customer, 'password' => $password]);
$customer->removeResetPasswordToken();
$customer->update();
$mail_params = [
'{email}' => $customer->email,
'{lastname}' => $customer->lastname,
'{firstname}' => $customer->firstname,
];
if (
Mail::Send(
$this->context->language->id,
'password',
$this->trans(
'Your new password',
[],
'Emails.Subject'
),
$mail_params,
$customer->email,
$customer->firstname . ' ' . $customer->lastname
)
) {
$this->context->smarty->assign([
'customer_email' => $customer->email,
]);
$this->success[] = $this->trans('Your password has been successfully reset and a confirmation has been sent to your email address: %s', [$customer->email], 'Shop.Notifications.Success');
$this->context->updateCustomer($customer);
$this->redirectWithNotifications($this->context->link->getPageLink('my-account'));
} else {
$this->errors[] = $this->trans('An error occurred while sending the email.', [], 'Shop.Notifications.Error');
}
} else {
$this->errors[] = $this->trans('An error occurred with your account, which prevents us from updating the new password. Please report this issue using the contact form.', [], 'Shop.Notifications.Error');
}
}
}
} else {
$this->errors[] = $this->trans('We cannot regenerate your password with the data you\'ve submitted', [], 'Shop.Notifications.Error');
}
}
/**
* @return void
*/
public function display(): void
{
$this->context->smarty->assign(
[
'layout' => $this->getLayout(),
'stylesheets' => $this->getStylesheets(),
'javascript' => $this->getJavascript(),
'js_custom_vars' => Media::getJsDef(),
'errors' => $this->getErrors(),
'successes' => $this->getSuccesses(),
]
);
$this->smartyOutputContent($this->template);
}
/**
* @return array
*/
protected function getErrors(): array
{
$notifications = $this->prepareNotifications();
$errors = [];
if (array_key_exists('error', $notifications)) {
$errors = $notifications['error'];
}
return $errors;
}
/**
* @return array
*/
protected function getSuccesses(): array
{
$notifications = $this->prepareNotifications();
$successes = [];
if (array_key_exists('success', $notifications)) {
$successes = $notifications['success'];
}
return $successes;
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = [
'title' => $this->trans('Reset your password', [], 'Shop.Theme.Customeraccount'),
'url' => $this->context->link->getPageLink('password'),
];
return $breadcrumb;
}
/**
* {@inheritdoc}
*/
public function getCanonicalURL(): string
{
return $this->context->link->getPageLink('password');
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class PdfInvoiceControllerCore extends FrontController
{
/** @var string */
public $php_self = 'pdf-invoice';
/** @var bool */
protected $display_header = false;
/** @var bool */
protected $display_footer = false;
/** @var bool */
public $content_only = true;
/** @var string */
protected $template = '';
public $filename;
/** @var Order */
public $order;
public function postProcess(): void
{
// If the customer is not logged in AND no secure key was passed
if (!$this->context->customer->isLogged() && !Tools::getValue('secure_key')) {
Tools::redirect($this->context->link->getPageLink(
'authentication',
null,
null,
['back' => 'pdf-invoice']
));
}
// If built-in invoicing is disabled
if (!(int) Configuration::get('PS_INVOICE')) {
die($this->trans('Invoices are disabled in this shop.', [], 'Shop.Notifications.Error'));
}
$id_order = (int) Tools::getValue('id_order');
if (Validate::isUnsignedId($id_order)) {
$order = new Order((int) $id_order);
}
// If the order doesn't exist
if (!isset($order) || !Validate::isLoadedObject($order)) {
die($this->trans('The invoice was not found.', [], 'Shop.Notifications.Error'));
}
// Check if the user is not trying to download an invoice of an order of different customer
// Either the ID of the customer in context must match the customer in order OR a secure_key matching the one on the order must be provided
if (Tools::isSubmit('secure_key') && $order->secure_key != Tools::getValue('secure_key')) {
die($this->trans('The invoice was not found.', [], 'Shop.Notifications.Error'));
}
if (!Tools::isSubmit('secure_key') && (!isset($this->context->customer->id) || $order->id_customer != $this->context->customer->id)) {
die($this->trans('The invoice was not found.', [], 'Shop.Notifications.Error'));
}
if (!OrderState::invoiceAvailable($order->getCurrentState()) && !$order->invoice_number) {
die($this->trans('No invoice is available.', [], 'Shop.Notifications.Error'));
}
$this->order = $order;
}
/**
* @return void
*
* @throws PrestaShopException
*/
public function display(): void
{
$order_invoice_list = $this->order->getInvoicesCollection();
Hook::exec('actionPDFInvoiceRender', ['order_invoice_list' => $order_invoice_list]);
$pdf = new PDF($order_invoice_list, PDF::TEMPLATE_INVOICE, $this->context->smarty);
$pdf->render();
}
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShopBundle\Security\Admin\LegacyAdminTokenValidator;
class PdfOrderReturnControllerCore extends FrontController
{
/** @var string */
public $php_self = 'pdf-order-return';
/** @var bool */
protected $display_header = false;
/** @var bool */
protected $display_footer = false;
/**
* @var OrderReturn|null
*/
public $orderReturn;
public function postProcess(): void
{
$adminToken = Tools::getValue('adtoken');
if (!empty($adminToken)) {
$adminTokenValidator = $this->getContainer()->get(LegacyAdminTokenValidator::class);
$from_admin = $adminTokenValidator->isTokenValid((int) Tools::getValue('id_employee'), $adminToken);
} else {
$from_admin = false;
}
if (!$from_admin && !$this->context->customer->isLogged()) {
Tools::redirect($this->context->link->getPageLink(
'authentication',
null,
null,
['back' => 'order-follow']
));
}
if (Tools::getValue('id_order_return') && Validate::isUnsignedId(Tools::getValue('id_order_return'))) {
$this->orderReturn = new OrderReturn(Tools::getValue('id_order_return'));
}
if (!isset($this->orderReturn) || !Validate::isLoadedObject($this->orderReturn)) {
die($this->trans('Order return not found.', [], 'Shop.Notifications.Error'));
} elseif (!$from_admin && $this->orderReturn->id_customer != $this->context->customer->id) {
die($this->trans('Order return not found.', [], 'Shop.Notifications.Error'));
} elseif ($this->orderReturn->state < 2) {
die($this->trans('Order return not confirmed.', [], 'Shop.Notifications.Error'));
}
}
/**
* @return void
*
* @throws PrestaShopException
*/
public function display(): void
{
$pdf = new PDF($this->orderReturn, PDF::TEMPLATE_ORDER_RETURN, $this->context->smarty);
$pdf->render();
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class PdfOrderSlipControllerCore extends FrontController
{
/** @var string */
public $php_self = 'pdf-order-slip';
/** @var bool */
protected $display_header = false;
/** @var bool */
protected $display_footer = false;
protected $order_slip;
public function postProcess(): void
{
if (!$this->context->customer->isLogged()) {
Tools::redirect($this->context->link->getPageLink(
'authentication',
null,
null,
['back' => 'order-follow']
));
}
if (isset($_GET['id_order_slip']) && Validate::isUnsignedId($_GET['id_order_slip'])) {
$this->order_slip = new OrderSlip($_GET['id_order_slip']);
}
if (!isset($this->order_slip) || !Validate::isLoadedObject($this->order_slip)) {
die($this->trans('Order return not found.', [], 'Shop.Notifications.Error'));
} elseif ($this->order_slip->id_customer != $this->context->customer->id) {
die($this->trans('Order return not found.', [], 'Shop.Notifications.Error'));
}
}
/**
* @return void
*
* @throws PrestaShopException
*/
public function display(): void
{
$pdf = new PDF($this->order_slip, PDF::TEMPLATE_ORDER_SLIP, $this->context->smarty);
$pdf->render();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,102 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class RegistrationControllerCore extends FrontController
{
/** @var bool */
public $ssl = true;
/** @var string */
public $php_self = 'registration';
/** @var bool */
public $auth = false;
/**
* Check if the controller is available for the current user/visitor.
*
* @see Controller::checkAccess()
*
* @return bool
*/
public function checkAccess(): bool
{
// If the customer is already logged and he got here by 'accident', we will redirect him away
if ($this->context->customer->isLogged() && !$this->ajax) {
$this->redirect_after = $this->authRedirection ? urlencode($this->authRedirection) : 'my-account';
$this->redirect();
}
return parent::checkAccess();
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
$register_form = $this
->makeCustomerForm()
->setGuestAllowed(false)
->fillWith(Tools::getAllValues());
// If registration form was submitted
if (Tools::isSubmit('submitCreate')) {
$hookResult = array_reduce(
Hook::exec('actionSubmitAccountBefore', [], null, true),
function ($carry, $item) {
return $carry && $item;
},
true
);
// If no problem occured in the hook, let's get the user redirected
if ($hookResult && $register_form->submit() && !$this->ajax) {
// First option - redirect the customer to desired URL specified in 'back' parameter
// Before that, we need to check if 'back' is legit URL that is on OUR domain, with the right protocol
$back = rawurldecode(Tools::getValue('back'));
if (Tools::urlBelongsToShop($back)) {
$this->redirectWithNotifications($back);
}
// Second option - we will redirect him to authRedirection if set
if ($this->authRedirection) {
$this->redirectWithNotifications($this->authRedirection);
}
// Third option - we will redirect him to home URL
$this->redirectWithNotifications(__PS_BASE_URI__);
}
}
$this->context->smarty->assign([
'register_form' => $register_form->getProxy(),
'hook_create_account_top' => Hook::exec('displayCustomerAccountFormTop'),
]);
$this->setTemplate('customer/registration');
parent::initContent();
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = [
'title' => $this->trans('Create an account', [], 'Shop.Theme.Customeraccount'),
'url' => $this->context->link->getPageLink('registration'),
];
return $breadcrumb;
}
/**
* {@inheritdoc}
*/
public function getCanonicalURL(): string
{
return $this->context->link->getPageLink('registration');
}
}

View File

@@ -0,0 +1,230 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class SitemapControllerCore extends FrontController
{
/** @var string */
public $php_self = 'sitemap';
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
$sitemapUrls = [
'our_offers' => [
'name' => $this->trans('Our Offers', [], 'Shop.Theme.Global'),
'links' => $this->getOffersLinks(),
],
'categories' => [
'name' => $this->trans('Categories', [], 'Shop.Theme.Catalog'),
'links' => $this->getCategoriesLinks(),
],
'your_account' => [
'name' => $this->trans('Your account', [], 'Shop.Theme.Customeraccount'),
'links' => $this->getUserAccountLinks(),
],
'pages' => [
'name' => $this->trans('Pages', [], 'Shop.Theme.Catalog'),
'links' => $this->getPagesLinks(),
],
];
/*
* Allows modules to add own urls (even whole new groups) to frontend sitemap.
* For example landing pages, blog posts and others.
*/
Hook::exec(
'actionModifyFrontendSitemap',
['urls' => &$sitemapUrls]
);
/*
* Backward compatibility with older themes.
* This should be removed as soon as possible, because $pages variable is overwriting
* our global template variable assigned in FrontController.
*/
$this->context->smarty->assign(
[
'our_offers' => !empty($sitemapUrls['our_offers']['name']) ? $sitemapUrls['our_offers']['name'] : '',
'categories' => !empty($sitemapUrls['categories']['name']) ? $sitemapUrls['categories']['name'] : '',
'your_account' => !empty($sitemapUrls['your_account']['name']) ? $sitemapUrls['your_account']['name'] : '',
'pages' => !empty($sitemapUrls['pages']['name']) ? $sitemapUrls['pages']['name'] : '',
'links' => [
'offers' => !empty($sitemapUrls['our_offers']['links']) ? $sitemapUrls['our_offers']['links'] : [],
'pages' => !empty($sitemapUrls['pages']['links']) ? $sitemapUrls['pages']['links'] : [],
'user_account' => !empty($sitemapUrls['your_account']['links']) ? $sitemapUrls['your_account']['links'] : [],
'categories' => !empty($sitemapUrls['categories']['links']) ? $sitemapUrls['categories']['links'] : [],
],
]
);
$this->context->smarty->assign('sitemapUrls', $sitemapUrls);
parent::initContent();
$this->setTemplate('cms/sitemap');
}
public function getCategoriesLinks(): array
{
return [Category::getRootCategory()->recurseLiteCategTree(0, 0, null, null, 'sitemap')];
}
/**
* @return array
*/
protected function getPagesLinks(): array
{
$cms = CMSCategory::getRecurseCategory($this->context->language->id, 1, 1, 1);
$links = $this->getCmsTree($cms);
// We hide stores page, if there is no page configured
if (Store::atLeastOneStoreExists()) {
$links[] = [
'id' => 'stores-page',
'label' => $this->trans('Our stores', [], 'Shop.Theme.Global'),
'url' => $this->context->link->getPageLink('stores'),
];
}
$links[] = [
'id' => 'contact-page',
'label' => $this->trans('Contact us', [], 'Shop.Theme.Global'),
'url' => $this->context->link->getPageLink('contact'),
];
$links[] = [
'id' => 'sitemap-page',
'label' => $this->trans('Sitemap', [], 'Shop.Theme.Global'),
'url' => $this->context->link->getPageLink('sitemap'),
];
return $links;
}
/**
* @return array
*/
protected function getCmsTree($cms): array
{
$links = [];
foreach ($cms['cms'] as $p) {
$links[] = [
'id' => 'cms-page-' . $p['id_cms'],
'label' => $p['meta_title'],
'url' => $p['link'],
];
}
if (isset($cms['children'])) {
foreach ($cms['children'] as $c) {
$links[] = [
'id' => 'cms-category-' . $c['id_cms_category'],
'label' => $c['name'],
'url' => $c['link'],
'children' => $this->getCmsTree($c),
];
}
}
return $links;
}
/**
* @return array
*/
protected function getUserAccountLinks(): array
{
$links = [];
$links[] = [
'id' => 'login-page',
'label' => $this->trans('Log in', [], 'Shop.Theme.Global'),
'url' => $this->context->link->getPageLink('authentication'),
];
$links[] = [
'id' => 'register-page',
'label' => $this->trans('Create new account', [], 'Shop.Theme.Global'),
'url' => $this->context->link->getPageLink('registration'),
];
return $links;
}
/**
* @return array
*/
protected function getOffersLinks(): array
{
$links = [
[
'id' => 'new-product-page',
'label' => $this->trans('New products', [], 'Shop.Theme.Catalog'),
'url' => $this->context->link->getPageLink('new-products'),
],
];
if (!Configuration::isCatalogMode()) {
if (Configuration::get('PS_DISPLAY_BEST_SELLERS')) {
$links[] = [
'id' => 'best-sales-page',
'label' => $this->trans('Best sellers', [], 'Shop.Theme.Catalog'),
'url' => $this->context->link->getPageLink('best-sales'),
];
}
$links[] = [
'id' => 'prices-drop-page',
'label' => $this->trans('Price drop', [], 'Shop.Theme.Catalog'),
'url' => $this->context->link->getPageLink('prices-drop'),
];
}
if (Configuration::get('PS_DISPLAY_MANUFACTURERS')) {
$manufacturers = Manufacturer::getLiteManufacturersList($this->context->language->id, 'sitemap');
$links[] = [
'id' => 'manufacturer-page',
'label' => $this->trans('Brands', [], 'Shop.Theme.Catalog'),
'url' => $this->context->link->getPageLink('manufacturer'),
'children' => $manufacturers,
];
}
if (Configuration::get('PS_DISPLAY_SUPPLIERS')) {
$suppliers = Supplier::getLiteSuppliersList($this->context->language->id, 'sitemap');
$links[] = [
'id' => 'supplier-page',
'label' => $this->trans('Suppliers', [], 'Shop.Theme.Catalog'),
'url' => $this->context->link->getPageLink('supplier'),
'children' => $suppliers,
];
}
return $links;
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = [
'title' => $this->trans('Sitemap', [], 'Shop.Theme.Global'),
'url' => $this->context->link->getPageLink('sitemap'),
];
return $breadcrumb;
}
/**
* {@inheritdoc}
*/
public function getCanonicalURL(): string
{
return $this->context->link->getPageLink('sitemap');
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class StatisticsControllerCore extends FrontController
{
/** @var bool */
public $display_header = false;
/** @var bool */
public $display_footer = false;
protected $param_token;
public function postProcess(): void
{
$this->param_token = Tools::getValue('token');
if (!$this->param_token) {
die;
}
if ($_POST['type'] == 'navinfo') {
$this->processNavigationStats();
} elseif ($_POST['type'] == 'pagetime') {
$this->processPageTime();
} else {
exit;
}
}
/**
* Log statistics on navigation (resolution, plugins, etc.).
*/
protected function processNavigationStats(): void
{
$id_guest = (int) Tools::getValue('id_guest');
if (sha1($id_guest . _COOKIE_KEY_) != $this->param_token) {
die;
}
$guest = new Guest((int) substr($_POST['id_guest'], 0, 10));
$guest->javascript = true;
$guest->screen_resolution_x = (int) substr($_POST['screen_resolution_x'], 0, 5);
$guest->screen_resolution_y = (int) substr($_POST['screen_resolution_y'], 0, 5);
$guest->screen_color = (int) substr($_POST['screen_color'], 0, 3);
$guest->sun_java = (int) substr($_POST['sun_java'], 0, 1);
$guest->adobe_flash = (int) substr($_POST['adobe_flash'], 0, 1);
$guest->adobe_director = (int) substr($_POST['adobe_director'], 0, 1);
$guest->apple_quicktime = (int) substr($_POST['apple_quicktime'], 0, 1);
$guest->real_player = (int) substr($_POST['real_player'], 0, 1);
$guest->windows_media = (int) substr($_POST['windows_media'], 0, 1);
$guest->update();
}
/**
* Log statistics on time spend on pages.
*/
protected function processPageTime(): void
{
$id_connection = (int) Tools::getValue('id_connections');
$time = (int) Tools::getValue('time');
$time_start = Tools::getValue('time_start');
$id_page = (int) Tools::getValue('id_page');
if (sha1($id_connection . $id_page . $time_start . _COOKIE_KEY_) != $this->param_token) {
die;
}
if ($time <= 0) {
die;
}
Connection::setPageTime($id_connection, $id_page, substr($time_start, 0, 19), $time);
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\Presenter\Store\StorePresenter;
class StoresControllerCore extends FrontController
{
/** @var string */
public $php_self = 'stores';
/** @var StorePresenter */
protected $storePresenter;
/**
* Initialize stores controller.
*
* @see FrontController::init()
*/
public function init(): void
{
// Initialize presenter, we will use it for all cases
$this->storePresenter = new StorePresenter(
$this->context->link,
$this->context->getTranslator()
);
parent::init();
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
$distance_unit = Configuration::get('PS_DISTANCE_UNIT');
if (!in_array($distance_unit, ['km', 'mi'])) {
$distance_unit = 'km';
}
// Load stores and present them
$stores = $this->getTemplateVarStores();
// If no stores are configured, we hide this page
if (!empty($stores)) {
$this->context->smarty->assign([
'mediumSize' => Image::getSize(ImageType::getFormattedName('medium')),
'searchUrl' => $this->context->link->getPageLink('stores'),
'distance_unit' => $distance_unit,
'stores' => $stores,
]);
parent::initContent();
$this->setTemplate('cms/stores');
} else {
$this->redirect_after = '404';
$this->redirect();
}
}
public function getTemplateVarStores(): array
{
$stores = Store::getStores($this->context->language->id);
foreach ($stores as &$store) {
$store = $this->storePresenter->present(
$store,
$this->context->language
);
}
return $stores;
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = [
'title' => $this->trans('Our stores', [], 'Shop.Theme.Global'),
'url' => $this->context->link->getPageLink('stores'),
];
return $breadcrumb;
}
/**
* {@inheritdoc}
*/
public function getCanonicalURL(): string
{
return $this->context->link->getPageLink('stores');
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
declare(strict_types=1);
class UploadControllerCore extends GetFileController
{
private $filename;
/**
* Initialize the controller.
*
* @see FrontController::init()
*/
public function init(): void
{
FrontController::init();
if (Tools::getValue('file') !== null) {
$this->filename = pSQL(Tools::getValue('file'));
}
if (!file_exists($this->getPath()) || (!$this->isCustomization() && !$this->isEmployee())) {
$this->redirect_after = '404';
$this->redirect();
}
}
private function isEmployee(): bool
{
return !empty((new Cookie('psAdmin'))->id_employee);
}
private function isCustomization(): bool
{
if ($this->filename === null || !($this->context->cart instanceof Cart)) {
return false;
}
$isCustomization = Db::getInstance()->getValue('SELECT 1
FROM ' . _DB_PREFIX_ . 'cart c
INNER JOIN ' . _DB_PREFIX_ . 'customization cu ON c.id_cart = cu.id_cart
INNER JOIN ' . _DB_PREFIX_ . 'customized_data cd ON cd.id_customization = cu.id_customization
LEFT JOIN ' . _DB_PREFIX_ . 'orders o ON c.id_cart = o.id_cart
WHERE (c.id_customer = ' . (int) $this->context->cart->id_customer . '
AND c.id_guest = ' . (int) $this->context->cart->id_guest . '
OR o.reference = "' . pSQL(Tools::getValue('reference')) . '")
AND cd.type = ' . Product::CUSTOMIZE_FILE . '
AND (cd.value = "' . $this->filename . '" OR CONCAT(cd.value, "_small") = "' . $this->filename . '")');
return (bool) $isCustomization;
}
public function postProcess(): void
{
$this->sendFile($this->getPath(), $this->filename, false);
}
private function getPath(): string
{
return _PS_UPLOAD_DIR_ . basename($this->filename);
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
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;

View File

@@ -0,0 +1,97 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\BestSales\BestSalesProductSearchProvider;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchQuery;
use PrestaShop\PrestaShop\Core\Product\Search\SortOrder;
class BestSalesControllerCore extends ProductListingFrontController
{
/** @var string */
public $php_self = 'best-sales';
/**
* Returns canonical URL for best-sales page
*
* @return string
*/
public function getCanonicalURL(): string
{
return $this->buildPaginatedUrl($this->context->link->getPageLink('best-sales'));
}
/**
* Initializes controller.
*
* @see FrontController::init()
*
* @throws PrestaShopException
*/
public function init(): void
{
if (Configuration::get('PS_DISPLAY_BEST_SELLERS')) {
parent::init();
} else {
Tools::redirect('pagenotfound');
}
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
parent::initContent();
$this->doProductSearch('catalog/listing/best-sales', ['entity' => 'best-sales']);
}
/**
* Gets the product search query for the controller. This is a set of information that
* a filtering module or the default provider will use to fetch our products.
*
* @return ProductSearchQuery
*/
protected function getProductSearchQuery(): ProductSearchQuery
{
$query = new ProductSearchQuery();
$query
->setQueryType('best-sales')
->setSortOrder(new SortOrder('product', 'sales', 'desc'));
return $query;
}
/**
* Default product search provider used if no filtering module stood up for the job
*
* @return BestSalesProductSearchProvider
*/
protected function getDefaultProductSearchProvider(): BestSalesProductSearchProvider
{
return new BestSalesProductSearchProvider(
$this->getTranslator()
);
}
public function getListingLabel(): string
{
return $this->getTranslator()->trans('Best sellers', [], 'Shop.Theme.Catalog');
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = [
'title' => $this->trans('Best sellers', [], 'Shop.Theme.Catalog'),
'url' => $this->context->link->getPageLink('best-sales'),
];
return $breadcrumb;
}
}

View File

@@ -0,0 +1,382 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\Category\CategoryProductSearchProvider;
use PrestaShop\PrestaShop\Adapter\Image\ImageRetriever;
use PrestaShop\PrestaShop\Adapter\Presenter\Category\CategoryLazyArray;
use PrestaShop\PrestaShop\Adapter\Presenter\Category\CategoryPresenter;
use PrestaShop\PrestaShop\Core\Domain\Category\ValueObject\RedirectType;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchQuery;
use PrestaShop\PrestaShop\Core\Product\Search\SortOrder;
class CategoryControllerCore extends ProductListingFrontController
{
/** @var string Internal controller name */
public $php_self = 'category';
/** @var bool If set to false, customer cannot view the current category. */
public $customer_access = true;
/** @var bool */
protected $notFound = false;
/**
* @var Category
*/
protected $category;
/** @var CategoryPresenter */
protected $categoryPresenter;
public function canonicalRedirection(string $canonicalURL = ''): void
{
if (Validate::isLoadedObject($this->category)) {
parent::canonicalRedirection($this->context->link->getCategoryLink($this->category));
}
}
/**
* Returns canonical URL for current category
*
* @return string
*/
public function getCanonicalURL(): string
{
if (!Validate::isLoadedObject($this->category)) {
return '';
}
return $this->buildPaginatedUrl($this->context->link->getCategoryLink($this->category));
}
/**
* Initializes category controller.
*
* @see FrontController::init()
*
* @throws PrestaShopException
*/
public function init(): void
{
// Get proper IDs
$id_category = (int) Tools::getValue('id_category');
// Try to load category object
$this->category = new Category($id_category, $this->context->language->id);
/*
* Call of parent::init() must be here, after initializing the category. Inside FrontController class
* there is a call to canonical redirection logic, that needs proper category data for it to work.
* If you move this to the top of init() method, the redirection will stop working.
*/
parent::init();
// Otherwise immediately show 404
if (!Validate::isLoadedObject($this->category)) {
header('HTTP/1.1 404 Not Found');
header('Status: 404 Not Found');
$this->errors[] = $this->trans('This category is no longer available.', [], 'Shop.Notifications.Error');
$this->setTemplate('errors/404');
$this->notFound = true;
return;
}
// If this category is not active or not related to current shop in multistore context,
// we treat it as not available. We will either redirect away or show error, depending
// on settings of the category.
if (!$this->category->active || !$this->category->existsInShop($this->context->shop->id)) {
// If category should redirect and we don't know where, we take the closest parent
if (!$this->category->id_type_redirected && in_array($this->category->redirect_type, [RedirectType::TYPE_PERMANENT, RedirectType::TYPE_TEMPORARY])) {
$this->category->id_type_redirected = $this->getCategoryToRedirectTo();
}
// Now, we do as configured in "Redirection when not displayed" field on the category
switch ($this->category->redirect_type) {
case RedirectType::TYPE_PERMANENT:
header('HTTP/1.1 301 Moved Permanently');
header('Location: ' . $this->context->link->getCategoryLink($this->category->id_type_redirected));
exit;
case RedirectType::TYPE_TEMPORARY:
header('HTTP/1.1 302 Moved Temporarily');
header('Cache-Control: no-cache');
header('Location: ' . $this->context->link->getCategoryLink($this->category->id_type_redirected));
exit;
case RedirectType::TYPE_GONE:
header('HTTP/1.1 410 Gone');
header('Status: 410 Gone');
$this->errors[] = $this->trans('This category is no longer available.', [], 'Shop.Notifications.Error');
$this->setTemplate('errors/410');
$this->notFound = true;
break;
case RedirectType::TYPE_NOT_FOUND:
default:
header('HTTP/1.1 404 Not Found');
header('Status: 404 Not Found');
$this->errors[] = $this->trans('This category is no longer available.', [], 'Shop.Notifications.Error');
$this->setTemplate('errors/404');
$this->notFound = true;
break;
}
return;
}
// And one last check, we need to validate if current customer is a member
// of at least one group allowed to view this category.
if (!$this->category->checkAccess($this->context->customer->id)) {
header('HTTP/1.1 403 Forbidden');
header('Status: 403 Forbidden');
$this->errors[] = $this->trans('You do not have access to this category.', [], 'Shop.Notifications.Error');
$this->setTemplate('errors/forbidden');
return;
}
// Initialize presenter, we will use it for all cases
$this->categoryPresenter = new CategoryPresenter($this->context->link);
$this->context->smarty->assign([
'category' => $this->getTemplateVarCategory(),
'subcategories' => $this->getTemplateVarSubCategories(),
]);
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
parent::initContent();
if (
Validate::isLoadedObject($this->category)
&& $this->category->active
&& $this->category->checkAccess($this->context->customer->id)
&& $this->category->existsInShop($this->context->shop->id)
) {
$this->doProductSearch(
'catalog/listing/category',
[
'entity' => 'category',
'id' => $this->category->id,
]
);
}
}
/**
* overrides layout if category is not visible.
*
* @return bool|string
*/
public function getLayout(): bool|string
{
if (!$this->category->checkAccess($this->context->customer->id) || $this->notFound) {
return $this->context->shop->theme->getLayoutRelativePathForPage('error');
}
return parent::getLayout();
}
protected function getAjaxProductSearchVariables(): array
{
// Basic data with rendered products, facets, active filters etc.
$data = parent::getAjaxProductSearchVariables();
// Extra data for category pages, so we can dynamically update also these parts
$rendered_category_header = $this->render('catalog/_partials/category-header', ['listing' => $data]);
$data['rendered_products_header'] = $rendered_category_header;
$rendered_category_footer = $this->render('catalog/_partials/category-footer', ['listing' => $data]);
$data['rendered_products_footer'] = $rendered_category_footer;
return $data;
}
/**
* Gets the product search query for the controller. This is a set of information that
* a filtering module or the default provider will use to fetch our products.
*
* @return ProductSearchQuery
*
* @throws PrestaShop\PrestaShop\Core\Product\Search\Exception\InvalidSortOrderDirectionException
*/
protected function getProductSearchQuery(): ProductSearchQuery
{
$query = new ProductSearchQuery();
$query
->setQueryType('category')
->setIdCategory($this->category->id)
->setSortOrder(new SortOrder('product', Tools::getProductsOrder('by'), Tools::getProductsOrder('way')));
return $query;
}
/**
* Default product search provider used if no filtering module stood up for the job
*
* @return CategoryProductSearchProvider
*/
protected function getDefaultProductSearchProvider(): CategoryProductSearchProvider
{
return new CategoryProductSearchProvider(
$this->getTranslator(),
$this->category
);
}
protected function getTemplateVarCategory(): CategoryLazyArray
{
$categoryVar = $this->categoryPresenter->present(
$this->category,
$this->context->language
);
$filteredCategory = Hook::exec(
'filterCategoryContent',
['object' => $categoryVar],
$id_module = null,
$array_return = false,
$check_exceptions = true,
$use_push = false,
$id_shop = null,
$chain = true
);
if (!empty($filteredCategory['object'])) {
$categoryVar = $filteredCategory['object'];
}
return $categoryVar;
}
protected function getTemplateVarSubCategories(): array
{
$subcategories = $this->category->getSubCategories($this->context->language->id);
foreach ($subcategories as &$subcategory) {
$subcategory = $this->categoryPresenter->present(
$subcategory,
$this->context->language
);
}
return $subcategories;
}
/**
* @deprecated since 9.0.0 and will be removed in 10.0.0
*/
protected function getImage(Category $object, int $id_image)
{
$retriever = new ImageRetriever(
$this->context->link
);
return $retriever->getImage($object, $id_image);
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
foreach ($this->category->getAllParents() as $category) {
/** @var Category $category */
if ($category->id_parent != 0 && !$category->is_root_category && $category->active) {
$breadcrumb['links'][] = [
'title' => $category->name,
'url' => $this->context->link->getCategoryLink($category),
];
}
}
if ($this->category->id_parent != 0 && !$this->category->is_root_category && $this->category->active) {
$breadcrumb['links'][] = [
'title' => $this->category->name,
'url' => $this->context->link->getCategoryLink($this->category),
];
}
return $breadcrumb;
}
/**
* @return Category
*/
public function getCategory(): Category
{
return $this->category;
}
/**
* Initializes a set of commonly used variables related to the current page, available for use
* in the template. @see FrontController::assignGeneralPurposeVariables for more information.
*
* @return array
*/
public function getTemplateVarPage(): array
{
$page = parent::getTemplateVarPage();
if ($this->notFound) {
$page['page_name'] = 'pagenotfound';
$page['body_classes']['pagenotfound'] = true;
$page['title'] = $this->trans('The page you are looking for was not found.', [], 'Shop.Theme.Global');
} else {
$page['body_classes']['category-id-' . $this->category->id] = true;
$page['body_classes']['category-' . $this->category->name] = true;
$page['body_classes']['category-id-parent-' . $this->category->id_parent] = true;
$page['body_classes']['category-depth-level-' . $this->category->level_depth] = true;
}
return $page;
}
public function getListingLabel(): string
{
if (!Validate::isLoadedObject($this->category)) {
$this->category = new Category(
(int) Tools::getValue('id_category'),
$this->context->language->id
);
}
return $this->trans(
'Category: %category_name%',
['%category_name%' => $this->category->name],
'Shop.Theme.Catalog'
);
}
/**
* Returns a category that we will redirect into, in case we need 301/302 redirect.
* We will try to get the closest active parent of the current category.
*
* @return int category ID
*/
private function getCategoryToRedirectTo(): int
{
$categoryToRedirectTo = null;
foreach ($this->category->getParentsCategories() as $category) {
/*
* Or new favourite category is a one:
* that is not the current one
* that is active
* and that is deeper than the last one
*/
if ($category['id_category'] != $this->category->id
&& $category['active'] == 1
&& $category['level_depth'] > $categoryToRedirectTo['level_depth']
) {
$categoryToRedirectTo = $category;
}
}
return $categoryToRedirectTo['id_category'];
}
}

View File

@@ -0,0 +1,263 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\Manufacturer\ManufacturerProductSearchProvider;
use PrestaShop\PrestaShop\Adapter\Presenter\Manufacturer\ManufacturerPresenter;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchQuery;
use PrestaShop\PrestaShop\Core\Product\Search\SortOrder;
class ManufacturerControllerCore extends ProductListingFrontController
{
/** @var string */
public $php_self = 'manufacturer';
/** @var Manufacturer|null */
protected $manufacturer;
protected $label;
/** @var ManufacturerPresenter */
protected $manufacturerPresenter;
public function canonicalRedirection(string $canonicalURL = ''): void
{
if (Validate::isLoadedObject($this->manufacturer)) {
parent::canonicalRedirection($this->context->link->getManufacturerLink($this->manufacturer));
} elseif ($canonicalURL) {
parent::canonicalRedirection($canonicalURL);
}
}
/**
* Returns canonical URL for current manufacturer or a manufacturer list
*
* @return string
*/
public function getCanonicalURL(): string
{
if (Validate::isLoadedObject($this->manufacturer)) {
return $this->buildPaginatedUrl($this->context->link->getManufacturerLink($this->manufacturer));
}
return $this->context->link->getPageLink('manufacturer');
}
/**
* Initialize manufaturer controller.
*
* @see FrontController::init()
*/
public function init(): void
{
if ($id_manufacturer = Tools::getValue('id_manufacturer')) {
$this->manufacturer = new Manufacturer((int) $id_manufacturer, $this->context->language->id);
if (!Validate::isLoadedObject($this->manufacturer) || !$this->manufacturer->active || !$this->manufacturer->isAssociatedToShop()) {
$this->redirect_after = '404';
$this->redirect();
} else {
$this->canonicalRedirection();
}
}
// Initialize presenter, we will use it for all cases
$this->manufacturerPresenter = new ManufacturerPresenter($this->context->link);
parent::init();
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
if (Configuration::get('PS_DISPLAY_MANUFACTURERS')) {
parent::initContent();
if (Validate::isLoadedObject($this->manufacturer) && $this->manufacturer->active && $this->manufacturer->isAssociatedToShop()) {
$this->assignManufacturer();
$this->label = $this->trans(
'List of products by brand %brand_name%',
[
'%brand_name%' => $this->manufacturer->name,
],
'Shop.Theme.Catalog'
);
$this->doProductSearch(
'catalog/listing/manufacturer',
['entity' => 'manufacturer', 'id' => $this->manufacturer->id]
);
} else {
$this->assignAll();
$this->label = $this->trans(
'List of all brands',
[],
'Shop.Theme.Catalog'
);
$this->setTemplate('catalog/manufacturers', ['entity' => 'manufacturers']);
}
} else {
$this->redirect_after = '404';
$this->redirect();
}
}
/**
* Gets the product search query for the controller. This is a set of information that
* a filtering module or the default provider will use to fetch our products.
*
* @return ProductSearchQuery
*
* @throws PrestaShop\PrestaShop\Core\Product\Search\Exception\InvalidSortOrderDirectionException
*/
protected function getProductSearchQuery(): ProductSearchQuery
{
$query = new ProductSearchQuery();
$query
->setQueryType('manufacturer')
->setIdManufacturer($this->manufacturer->id)
->setSortOrder(new SortOrder('product', Tools::getProductsOrder('by'), Tools::getProductsOrder('way')));
return $query;
}
/**
* Default product search provider used if no filtering module stood up for the job
*
* @return ManufacturerProductSearchProvider
*/
protected function getDefaultProductSearchProvider(): ManufacturerProductSearchProvider
{
return new ManufacturerProductSearchProvider(
$this->getTranslator(),
$this->manufacturer
);
}
/**
* Assign template vars if displaying one manufacturer.
*/
protected function assignManufacturer(): void
{
$manufacturerVar = $this->manufacturerPresenter->present(
$this->manufacturer,
$this->context->language
);
// Chained hook call - if multiple modules are hooked here, they will receive the result of the previous one as a parameter
$filteredManufacturer = Hook::exec(
'filterManufacturerContent',
['object' => $manufacturerVar],
$id_module = null,
$array_return = false,
$check_exceptions = true,
$use_push = false,
$id_shop = null,
$chain = true
);
if (!empty($filteredManufacturer['object'])) {
$manufacturerVar = $filteredManufacturer['object'];
}
$this->context->smarty->assign([
'manufacturer' => $manufacturerVar,
]);
}
/**
* Assign template vars if displaying the manufacturer list.
*/
protected function assignAll(): void
{
$manufacturersVar = $this->getTemplateVarManufacturers();
if (!empty($manufacturersVar)) {
foreach ($manufacturersVar as $k => $manufacturer) {
// Chained hook call - if multiple modules are hooked here, they will receive the result of the previous one as a parameter
$filteredManufacturer = Hook::exec(
'filterManufacturerContent',
['object' => $manufacturer],
$id_module = null,
$array_return = false,
$check_exceptions = true,
$use_push = false,
$id_shop = null,
$chain = true
);
if (!empty($filteredManufacturer['object'])) {
$manufacturersVar[$k] = $filteredManufacturer['object'];
}
}
}
$this->context->smarty->assign([
'brands' => $manufacturersVar,
]);
}
public function getTemplateVarManufacturers(): array
{
$manufacturers = Manufacturer::getManufacturers(true, $this->context->language->id);
foreach ($manufacturers as &$manufacturer) {
$manufacturer = $this->manufacturerPresenter->present(
$manufacturer,
$this->context->language
);
}
return $manufacturers;
}
public function getListingLabel(): string
{
return $this->label;
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = [
'title' => $this->getTranslator()->trans('Brands', [], 'Shop.Theme.Global'),
'url' => $this->context->link->getPageLink('manufacturer'),
];
if (!empty($this->manufacturer)) {
$breadcrumb['links'][] = [
'title' => $this->manufacturer->name,
'url' => $this->context->link->getManufacturerLink($this->manufacturer),
];
}
return $breadcrumb;
}
/**
* Initializes a set of commonly used variables related to the current page, available for use
* in the template. @see FrontController::assignGeneralPurposeVariables for more information.
*
* @return array
*/
public function getTemplateVarPage(): array
{
$page = parent::getTemplateVarPage();
if (!empty($this->manufacturer)) {
$page['body_classes']['manufacturer-id-' . $this->manufacturer->id] = true;
$page['body_classes']['manufacturer-' . $this->manufacturer->name] = true;
}
return $page;
}
/**
* @return Manufacturer|null
*/
public function getManufacturer(): ?Manufacturer
{
return $this->manufacturer;
}
}

View File

@@ -0,0 +1,85 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\NewProducts\NewProductsProductSearchProvider;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchQuery;
use PrestaShop\PrestaShop\Core\Product\Search\SortOrder;
class NewProductsControllerCore extends ProductListingFrontController
{
/** @var string */
public $php_self = 'new-products';
/**
* Returns canonical URL for new-products page
*
* @return string
*/
public function getCanonicalURL(): string
{
return $this->buildPaginatedUrl($this->context->link->getPageLink('new-products'));
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
parent::initContent();
$this->doProductSearch('catalog/listing/new-products', ['entity' => 'new-products']);
}
/**
* Gets the product search query for the controller. This is a set of information that
* a filtering module or the default provider will use to fetch our products.
*
* @return ProductSearchQuery
*/
protected function getProductSearchQuery(): ProductSearchQuery
{
$query = new ProductSearchQuery();
$query
->setQueryType('new-products')
->setSortOrder(new SortOrder('product', 'date_add', 'desc'));
return $query;
}
/**
* Default product search provider used if no filtering module stood up for the job
*
* @return NewProductsProductSearchProvider
*/
protected function getDefaultProductSearchProvider(): NewProductsProductSearchProvider
{
return new NewProductsProductSearchProvider(
$this->getTranslator()
);
}
public function getListingLabel(): string
{
return $this->trans(
'New products',
[],
'Shop.Theme.Catalog'
);
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = [
'title' => $this->trans('New products', [], 'Shop.Theme.Catalog'),
'url' => $this->context->link->getPageLink('new-products'),
];
return $breadcrumb;
}
}

View File

@@ -0,0 +1,85 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\PricesDrop\PricesDropProductSearchProvider;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchQuery;
use PrestaShop\PrestaShop\Core\Product\Search\SortOrder;
class PricesDropControllerCore extends ProductListingFrontController
{
/** @var string */
public $php_self = 'prices-drop';
/**
* Returns canonical URL for prices-drop page
*
* @return string
*/
public function getCanonicalURL(): string
{
return $this->buildPaginatedUrl($this->context->link->getPageLink('prices-drop'));
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
parent::initContent();
$this->doProductSearch('catalog/listing/prices-drop', ['entity' => 'prices-drop']);
}
/**
* Gets the product search query for the controller. This is a set of information that
* a filtering module or the default provider will use to fetch our products.
*
* @return ProductSearchQuery
*/
protected function getProductSearchQuery(): ProductSearchQuery
{
$query = new ProductSearchQuery();
$query
->setQueryType('prices-drop')
->setSortOrder(new SortOrder('product', 'name', 'asc'));
return $query;
}
/**
* Default product search provider used if no filtering module stood up for the job
*
* @return PricesDropProductSearchProvider
*/
protected function getDefaultProductSearchProvider(): PricesDropProductSearchProvider
{
return new PricesDropProductSearchProvider(
$this->getTranslator()
);
}
public function getListingLabel(): string
{
return $this->trans(
'Prices drop',
[],
'Shop.Theme.Catalog'
);
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = [
'title' => $this->trans('Prices drop', [], 'Shop.Theme.Catalog'),
'url' => $this->context->link->getPageLink('prices-drop'),
];
return $breadcrumb;
}
}

View File

@@ -0,0 +1,128 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\Search\SearchProductSearchProvider;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchQuery;
use PrestaShop\PrestaShop\Core\Product\Search\SortOrder;
class SearchControllerCore extends ProductListingFrontController
{
/** @var string */
public $php_self = 'search';
public $instant_search;
public $ajax_search;
protected $search_string;
protected $search_tag;
/**
* Initialize the controller.
*
* @see FrontController::init()
*/
public function init(): void
{
parent::init();
$this->search_string = Tools::getValue('s');
if (!$this->search_string) {
$this->search_string = Tools::getValue('search_query');
}
$this->search_tag = Tools::getValue('tag');
$this->context->smarty->assign(
[
'search_string' => $this->search_string,
'search_tag' => $this->search_tag,
'subcategories' => [],
]
);
}
/**
* Returns canonical URL for a search page with this term
*
* @return string
*/
public function getCanonicalURL(): string
{
return $this->buildPaginatedUrl($this->context->link->getPageLink('search', null, null, ['s' => $this->search_string]));
}
/**
* Initializes a set of commonly used variables related to the current page, available for use
* in the template. @see FrontController::assignGeneralPurposeVariables for more information.
*
* @return array
*/
public function getTemplateVarPage(): array
{
$page = parent::getTemplateVarPage();
// Ensure that no search results page is indexed by search engines.
$page['meta']['robots'] = 'noindex';
return $page;
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
parent::initContent();
$this->doProductSearch('catalog/listing/search', ['entity' => 'search']);
}
/**
* Gets the product search query for the controller. This is a set of information that
* a filtering module or the default provider will use to fetch our products.
*
* @return ProductSearchQuery
*/
protected function getProductSearchQuery(): ProductSearchQuery
{
$query = new ProductSearchQuery();
$query
->setQueryType('search')
->setSortOrder(new SortOrder('product', 'position', 'desc'))
->setSearchString($this->search_string)
->setSearchTag($this->search_tag);
return $query;
}
/**
* Default product search provider used if no filtering module stood up for the job
*
* @return SearchProductSearchProvider
*/
protected function getDefaultProductSearchProvider(): SearchProductSearchProvider
{
return new SearchProductSearchProvider(
$this->getTranslator()
);
}
public function getListingLabel(): string
{
return $this->getTranslator()->trans('Search results', [], 'Shop.Theme.Catalog');
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = [
'title' => $this->getTranslator()->trans('Search results', [], 'Shop.Theme.Catalog'),
'url' => $this->getCurrentUrl(),
];
return $breadcrumb;
}
}

View File

@@ -0,0 +1,261 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
use PrestaShop\PrestaShop\Adapter\Presenter\Supplier\SupplierPresenter;
use PrestaShop\PrestaShop\Adapter\Supplier\SupplierProductSearchProvider;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchQuery;
use PrestaShop\PrestaShop\Core\Product\Search\SortOrder;
class SupplierControllerCore extends ProductListingFrontController
{
/** @var string */
public $php_self = 'supplier';
/** @var Supplier|null */
protected $supplier;
protected $label;
/** @var SupplierPresenter */
protected $supplierPresenter;
public function canonicalRedirection(string $canonicalURL = ''): void
{
if (Validate::isLoadedObject($this->supplier)) {
parent::canonicalRedirection($this->context->link->getSupplierLink($this->supplier));
} elseif ($canonicalURL) {
parent::canonicalRedirection($canonicalURL);
}
}
/**
* Returns canonical URL for current supplier or a supplier list
*
* @return string
*/
public function getCanonicalURL(): string
{
if (Validate::isLoadedObject($this->supplier)) {
return $this->buildPaginatedUrl($this->context->link->getSupplierLink($this->supplier));
}
return $this->context->link->getPageLink('supplier');
}
/**
* Initialize supplier controller.
*
* @see FrontController::init()
*/
public function init(): void
{
if ($id_supplier = (int) Tools::getValue('id_supplier')) {
$this->supplier = new Supplier($id_supplier, $this->context->language->id);
if (!Validate::isLoadedObject($this->supplier) || !$this->supplier->active) {
$this->redirect_after = '404';
$this->redirect();
} else {
$this->canonicalRedirection();
}
}
// Initialize presenter, we will use it for all cases
$this->supplierPresenter = new SupplierPresenter($this->context->link);
parent::init();
}
/**
* Assign template vars related to page content.
*
* @see FrontController::initContent()
*/
public function initContent(): void
{
if (Configuration::get('PS_DISPLAY_SUPPLIERS')) {
parent::initContent();
if (Validate::isLoadedObject($this->supplier) && $this->supplier->active && $this->supplier->isAssociatedToShop()) {
$this->assignSupplier();
$this->label = $this->trans(
'List of products by supplier %supplier_name%',
[
'%supplier_name%' => $this->supplier->name,
],
'Shop.Theme.Catalog'
);
$this->doProductSearch(
'catalog/listing/supplier',
['entity' => 'supplier', 'id' => $this->supplier->id]
);
} else {
$this->assignAll();
$this->label = $this->trans(
'List of all suppliers',
[],
'Shop.Theme.Catalog'
);
$this->setTemplate('catalog/suppliers', ['entity' => 'suppliers']);
}
} else {
$this->redirect_after = '404';
$this->redirect();
}
}
/**
* Gets the product search query for the controller. This is a set of information that
* a filtering module or the default provider will use to fetch our products.
*
* @return ProductSearchQuery
*/
protected function getProductSearchQuery(): ProductSearchQuery
{
$query = new ProductSearchQuery();
$query
->setQueryType('supplier')
->setIdSupplier($this->supplier->id)
->setSortOrder(new SortOrder('product', 'position', 'asc'));
return $query;
}
/**
* Default product search provider used if no filtering module stood up for the job
*
* @return SupplierProductSearchProvider
*/
protected function getDefaultProductSearchProvider(): SupplierProductSearchProvider
{
return new SupplierProductSearchProvider(
$this->getTranslator(),
$this->supplier
);
}
/**
* Assign template vars if displaying one supplier.
*/
protected function assignSupplier(): void
{
$supplierVar = $this->supplierPresenter->present(
$this->supplier,
$this->context->language
);
// Chained hook call - if multiple modules are hooked here, they will receive the result of the previous one as a parameter
$filteredSupplier = Hook::exec(
'filterSupplierContent',
['object' => $supplierVar],
null,
false,
true,
false,
null,
true
);
if (!empty($filteredSupplier['object'])) {
$supplierVar = $filteredSupplier['object'];
}
$this->context->smarty->assign([
'supplier' => $supplierVar,
]);
}
/**
* Assign template vars if displaying the supplier list.
*/
protected function assignAll(): void
{
$suppliersVar = $this->getTemplateVarSuppliers();
if (!empty($suppliersVar)) {
foreach ($suppliersVar as $k => $supplier) {
// Chained hook call - if multiple modules are hooked here, they will receive the result of the previous one as a parameter
$filteredSupplier = Hook::exec(
'filterSupplierContent',
['object' => $supplier],
null,
false,
true,
false,
null,
true
);
if (!empty($filteredSupplier['object'])) {
$suppliersVar[$k] = $filteredSupplier['object'];
}
}
}
$this->context->smarty->assign([
'suppliers' => $suppliersVar,
]);
}
public function getTemplateVarSuppliers(): array
{
$suppliers = Supplier::getSuppliers(true, $this->context->language->id, true);
foreach ($suppliers as &$supplier) {
$supplier = $this->supplierPresenter->present(
$supplier,
$this->context->language
);
}
return $suppliers;
}
public function getListingLabel(): string
{
return $this->label;
}
public function getBreadcrumbLinks(): array
{
$breadcrumb = parent::getBreadcrumbLinks();
$breadcrumb['links'][] = [
'title' => $this->trans('Suppliers', [], 'Shop.Theme.Catalog'),
'url' => $this->context->link->getPageLink('supplier'),
];
if (!empty($this->supplier)) {
$breadcrumb['links'][] = [
'title' => $this->supplier->name,
'url' => $this->context->link->getSupplierLink($this->supplier),
];
}
return $breadcrumb;
}
/**
* Initializes a set of commonly used variables related to the current page, available for use
* in the template. @see FrontController::assignGeneralPurposeVariables for more information.
*
* @return array
*/
public function getTemplateVarPage(): array
{
$page = parent::getTemplateVarPage();
if (!empty($this->supplier)) {
$page['body_classes']['supplier-id-' . $this->supplier->id] = true;
$page['body_classes']['supplier-' . $this->supplier->name] = true;
}
return $page;
}
/**
* @return Supplier|null
*/
public function getSupplier(): ?Supplier
{
return $this->supplier;
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
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;