first commit
This commit is contained in:
4888
classes/controller/AdminController.php
Normal file
4888
classes/controller/AdminController.php
Normal file
File diff suppressed because it is too large
Load Diff
817
classes/controller/Controller.php
Normal file
817
classes/controller/Controller.php
Normal file
@@ -0,0 +1,817 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Open Software License (OSL 3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/OSL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
||||
* versions in the future. If you wish to customize PrestaShop for your
|
||||
* needs please refer to https://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* @TODO Move undeclared variables and methods to this (base) class: $errors, $layout, checkLiveEditAccess, etc.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
abstract class ControllerCore
|
||||
{
|
||||
const SERVICE_LOCALE_REPOSITORY = 'prestashop.core.localization.locale.repository';
|
||||
|
||||
/**
|
||||
* @var Context
|
||||
*/
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* List of CSS files.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $css_files = [];
|
||||
|
||||
/**
|
||||
* List of JavaScript files.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $js_files = [];
|
||||
|
||||
/**
|
||||
* List of PHP errors.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $php_errors = [];
|
||||
|
||||
/**
|
||||
* Set to true to display page header.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $display_header;
|
||||
|
||||
/**
|
||||
* Set to true to display page header javascript.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $display_header_javascript;
|
||||
|
||||
/**
|
||||
* Template filename for the page content.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $template;
|
||||
|
||||
/**
|
||||
* Set to true to display page footer.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $display_footer;
|
||||
|
||||
/**
|
||||
* Set to true to only render page content (used to get iframe content).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $content_only = false;
|
||||
|
||||
/**
|
||||
* If AJAX parameter is detected in request, set this flag to true.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $ajax = false;
|
||||
|
||||
/**
|
||||
* If set to true, page content and messages will be encoded to JSON before responding to AJAX request.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $json = false;
|
||||
|
||||
/**
|
||||
* JSON response status string.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $status = '';
|
||||
|
||||
/**
|
||||
* Redirect link. If not empty, the user will be redirected after initializing and processing input.
|
||||
*
|
||||
* @see Controller::run()
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $redirect_after = null;
|
||||
|
||||
/**
|
||||
* Controller type. Possible values: 'front', 'modulefront', 'admin', 'moduleadmin'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $controller_type;
|
||||
|
||||
/**
|
||||
* Controller name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $php_self;
|
||||
|
||||
/**
|
||||
* @var PrestaShopBundle\Translation\Translator
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
/**
|
||||
* Dependency container.
|
||||
*
|
||||
* @var ContainerBuilder
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Check if the controller is available for the current user/visitor.
|
||||
*/
|
||||
abstract public function checkAccess();
|
||||
|
||||
/**
|
||||
* Check if the current user/visitor has valid view permissions.
|
||||
*/
|
||||
abstract public function viewAccess();
|
||||
|
||||
/**
|
||||
* Initialize the page.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
Hook::exec(
|
||||
'actionControllerInitBefore',
|
||||
[
|
||||
'controller' => $this,
|
||||
]
|
||||
);
|
||||
|
||||
if (_PS_MODE_DEV_ && $this->controller_type == 'admin') {
|
||||
set_error_handler([__CLASS__, 'myErrorHandler']);
|
||||
}
|
||||
|
||||
if (!defined('_PS_BASE_URL_')) {
|
||||
define('_PS_BASE_URL_', Tools::getShopDomain(true));
|
||||
}
|
||||
|
||||
if (!defined('_PS_BASE_URL_SSL_')) {
|
||||
define('_PS_BASE_URL_SSL_', Tools::getShopDomainSsl(true));
|
||||
}
|
||||
|
||||
if (null === $this->getContainer()) {
|
||||
$this->container = $this->buildContainer();
|
||||
}
|
||||
|
||||
$localeRepo = $this->get(self::SERVICE_LOCALE_REPOSITORY);
|
||||
$this->context->currentLocale = $localeRepo->getLocale(
|
||||
$this->context->language->getLocale()
|
||||
);
|
||||
|
||||
Hook::exec(
|
||||
'actionControllerInitAfter',
|
||||
[
|
||||
'controller' => $this,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the page treatment: process input, process AJAX, etc.
|
||||
*/
|
||||
abstract public function postProcess();
|
||||
|
||||
/**
|
||||
* Displays page view.
|
||||
*/
|
||||
abstract public function display();
|
||||
|
||||
/**
|
||||
* Sets default media list for this controller.
|
||||
*/
|
||||
abstract public function setMedia();
|
||||
|
||||
/**
|
||||
* returns a new instance of this controller.
|
||||
*
|
||||
* @param string $class_name
|
||||
* @param bool $auth
|
||||
* @param bool $ssl
|
||||
*
|
||||
* @return Controller
|
||||
*/
|
||||
public static function getController($class_name, $auth = false, $ssl = false)
|
||||
{
|
||||
return new $class_name($auth, $ssl);
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (null === $this->display_header) {
|
||||
$this->display_header = true;
|
||||
}
|
||||
if (null === $this->display_header_javascript) {
|
||||
$this->display_header_javascript = true;
|
||||
}
|
||||
if (null === $this->display_footer) {
|
||||
$this->display_footer = true;
|
||||
}
|
||||
$this->context = Context::getContext();
|
||||
$this->context->controller = $this;
|
||||
$this->translator = Context::getContext()->getTranslator();
|
||||
$this->ajax = $this->isAjax();
|
||||
|
||||
if (
|
||||
!headers_sent() &&
|
||||
isset($_SERVER['HTTP_USER_AGENT']) &&
|
||||
(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false ||
|
||||
strpos($_SERVER['HTTP_USER_AGENT'], 'Trident') !== false)
|
||||
) {
|
||||
header('X-UA-Compatible: IE=edge,chrome=1');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the current request is an AJAX request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isAjax()
|
||||
{
|
||||
// Usage of ajax parameter is deprecated
|
||||
$isAjax = Tools::getValue('ajax') || Tools::isSubmit('ajax');
|
||||
|
||||
if (isset($_SERVER['HTTP_ACCEPT'])) {
|
||||
$isAjax = $isAjax || preg_match(
|
||||
'#\bapplication/json\b#',
|
||||
$_SERVER['HTTP_ACCEPT']
|
||||
);
|
||||
}
|
||||
|
||||
return $isAjax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the controller process (this method should not be overridden!).
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->init();
|
||||
if ($this->checkAccess()) {
|
||||
// setMedia MUST be called before postProcess
|
||||
if (!$this->content_only && ($this->display_header || (isset($this->className) && $this->className))) {
|
||||
$this->setMedia();
|
||||
}
|
||||
|
||||
// postProcess handles ajaxProcess
|
||||
$this->postProcess();
|
||||
|
||||
if (!empty($this->redirect_after)) {
|
||||
$this->redirect();
|
||||
}
|
||||
|
||||
if (!$this->content_only && ($this->display_header || (isset($this->className) && $this->className))) {
|
||||
$this->initHeader();
|
||||
}
|
||||
|
||||
if ($this->viewAccess()) {
|
||||
$this->initContent();
|
||||
} else {
|
||||
$this->errors[] = $this->trans('Access denied.', [], 'Admin.Notifications.Error');
|
||||
}
|
||||
|
||||
if (!$this->content_only && ($this->display_footer || (isset($this->className) && $this->className))) {
|
||||
$this->initFooter();
|
||||
}
|
||||
|
||||
// Default behavior for ajax process is to use $_POST[action] or $_GET[action]
|
||||
// then using displayAjax[action]
|
||||
if ($this->ajax) {
|
||||
$action = Tools::toCamelCase(Tools::getValue('action'), true);
|
||||
|
||||
if (!empty($action) && method_exists($this, 'displayAjax' . $action)) {
|
||||
$this->{'displayAjax' . $action}();
|
||||
} elseif (method_exists($this, 'displayAjax')) {
|
||||
$this->displayAjax();
|
||||
}
|
||||
} else {
|
||||
$this->display();
|
||||
}
|
||||
} else {
|
||||
$this->initCursedPage();
|
||||
$this->smartyOutputContent($this->layout);
|
||||
}
|
||||
}
|
||||
|
||||
protected function trans($id, array $parameters = [], $domain = null, $locale = null)
|
||||
{
|
||||
$parameters['legacy'] = 'htmlspecialchars';
|
||||
|
||||
return $this->translator->trans($id, $parameters, $domain, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets page header display.
|
||||
*
|
||||
* @param bool $display
|
||||
*/
|
||||
public function displayHeader($display = true)
|
||||
{
|
||||
$this->display_header = $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets page header javascript display.
|
||||
*
|
||||
* @param bool $display
|
||||
*/
|
||||
public function displayHeaderJavaScript($display = true)
|
||||
{
|
||||
$this->display_header_javascript = $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets page header display.
|
||||
*
|
||||
* @param bool $display
|
||||
*/
|
||||
public function displayFooter($display = true)
|
||||
{
|
||||
$this->display_footer = $display;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets template file for page content output.
|
||||
*
|
||||
* @param string $template
|
||||
*/
|
||||
public function setTemplate($template)
|
||||
{
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns Smarty variables for the page header.
|
||||
*/
|
||||
abstract public function initHeader();
|
||||
|
||||
/**
|
||||
* Assigns Smarty variables for the page main content.
|
||||
*/
|
||||
abstract public function initContent();
|
||||
|
||||
/**
|
||||
* Assigns Smarty variables when access is forbidden.
|
||||
*/
|
||||
abstract public function initCursedPage();
|
||||
|
||||
/**
|
||||
* Assigns Smarty variables for the page footer.
|
||||
*/
|
||||
abstract public function initFooter();
|
||||
|
||||
/**
|
||||
* Redirects to $this->redirect_after after the process if there is no error.
|
||||
*/
|
||||
abstract protected function redirect();
|
||||
|
||||
/**
|
||||
* Set $this->redirect_after that will be used by redirect() after the process.
|
||||
*/
|
||||
public function setRedirectAfter($url)
|
||||
{
|
||||
$this->redirect_after = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new stylesheet(s) to the page header.
|
||||
*
|
||||
* @param string|array $css_uri Path to CSS file, or list of css files like this : array(array(uri => media_type), ...)
|
||||
* @param string $css_media_type
|
||||
* @param int|null $offset
|
||||
* @param bool $check_path
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function addCSS($css_uri, $css_media_type = 'all', $offset = null, $check_path = true)
|
||||
{
|
||||
if (!is_array($css_uri)) {
|
||||
$css_uri = [$css_uri];
|
||||
}
|
||||
|
||||
foreach ($css_uri as $css_file => $media) {
|
||||
if (is_string($css_file) && strlen($css_file) > 1) {
|
||||
if ($check_path) {
|
||||
$css_path = Media::getCSSPath($css_file, $media);
|
||||
} else {
|
||||
$css_path = [$css_file => $media];
|
||||
}
|
||||
} else {
|
||||
if ($check_path) {
|
||||
$css_path = Media::getCSSPath($media, $css_media_type);
|
||||
} else {
|
||||
$css_path = [$media => $css_media_type];
|
||||
}
|
||||
}
|
||||
|
||||
$key = is_array($css_path) ? key($css_path) : $css_path;
|
||||
if ($css_path && (!isset($this->css_files[$key]) || ($this->css_files[$key] != reset($css_path)))) {
|
||||
$size = count($this->css_files);
|
||||
if ($offset === null || $offset > $size || $offset < 0 || !is_numeric($offset)) {
|
||||
$offset = $size;
|
||||
}
|
||||
|
||||
$this->css_files = array_merge(array_slice($this->css_files, 0, $offset), $css_path, array_slice($this->css_files, $offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes CSS stylesheet(s) from the queued stylesheet list.
|
||||
*
|
||||
* @param string|array $css_uri Path to CSS file or an array like: array(array(uri => media_type), ...)
|
||||
* @param string $css_media_type
|
||||
* @param bool $check_path
|
||||
*/
|
||||
public function removeCSS($css_uri, $css_media_type = 'all', $check_path = true)
|
||||
{
|
||||
if (!is_array($css_uri)) {
|
||||
$css_uri = [$css_uri];
|
||||
}
|
||||
|
||||
foreach ($css_uri as $css_file => $media) {
|
||||
if (is_string($css_file) && strlen($css_file) > 1) {
|
||||
if ($check_path) {
|
||||
$css_path = Media::getCSSPath($css_file, $media);
|
||||
} else {
|
||||
$css_path = [$css_file => $media];
|
||||
}
|
||||
} else {
|
||||
if ($check_path) {
|
||||
$css_path = Media::getCSSPath($media, $css_media_type);
|
||||
} else {
|
||||
$css_path = [$media => $css_media_type];
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
$css_path
|
||||
&& isset($this->css_files[key($css_path)])
|
||||
&& ($this->css_files[key($css_path)] == reset($css_path))
|
||||
) {
|
||||
unset($this->css_files[key($css_path)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new JavaScript file(s) to the page header.
|
||||
*
|
||||
* @param string|array $js_uri Path to JS file or an array like: array(uri, ...)
|
||||
* @param bool $check_path
|
||||
*/
|
||||
public function addJS($js_uri, $check_path = true)
|
||||
{
|
||||
if (!is_array($js_uri)) {
|
||||
$js_uri = [$js_uri];
|
||||
}
|
||||
|
||||
foreach ($js_uri as $js_file) {
|
||||
$js_file = explode('?', $js_file);
|
||||
$version = '';
|
||||
if (isset($js_file[1]) && $js_file[1]) {
|
||||
$version = $js_file[1];
|
||||
}
|
||||
$js_path = $js_file = $js_file[0];
|
||||
if ($check_path) {
|
||||
$js_path = Media::getJSPath($js_file);
|
||||
}
|
||||
|
||||
if ($js_path && !in_array($js_path, $this->js_files)) {
|
||||
$this->js_files[] = $js_path . ($version ? '?' . $version : '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes JS file(s) from the queued JS file list.
|
||||
*
|
||||
* @param string|array $js_uri Path to JS file or an array like: array(uri, ...)
|
||||
* @param bool $check_path
|
||||
*/
|
||||
public function removeJS($js_uri, $check_path = true)
|
||||
{
|
||||
if (!is_array($js_uri)) {
|
||||
$js_uri = [$js_uri];
|
||||
}
|
||||
|
||||
foreach ($js_uri as $js_file) {
|
||||
if ($check_path) {
|
||||
$js_file = Media::getJSPath($js_file);
|
||||
}
|
||||
|
||||
if ($js_file && in_array($js_file, $this->js_files)) {
|
||||
unset($this->js_files[array_search($js_file, $this->js_files)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds jQuery library file to queued JS file list.
|
||||
*
|
||||
* @param string|null $version jQuery library version
|
||||
* @param string|null $folder jQuery file folder
|
||||
* @param bool $minifier if set tot true, a minified version will be included
|
||||
*
|
||||
* @deprecated 1.7.7 jQuery is always included, this method should no longer be used
|
||||
*/
|
||||
public function addJquery($version = null, $folder = null, $minifier = true)
|
||||
{
|
||||
@trigger_error(
|
||||
'Controller->addJquery() is deprecated since version 1.7.7.0, jQuery is always included',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds jQuery UI component(s) to queued JS file list.
|
||||
*
|
||||
* @param string|array $component
|
||||
* @param string $theme
|
||||
* @param bool $check_dependencies
|
||||
*/
|
||||
public function addJqueryUI($component, $theme = 'base', $check_dependencies = true)
|
||||
{
|
||||
if (!is_array($component)) {
|
||||
$component = [$component];
|
||||
}
|
||||
|
||||
foreach ($component as $ui) {
|
||||
$ui_path = Media::getJqueryUIPath($ui, $theme, $check_dependencies);
|
||||
$this->addCSS($ui_path['css'], 'all', false);
|
||||
$this->addJS($ui_path['js'], false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds jQuery plugin(s) to queued JS file list.
|
||||
*
|
||||
* @param string|array $name
|
||||
* @param string null $folder
|
||||
* @param bool $css
|
||||
*/
|
||||
public function addJqueryPlugin($name, $folder = null, $css = true)
|
||||
{
|
||||
if (!is_array($name)) {
|
||||
$name = [$name];
|
||||
}
|
||||
|
||||
foreach ($name as $plugin) {
|
||||
$plugin_path = Media::getJqueryPluginPath($plugin, $folder);
|
||||
|
||||
if (!empty($plugin_path['js'])) {
|
||||
$this->addJS($plugin_path['js'], false);
|
||||
}
|
||||
if ($css && !empty($plugin_path['css'])) {
|
||||
$this->addCSS(key($plugin_path['css']), 'all', null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the controller has been called from XmlHttpRequest (AJAX).
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isXmlHttpRequest()
|
||||
{
|
||||
return
|
||||
!empty($_SERVER['HTTP_X_REQUESTED_WITH'])
|
||||
&& strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
|
||||
}
|
||||
|
||||
public function getLayout()
|
||||
{
|
||||
// This is implemented by some children classes (e.g. FrontController)
|
||||
// but not required for all controllers.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders controller templates and generates page content.
|
||||
*
|
||||
* @param array|string $templates Template file(s) to be rendered
|
||||
*
|
||||
* @throws Exception
|
||||
* @throws SmartyException
|
||||
*/
|
||||
protected function smartyOutputContent($templates)
|
||||
{
|
||||
$this->context->cookie->write();
|
||||
|
||||
$js_tag = 'js_def';
|
||||
$this->context->smarty->assign($js_tag, $js_tag);
|
||||
|
||||
if (!is_array($templates)) {
|
||||
$templates = [$templates];
|
||||
}
|
||||
|
||||
$html = '';
|
||||
foreach ($templates as $template) {
|
||||
$html .= $this->context->smarty->fetch($template, null, $this->getLayout());
|
||||
}
|
||||
|
||||
echo trim($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a template is cached.
|
||||
*
|
||||
* @param string $template
|
||||
* @param string|null $cache_id Cache item ID
|
||||
* @param string|null $compile_id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isCached($template, $cache_id = null, $compile_id = null)
|
||||
{
|
||||
Tools::enableCache();
|
||||
$isCached = $this->context->smarty->isCached($template, $cache_id, $compile_id);
|
||||
Tools::restoreCacheSettings();
|
||||
|
||||
return $isCached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom error handler.
|
||||
*
|
||||
* @param string $errno
|
||||
* @param string $errstr
|
||||
* @param string $errfile
|
||||
* @param int $errline
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function myErrorHandler($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
if (error_reporting() === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ($errno) {
|
||||
case E_USER_ERROR:
|
||||
case E_ERROR:
|
||||
die('Fatal error: ' . $errstr . ' in ' . $errfile . ' on line ' . $errline);
|
||||
|
||||
break;
|
||||
case E_USER_WARNING:
|
||||
case E_WARNING:
|
||||
$type = 'Warning';
|
||||
|
||||
break;
|
||||
case E_USER_NOTICE:
|
||||
case E_NOTICE:
|
||||
$type = 'Notice';
|
||||
|
||||
break;
|
||||
default:
|
||||
$type = 'Unknown error';
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Controller::$php_errors[] = [
|
||||
'type' => $type,
|
||||
'errline' => (int) $errline,
|
||||
'errfile' => str_replace('\\', '\\\\', $errfile), // Hack for Windows paths
|
||||
'errno' => (int) $errno,
|
||||
'errstr' => $errstr,
|
||||
];
|
||||
Context::getContext()->smarty->assign('php_errors', Controller::$php_errors);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated deprecated since 1.7.5.0, use ajaxRender instead
|
||||
* Dies and echoes output value
|
||||
*
|
||||
* @param string|null $value
|
||||
* @param string|null $controller
|
||||
* @param string|null $method
|
||||
*
|
||||
* @throws PrestaShopException
|
||||
*/
|
||||
protected function ajaxDie($value = null, $controller = null, $method = null)
|
||||
{
|
||||
$this->ajaxRender($value, $controller, $method);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $value
|
||||
* @param string|null $controller
|
||||
* @param string|null $method
|
||||
*
|
||||
* @throws PrestaShopException
|
||||
*/
|
||||
protected function ajaxRender($value = null, $controller = null, $method = null)
|
||||
{
|
||||
if ($controller === null) {
|
||||
$controller = get_class($this);
|
||||
}
|
||||
|
||||
if ($method === null) {
|
||||
$bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
$method = $bt[1]['function'];
|
||||
}
|
||||
|
||||
/* @deprecated deprecated since 1.6.1.1 */
|
||||
Hook::exec('actionAjaxDieBefore', ['controller' => $controller, 'method' => $method, 'value' => $value]);
|
||||
|
||||
/*
|
||||
* @deprecated deprecated since 1.6.1.1
|
||||
* use 'actionAjaxDie'.$controller.$method.'Before' instead
|
||||
*/
|
||||
Hook::exec('actionBeforeAjaxDie' . $controller . $method, ['value' => $value]);
|
||||
Hook::exec('actionAjaxDie' . $controller . $method . 'Before', ['value' => $value]);
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
|
||||
|
||||
echo $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the dependency container.
|
||||
*
|
||||
* @return ContainerBuilder
|
||||
*/
|
||||
abstract protected function buildContainer();
|
||||
|
||||
/**
|
||||
* Gets a service from the service container.
|
||||
*
|
||||
* @param string $serviceId Service identifier
|
||||
*
|
||||
* @return object The associated service
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function get($serviceId)
|
||||
{
|
||||
return $this->container->get($serviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a parameter.
|
||||
*
|
||||
* @param string $parameterId The parameter name
|
||||
*
|
||||
* @return mixed The parameter value
|
||||
*
|
||||
* @throws InvalidArgumentException if the parameter is not defined
|
||||
*/
|
||||
public function getParameter($parameterId)
|
||||
{
|
||||
return $this->container->getParameter($parameterId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the dependency container.
|
||||
*
|
||||
* @return ContainerBuilder|null
|
||||
*/
|
||||
public function getContainer()
|
||||
{
|
||||
return $this->container;
|
||||
}
|
||||
}
|
||||
2121
classes/controller/FrontController.php
Normal file
2121
classes/controller/FrontController.php
Normal file
File diff suppressed because it is too large
Load Diff
98
classes/controller/ModuleAdminController.php
Normal file
98
classes/controller/ModuleAdminController.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Open Software License (OSL 3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/OSL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
||||
* versions in the future. If you wish to customize PrestaShop for your
|
||||
* needs please refer to https://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @since 1.5.0
|
||||
*/
|
||||
abstract class ModuleAdminControllerCore extends AdminController
|
||||
{
|
||||
/** @var Module */
|
||||
public $module;
|
||||
|
||||
/**
|
||||
* @throws PrestaShopException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->controller_type = 'moduleadmin';
|
||||
|
||||
$tab = new Tab($this->id);
|
||||
if (!$tab->module) {
|
||||
throw new PrestaShopException('Admin tab ' . get_class($this) . ' is not a module tab');
|
||||
}
|
||||
|
||||
$this->module = Module::getInstanceByName($tab->module);
|
||||
if (!$this->module->id) {
|
||||
throw new PrestaShopException("Module {$tab->module} not found");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a template object.
|
||||
*
|
||||
* @param string $tpl_name Template filename
|
||||
*
|
||||
* @return Smarty_Internal_Template
|
||||
*/
|
||||
public function createTemplate($tpl_name)
|
||||
{
|
||||
if ($this->viewAccess()) {
|
||||
foreach ($this->getTemplateLookupPaths() as $path) {
|
||||
if (file_exists($path . $tpl_name)) {
|
||||
return $this->context->smarty->createTemplate($path . $tpl_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::createTemplate($tpl_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path to back office templates for the module.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplatePath()
|
||||
{
|
||||
return _PS_MODULE_DIR_ . $this->module->name . '/views/templates/admin/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getTemplateLookupPaths()
|
||||
{
|
||||
$templatePath = $this->getTemplatePath();
|
||||
|
||||
return [
|
||||
_PS_THEME_DIR_ . 'modules/' . $this->module->name . '/views/templates/admin/',
|
||||
$templatePath . $this->override_folder,
|
||||
$templatePath,
|
||||
];
|
||||
}
|
||||
}
|
||||
101
classes/controller/ModuleFrontController.php
Normal file
101
classes/controller/ModuleFrontController.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Open Software License (OSL 3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/OSL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
||||
* versions in the future. If you wish to customize PrestaShop for your
|
||||
* needs please refer to https://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @since 1.5.0
|
||||
*/
|
||||
class ModuleFrontControllerCore extends FrontController
|
||||
{
|
||||
/** @var Module */
|
||||
public $module;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->module = Module::getInstanceByName(Tools::getValue('module'));
|
||||
if (!$this->module->active) {
|
||||
Tools::redirect('index');
|
||||
}
|
||||
|
||||
$this->page_name = 'module-' . $this->module->name . '-' . Dispatcher::getInstance()->getController();
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->controller_type = 'modulefront';
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns module template for page content.
|
||||
*
|
||||
* @param string $template Template filename
|
||||
*
|
||||
* @throws PrestaShopException
|
||||
*/
|
||||
public function setTemplate($template, $params = [], $locale = null)
|
||||
{
|
||||
if (strpos($template, 'module:') === 0) {
|
||||
$this->template = $template;
|
||||
} else {
|
||||
parent::setTemplate($template, $params, $locale);
|
||||
}
|
||||
}
|
||||
|
||||
public function initContent()
|
||||
{
|
||||
if (Tools::isSubmit('module') && Tools::getValue('controller') == 'payment') {
|
||||
$currency = Currency::getCurrency((int) $this->context->cart->id_currency);
|
||||
$minimalPurchase = Tools::convertPrice((float) Configuration::get('PS_PURCHASE_MINIMUM'), $currency);
|
||||
Hook::exec('overrideMinimalPurchasePrice', [
|
||||
'minimalPurchase' => &$minimalPurchase,
|
||||
]);
|
||||
if ($this->context->cart->getOrderTotal(false, Cart::ONLY_PRODUCTS) < $minimalPurchase) {
|
||||
Tools::redirect('index.php?controller=order&step=1');
|
||||
}
|
||||
}
|
||||
parent::initContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-static translation method for frontoffice modules.
|
||||
*
|
||||
* @deprecated use Context::getContext()->getTranslator()->trans($id, $parameters, $domain, $locale); instead
|
||||
*
|
||||
* @param string $string Term or expression in english
|
||||
* @param false|string $specific Specific name, only for ModuleFrontController
|
||||
* @param string|null $class Name of the class
|
||||
* @param bool $addslashes If set to true, the return value will pass through addslashes(). Otherwise, stripslashes()
|
||||
* @param bool $htmlentities If set to true(default), the return value will pass through htmlentities($string, ENT_QUOTES, 'utf-8')
|
||||
*
|
||||
* @return string The translation if available, or the english default text
|
||||
*/
|
||||
protected function l($string, $specific = false, $class = null, $addslashes = false, $htmlentities = true)
|
||||
{
|
||||
if (isset($this->module) && is_a($this->module, 'Module')) {
|
||||
return $this->module->l($string, $specific);
|
||||
} else {
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
}
|
||||
615
classes/controller/ProductListingFrontController.php
Normal file
615
classes/controller/ProductListingFrontController.php
Normal file
@@ -0,0 +1,615 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Open Software License (OSL 3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/OSL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
||||
* versions in the future. If you wish to customize PrestaShop for your
|
||||
* needs please refer to https://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\Facet;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\FacetsRendererInterface;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\Pagination;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchContext;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchProviderInterface;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchQuery;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchResult;
|
||||
use PrestaShop\PrestaShop\Core\Product\Search\SortOrder;
|
||||
|
||||
/**
|
||||
* This class is the base class for all front-end "product listing" controllers,
|
||||
* like "CategoryController", that is, controllers whose primary job is
|
||||
* to display a list of products and filters to make navigation easier.
|
||||
*/
|
||||
abstract class ProductListingFrontControllerCore extends ProductPresentingFrontController
|
||||
{
|
||||
/**
|
||||
* Takes an associative array with at least the "id_product" key
|
||||
* and returns an array containing all information necessary for
|
||||
* rendering the product in the template.
|
||||
*
|
||||
* @param array $rawProduct an associative array with at least the "id_product" key
|
||||
*
|
||||
* @return array a product ready for templating
|
||||
*/
|
||||
private function prepareProductForTemplate(array $rawProduct)
|
||||
{
|
||||
$product = (new ProductAssembler($this->context))
|
||||
->assembleProduct($rawProduct);
|
||||
|
||||
$presenter = $this->getProductPresenter();
|
||||
$settings = $this->getProductPresentationSettings();
|
||||
|
||||
return $presenter->present(
|
||||
$settings,
|
||||
$product,
|
||||
$this->context->language
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs "prepareProductForTemplate" on the collection
|
||||
* of product ids passed in.
|
||||
*
|
||||
* @param array $products array of arrays containing at list the "id_product" key
|
||||
*
|
||||
* @return array of products ready for templating
|
||||
*/
|
||||
protected function prepareMultipleProductsForTemplate(array $products)
|
||||
{
|
||||
return array_map([$this, 'prepareProductForTemplate'], $products);
|
||||
}
|
||||
|
||||
/**
|
||||
* The ProductSearchContext is passed to search providers
|
||||
* so that they can avoid using the global id_lang and such
|
||||
* variables. This method acts as a factory for the ProductSearchContext.
|
||||
*
|
||||
* @return ProductSearchContext a search context for the queries made by this controller
|
||||
*/
|
||||
protected function getProductSearchContext()
|
||||
{
|
||||
return (new ProductSearchContext())
|
||||
->setIdShop($this->context->shop->id)
|
||||
->setIdLang($this->context->language->id)
|
||||
->setIdCurrency($this->context->currency->id)
|
||||
->setIdCustomer(
|
||||
$this->context->customer ?
|
||||
$this->context->customer->id :
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Facet to an array with all necessary
|
||||
* information for templating.
|
||||
*
|
||||
* @param Facet $facet
|
||||
*
|
||||
* @return array ready for templating
|
||||
*/
|
||||
protected function prepareFacetForTemplate(Facet $facet)
|
||||
{
|
||||
$facetsArray = $facet->toArray();
|
||||
foreach ($facetsArray['filters'] as &$filter) {
|
||||
$filter['facetLabel'] = $facet->getLabel();
|
||||
if ($filter['nextEncodedFacets']) {
|
||||
$filter['nextEncodedFacetsURL'] = $this->updateQueryString([
|
||||
'q' => $filter['nextEncodedFacets'],
|
||||
'page' => null,
|
||||
]);
|
||||
} else {
|
||||
$filter['nextEncodedFacetsURL'] = $this->updateQueryString([
|
||||
'q' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
unset($filter);
|
||||
|
||||
return $facetsArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an array of facets.
|
||||
*
|
||||
* @param array $facets
|
||||
*
|
||||
* @return string the HTML of the facets
|
||||
*/
|
||||
protected function renderFacets(ProductSearchResult $result)
|
||||
{
|
||||
$facetCollection = $result->getFacetCollection();
|
||||
// not all search providers generate menus
|
||||
if (empty($facetCollection)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$facetsVar = array_map(
|
||||
[$this, 'prepareFacetForTemplate'],
|
||||
$facetCollection->getFacets()
|
||||
);
|
||||
|
||||
$activeFilters = [];
|
||||
foreach ($facetsVar as $facet) {
|
||||
foreach ($facet['filters'] as $filter) {
|
||||
if ($filter['active']) {
|
||||
$activeFilters[] = $filter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('catalog/_partials/facets', [
|
||||
'facets' => $facetsVar,
|
||||
'js_enabled' => $this->ajax,
|
||||
'activeFilters' => $activeFilters,
|
||||
'sort_order' => $result->getCurrentSortOrder()->toString(),
|
||||
'clear_all_link' => $this->updateQueryString(['q' => null, 'page' => null]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an array of active filters.
|
||||
*
|
||||
* @param array $facets
|
||||
*
|
||||
* @return string the HTML of the facets
|
||||
*/
|
||||
protected function renderActiveFilters(ProductSearchResult $result)
|
||||
{
|
||||
$facetCollection = $result->getFacetCollection();
|
||||
// not all search providers generate menus
|
||||
if (empty($facetCollection)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$facetsVar = array_map(
|
||||
[$this, 'prepareFacetForTemplate'],
|
||||
$facetCollection->getFacets()
|
||||
);
|
||||
|
||||
$activeFilters = [];
|
||||
foreach ($facetsVar as $facet) {
|
||||
foreach ($facet['filters'] as $filter) {
|
||||
if ($filter['active']) {
|
||||
$activeFilters[] = $filter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('catalog/_partials/active_filters', [
|
||||
'activeFilters' => $activeFilters,
|
||||
'clear_all_link' => $this->updateQueryString(['q' => null, 'page' => null]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is the heart of the search provider delegation
|
||||
* mechanism.
|
||||
*
|
||||
* It executes the `productSearchProvider` hook (array style),
|
||||
* and returns the first one encountered.
|
||||
*
|
||||
* This provides a well specified way for modules to execute
|
||||
* the search query instead of the core.
|
||||
*
|
||||
* The hook is called with the $query argument, which allows
|
||||
* modules to decide if they can manage the query.
|
||||
*
|
||||
* For instance, if two search modules are installed and
|
||||
* one module knows how to search by category but not by manufacturer,
|
||||
* then "ManufacturerController" will use one module to do the query while
|
||||
* "CategoryController" will use another module to do the query.
|
||||
*
|
||||
* If no module can perform the query then null is returned.
|
||||
*
|
||||
* @param ProductSearchQuery $query
|
||||
*
|
||||
* @return ProductSearchProviderInterface or null
|
||||
*/
|
||||
private function getProductSearchProviderFromModules($query)
|
||||
{
|
||||
$providers = Hook::exec(
|
||||
'productSearchProvider',
|
||||
['query' => $query],
|
||||
null,
|
||||
true
|
||||
);
|
||||
|
||||
if (!is_array($providers)) {
|
||||
$providers = [];
|
||||
}
|
||||
|
||||
foreach ($providers as $provider) {
|
||||
if ($provider instanceof ProductSearchProviderInterface) {
|
||||
return $provider;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns all template variables needed for rendering
|
||||
* the product list, the facets, the pagination and the sort orders.
|
||||
*
|
||||
* @return array variables ready for templating
|
||||
*/
|
||||
protected function getProductSearchVariables()
|
||||
{
|
||||
/*
|
||||
* To render the page we need to find something (a ProductSearchProviderInterface)
|
||||
* that knows how to query products.
|
||||
*/
|
||||
|
||||
// the search provider will need a context (language, shop...) to do its job
|
||||
$context = $this->getProductSearchContext();
|
||||
|
||||
// the controller generates the query...
|
||||
$query = $this->getProductSearchQuery();
|
||||
|
||||
// ...modules decide if they can handle it (first one that can is used)
|
||||
$provider = $this->getProductSearchProviderFromModules($query);
|
||||
|
||||
// if no module wants to do the query, then the core feature is used
|
||||
if (null === $provider) {
|
||||
$provider = $this->getDefaultProductSearchProvider();
|
||||
}
|
||||
|
||||
$resultsPerPage = (int) Tools::getValue('resultsPerPage');
|
||||
if ($resultsPerPage <= 0) {
|
||||
$resultsPerPage = Configuration::get('PS_PRODUCTS_PER_PAGE');
|
||||
}
|
||||
|
||||
// we need to set a few parameters from back-end preferences
|
||||
$query
|
||||
->setResultsPerPage($resultsPerPage)
|
||||
->setPage(max((int) Tools::getValue('page'), 1))
|
||||
;
|
||||
|
||||
// set the sort order if provided in the URL
|
||||
if (($encodedSortOrder = Tools::getValue('order'))) {
|
||||
$query->setSortOrder(SortOrder::newFromString(
|
||||
$encodedSortOrder
|
||||
));
|
||||
}
|
||||
|
||||
// get the parameters containing the encoded facets from the URL
|
||||
$encodedFacets = Tools::getValue('q');
|
||||
|
||||
/*
|
||||
* The controller is agnostic of facets.
|
||||
* It's up to the search module to use /define them.
|
||||
*
|
||||
* Facets are encoded in the "q" URL parameter, which is passed
|
||||
* to the search provider through the query's "$encodedFacets" property.
|
||||
*/
|
||||
|
||||
$query->setEncodedFacets($encodedFacets);
|
||||
|
||||
Hook::exec('actionProductSearchProviderRunQueryBefore', [
|
||||
'query' => $query,
|
||||
]);
|
||||
|
||||
// We're ready to run the actual query!
|
||||
|
||||
/** @var ProductSearchResult $result */
|
||||
$result = $provider->runQuery(
|
||||
$context,
|
||||
$query
|
||||
);
|
||||
|
||||
Hook::exec('actionProductSearchProviderRunQueryAfter', [
|
||||
'query' => $query,
|
||||
'result' => $result,
|
||||
]);
|
||||
|
||||
if (Configuration::get('PS_CATALOG_MODE') && !Configuration::get('PS_CATALOG_MODE_WITH_PRICES')) {
|
||||
$this->disablePriceControls($result);
|
||||
}
|
||||
|
||||
// sort order is useful for template,
|
||||
// add it if undefined - it should be the same one
|
||||
// as for the query anyway
|
||||
if (!$result->getCurrentSortOrder()) {
|
||||
$result->setCurrentSortOrder($query->getSortOrder());
|
||||
}
|
||||
|
||||
// prepare the products
|
||||
$products = $this->prepareMultipleProductsForTemplate(
|
||||
$result->getProducts()
|
||||
);
|
||||
|
||||
// render the facets
|
||||
if ($provider instanceof FacetsRendererInterface) {
|
||||
// with the provider if it wants to
|
||||
$rendered_facets = $provider->renderFacets(
|
||||
$context,
|
||||
$result
|
||||
);
|
||||
$rendered_active_filters = $provider->renderActiveFilters(
|
||||
$context,
|
||||
$result
|
||||
);
|
||||
} else {
|
||||
// with the core
|
||||
$rendered_facets = $this->renderFacets(
|
||||
$result
|
||||
);
|
||||
$rendered_active_filters = $this->renderActiveFilters(
|
||||
$result
|
||||
);
|
||||
}
|
||||
|
||||
$pagination = $this->getTemplateVarPagination(
|
||||
$query,
|
||||
$result
|
||||
);
|
||||
|
||||
// prepare the sort orders
|
||||
// note that, again, the product controller is sort-orders
|
||||
// agnostic
|
||||
// a module can easily add specific sort orders that it needs
|
||||
// to support (e.g. sort by "energy efficiency")
|
||||
$sort_orders = $this->getTemplateVarSortOrders(
|
||||
$result->getAvailableSortOrders(),
|
||||
$query->getSortOrder()->toString()
|
||||
);
|
||||
|
||||
$sort_selected = false;
|
||||
if (!empty($sort_orders)) {
|
||||
foreach ($sort_orders as $order) {
|
||||
if (isset($order['current']) && true === $order['current']) {
|
||||
$sort_selected = $order['label'];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$searchVariables = [
|
||||
'result' => $result,
|
||||
'label' => $this->getListingLabel(),
|
||||
'products' => $products,
|
||||
'sort_orders' => $sort_orders,
|
||||
'sort_selected' => $sort_selected,
|
||||
'pagination' => $pagination,
|
||||
'rendered_facets' => $rendered_facets,
|
||||
'rendered_active_filters' => $rendered_active_filters,
|
||||
'js_enabled' => $this->ajax,
|
||||
'current_url' => $this->updateQueryString([
|
||||
'q' => $result->getEncodedFacets(),
|
||||
]),
|
||||
];
|
||||
|
||||
Hook::exec('filterProductSearch', ['searchVariables' => &$searchVariables]);
|
||||
Hook::exec('actionProductSearchAfter', $searchVariables);
|
||||
|
||||
return $searchVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes price information from result (in facet collection and available sorters)
|
||||
* Usually used for catalog mode.
|
||||
*
|
||||
* @param ProductSearchResult $result
|
||||
*/
|
||||
protected function disablePriceControls(ProductSearchResult $result)
|
||||
{
|
||||
if ($result->getFacetCollection()) {
|
||||
$filteredFacets = [];
|
||||
/** @var Facet $facet */
|
||||
foreach ($result->getFacetCollection()->getFacets() as $facet) {
|
||||
if ('price' === $facet->getType()) {
|
||||
continue;
|
||||
}
|
||||
$filteredFacets[] = $facet;
|
||||
}
|
||||
$result->getFacetCollection()->setFacets($filteredFacets);
|
||||
}
|
||||
|
||||
if ($result->getAvailableSortOrders()) {
|
||||
$filteredOrders = [];
|
||||
/** @var SortOrder $sortOrder */
|
||||
foreach ($result->getAvailableSortOrders() as $sortOrder) {
|
||||
if ('price' === $sortOrder->getField()) {
|
||||
continue;
|
||||
}
|
||||
$filteredOrders[] = $sortOrder;
|
||||
}
|
||||
$result->setAvailableSortOrders($filteredOrders);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pagination is HARD. We let the core do the heavy lifting from
|
||||
* a simple representation of the pagination.
|
||||
*
|
||||
* Generated URLs will include the page number, obviously,
|
||||
* but also the sort order and the "q" (facets) parameters.
|
||||
*
|
||||
* @param ProductSearchQuery $query
|
||||
* @param ProductSearchResult $result
|
||||
*
|
||||
* @return array An array that makes rendering the pagination very easy
|
||||
*/
|
||||
protected function getTemplateVarPagination(
|
||||
ProductSearchQuery $query,
|
||||
ProductSearchResult $result
|
||||
) {
|
||||
$pagination = new Pagination();
|
||||
$pagination
|
||||
->setPage($query->getPage())
|
||||
->setPagesCount(
|
||||
(int) ceil($result->getTotalProductsCount() / $query->getResultsPerPage())
|
||||
)
|
||||
;
|
||||
|
||||
$totalItems = $result->getTotalProductsCount();
|
||||
$itemsShownFrom = ($query->getResultsPerPage() * ($query->getPage() - 1)) + 1;
|
||||
$itemsShownTo = $query->getResultsPerPage() * $query->getPage();
|
||||
|
||||
$pages = array_map(function ($link) {
|
||||
$link['url'] = $this->updateQueryString([
|
||||
'page' => $link['page'] > 1 ? $link['page'] : null,
|
||||
]);
|
||||
|
||||
return $link;
|
||||
}, $pagination->buildLinks());
|
||||
|
||||
//Filter next/previous link on first/last page
|
||||
$pages = array_filter($pages, function ($page) use ($pagination) {
|
||||
if ('previous' === $page['type'] && 1 === $pagination->getPage()) {
|
||||
return false;
|
||||
}
|
||||
if ('next' === $page['type'] && $pagination->getPagesCount() === $pagination->getPage()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return [
|
||||
'total_items' => $totalItems,
|
||||
'items_shown_from' => $itemsShownFrom,
|
||||
'items_shown_to' => ($itemsShownTo <= $totalItems) ? $itemsShownTo : $totalItems,
|
||||
'current_page' => $pagination->getPage(),
|
||||
'pages_count' => $pagination->getPagesCount(),
|
||||
'pages' => $pages,
|
||||
// Compare to 3 because there are the next and previous links
|
||||
'should_be_displayed' => (count($pagination->buildLinks()) > 3),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the sort-order links.
|
||||
*
|
||||
* Sort order links contain the current encoded facets if any,
|
||||
* but not the page number because normally when you change the sort order
|
||||
* you want to go back to page one.
|
||||
*
|
||||
* @param array $sortOrders the available sort orders
|
||||
* @param string $currentSortOrderURLParameter used to know which of the sort orders (if any) is active
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getTemplateVarSortOrders(array $sortOrders, $currentSortOrderURLParameter)
|
||||
{
|
||||
return array_map(function ($sortOrder) use ($currentSortOrderURLParameter) {
|
||||
$order = $sortOrder->toArray();
|
||||
$order['current'] = $order['urlParameter'] === $currentSortOrderURLParameter;
|
||||
$order['url'] = $this->updateQueryString([
|
||||
'order' => $order['urlParameter'],
|
||||
'page' => null,
|
||||
]);
|
||||
|
||||
return $order;
|
||||
}, $sortOrders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to "getProductSearchVariables" but used in AJAX queries.
|
||||
*
|
||||
* It returns an array with the HTML for the products and facets,
|
||||
* and the current URL to put it in the browser URL bar (we don't want to
|
||||
* break the back button!).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAjaxProductSearchVariables()
|
||||
{
|
||||
$search = $this->getProductSearchVariables();
|
||||
|
||||
$rendered_products_top = $this->render('catalog/_partials/products-top', ['listing' => $search]);
|
||||
$rendered_products = $this->render('catalog/_partials/products', ['listing' => $search]);
|
||||
$rendered_products_bottom = $this->render('catalog/_partials/products-bottom', ['listing' => $search]);
|
||||
|
||||
$data = array_merge(
|
||||
[
|
||||
'rendered_products_top' => $rendered_products_top,
|
||||
'rendered_products' => $rendered_products,
|
||||
'rendered_products_bottom' => $rendered_products_bottom,
|
||||
],
|
||||
$search
|
||||
);
|
||||
|
||||
if (!empty($data['products']) && is_array($data['products'])) {
|
||||
$data['products'] = $this->prepareProductArrayForAjaxReturn($data['products']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans the products array with only whitelisted properties.
|
||||
*
|
||||
* @param array[] $products
|
||||
*
|
||||
* @return array[] Filtered product list
|
||||
*/
|
||||
protected function prepareProductArrayForAjaxReturn(array $products)
|
||||
{
|
||||
$filter = $this->get('prestashop.core.filter.front_end_object.search_result_product_collection');
|
||||
|
||||
return $filter->filter($products);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finally, the methods that wraps it all:.
|
||||
*
|
||||
* If we're doing AJAX, output a JSON of the necessary product search related
|
||||
* variables.
|
||||
*
|
||||
* If we're not doing AJAX, then render the whole page with the given template.
|
||||
*
|
||||
* @param string $template the template for this page
|
||||
*/
|
||||
protected function doProductSearch($template, $params = [], $locale = null)
|
||||
{
|
||||
if ($this->ajax) {
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
$this->ajaxRender(json_encode($this->getAjaxProductSearchVariables()));
|
||||
|
||||
return;
|
||||
} else {
|
||||
$variables = $this->getProductSearchVariables();
|
||||
$this->context->smarty->assign([
|
||||
'listing' => $variables,
|
||||
]);
|
||||
$this->setTemplate($template, $params, $locale);
|
||||
}
|
||||
}
|
||||
|
||||
abstract public function getListingLabel();
|
||||
|
||||
/**
|
||||
* Gets the product search query for the controller.
|
||||
* That is, the minimum contract with which search modules
|
||||
* must comply.
|
||||
*
|
||||
* @return ProductSearchQuery
|
||||
*/
|
||||
abstract protected function getProductSearchQuery();
|
||||
|
||||
/**
|
||||
* We cannot assume that modules will handle the query,
|
||||
* so we need a default implementation for the search provider.
|
||||
*
|
||||
* @return ProductSearchProviderInterface
|
||||
*/
|
||||
abstract protected function getDefaultProductSearchProvider();
|
||||
}
|
||||
42
classes/controller/ProductPresentingFrontController.php
Normal file
42
classes/controller/ProductPresentingFrontController.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Open Software License (OSL 3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/OSL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
||||
* versions in the future. If you wish to customize PrestaShop for your
|
||||
* needs please refer to https://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
abstract class ProductPresentingFrontControllerCore extends FrontController
|
||||
{
|
||||
private function getFactory()
|
||||
{
|
||||
return new ProductPresenterFactory($this->context, new TaxConfiguration());
|
||||
}
|
||||
|
||||
protected function getProductPresentationSettings()
|
||||
{
|
||||
return $this->getFactory()->getPresentationSettings();
|
||||
}
|
||||
|
||||
protected function getProductPresenter()
|
||||
{
|
||||
return $this->getFactory()->getPresenter();
|
||||
}
|
||||
}
|
||||
34
classes/controller/index.php
Normal file
34
classes/controller/index.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright since 2007 PrestaShop SA and Contributors
|
||||
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Open Software License (OSL 3.0)
|
||||
* that is bundled with this package in the file LICENSE.md.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* https://opensource.org/licenses/OSL-3.0
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
||||
* versions in the future. If you wish to customize PrestaShop for your
|
||||
* needs please refer to https://devdocs.prestashop.com/ for more information.
|
||||
*
|
||||
* @author PrestaShop SA and Contributors <contact@prestashop.com>
|
||||
* @copyright Since 2007 PrestaShop SA and Contributors
|
||||
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
|
||||
*/
|
||||
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;
|
||||
Reference in New Issue
Block a user