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,215 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property Profile $object
*/
class AdminAccessControllerCore extends AdminController
{
/** @var array : Black list of id_tab that do not have access */
public $accesses_black_list = [];
public function __construct()
{
$this->bootstrap = true;
$this->show_toolbar = false;
$this->table = 'access';
$this->className = 'Profile';
$this->multishop_context = Shop::CONTEXT_ALL;
$this->lang = false;
$this->context = Context::getContext();
// Blacklist AdminLogin
$this->accesses_black_list[] = Tab::getIdFromClassName('AdminLogin');
parent::__construct();
}
/**
* AdminController::renderForm() override.
*
* @see AdminController::renderForm()
*/
public function renderForm()
{
$current_profile = (int) $this->getCurrentProfileId();
$profiles = Profile::getProfiles($this->context->language->id);
$tabs = Tab::getTabs($this->context->language->id);
$accesses = [];
foreach ($profiles as $profile) {
$accesses[$profile['id_profile']] = Profile::getProfileAccesses($profile['id_profile']);
}
// Deleted id_tab that do not have access
foreach ($tabs as $key => $tab) {
// Don't allow permissions for unnamed tabs (ie. AdminLogin)
if (empty($tab['name'])) {
unset($tabs[$key]);
}
foreach ($this->accesses_black_list as $id_tab) {
if ($tab['id_tab'] == (int) $id_tab) {
unset($tabs[$key]);
}
}
}
$modules = [];
foreach ($profiles as $profile) {
$modules[$profile['id_profile']] = Module::getModulesAccessesByIdProfile($profile['id_profile']);
uasort($modules[$profile['id_profile']], [$this, 'sortModuleByName']);
}
$this->fields_form = [''];
$this->tpl_form_vars = [
'profiles' => $profiles,
'accesses' => $accesses,
'id_tab_parentmodule' => (int) Tab::getIdFromClassName('AdminParentModules'),
'id_tab_module' => (int) Tab::getIdFromClassName('AdminModules'),
'tabs' => $this->displayTabs($tabs),
'current_profile' => (int) $current_profile,
'admin_profile' => (int) _PS_ADMIN_PROFILE_,
'access_edit' => $this->access('edit'),
'perms' => ['view', 'add', 'edit', 'delete'],
'id_perms' => ['view' => 0, 'add' => 1, 'edit' => 2, 'delete' => 3, 'all' => 4],
'modules' => $modules,
'link' => $this->context->link,
'employee_profile_id' => (int) $this->context->employee->id_profile,
];
return parent::renderForm();
}
/**
* AdminController::initContent() override.
*
* @see AdminController::initContent()
*/
public function initContent()
{
$this->display = 'edit';
if (!$this->loadObject(true)) {
return;
}
$this->content .= $this->renderForm();
$this->context->smarty->assign([
'content' => $this->content,
]);
}
public function initToolbarTitle()
{
$this->toolbar_title = array_unique($this->breadcrumbs);
}
public function initPageHeaderToolbar()
{
parent::initPageHeaderToolbar();
unset($this->page_header_toolbar_btn['cancel']);
}
public function ajaxProcessUpdateAccess()
{
if (_PS_MODE_DEMO_) {
throw new PrestaShopException($this->trans('This functionality has been disabled.', [], 'Admin.Notifications.Error'));
}
if ($this->access('edit') != '1') {
throw new PrestaShopException($this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error'));
}
if (Tools::isSubmit('submitAddAccess')) {
$access = new Access();
$perm = Tools::getValue('perm');
if (!in_array($perm, ['view', 'add', 'edit', 'delete', 'all'])) {
throw new PrestaShopException('permission does not exist');
}
$enabled = (bool) Tools::getValue('enabled');
$id_tab = (int) Tools::getValue('id_tab');
$id_profile = (int) Tools::getValue('id_profile');
$addFromParent = (bool) Tools::getValue('addFromParent');
die($access->updateLgcAccess((int) $id_profile, $id_tab, $perm, $enabled, $addFromParent));
}
}
public function ajaxProcessUpdateModuleAccess()
{
if (_PS_MODE_DEMO_) {
throw new PrestaShopException($this->trans('This functionality has been disabled.', [], 'Admin.Notifications.Error'));
}
if ($this->access('edit') != '1') {
throw new PrestaShopException($this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error'));
}
if (Tools::isSubmit('changeModuleAccess')) {
$access = new Access();
$perm = Tools::getValue('perm');
$enabled = (bool) Tools::getValue('enabled');
$id_module = (int) Tools::getValue('id_module');
$id_profile = (int) Tools::getValue('id_profile');
if (!in_array($perm, ['view', 'configure', 'uninstall'])) {
throw new PrestaShopException('permission does not exist');
}
die($access->updateLgcModuleAccess((int) $id_profile, $id_module, $perm, $enabled));
}
}
/**
* Get the current profile id.
*
* @return int the $_GET['profile'] if valid, else 1 (the first profile id)
*/
public function getCurrentProfileId()
{
return (isset($_GET['id_profile']) && !empty($_GET['id_profile']) && is_numeric($_GET['id_profile'])) ? (int) $_GET['id_profile'] : 1;
}
/**
* @param array $a module data
* @param array $b module data
*
* @return int
*/
protected function sortModuleByName(array $a, array $b)
{
$moduleAName = isset($a['name']) ? $a['name'] : null;
$moduleBName = isset($b['name']) ? $b['name'] : null;
return strnatcmp($moduleAName, $moduleBName);
}
/**
* return human readable Tabs hierarchy for display.
*/
protected function displayTabs(array $tabs)
{
$tabsTree = $this->getChildrenTab($tabs);
return $tabsTree;
}
protected function getChildrenTab(array &$tabs, int $id_parent = 0)
{
$children = [];
foreach ($tabs as $tab) {
$id = $tab['id_tab'];
if ($tab['id_parent'] == $id_parent) {
$children[$id] = $tab;
$children[$id]['children'] = $this->getChildrenTab($tabs, $id);
}
}
return $children;
}
}

View File

@@ -0,0 +1,967 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property Carrier $object
*/
class AdminCarrierWizardControllerCore extends AdminController
{
protected $wizard_access;
/**
* @var Context
*/
public $old_context;
/**
* @var int
*/
public $type_context;
/**
* @var array<string, string|array<array<string, string>>>
*/
public $wizard_steps;
public function __construct()
{
$this->bootstrap = true;
$this->display = 'view';
$this->table = 'carrier';
$this->identifier = 'id_carrier';
$this->className = 'Carrier';
$this->lang = false;
$this->deleted = true;
$this->type_context = Shop::getContext();
$this->old_context = Context::getContext();
$this->multishop_context = Shop::CONTEXT_ALL;
$this->context = Context::getContext();
$this->fieldImageSettings = [
'name' => 'logo',
'dir' => 's',
];
parent::__construct();
$this->tabAccess = Profile::getProfileAccess($this->context->employee->id_profile, Tab::getIdFromClassName('AdminCarriers'));
}
public function setMedia($isNewTheme = false)
{
parent::setMedia($isNewTheme);
$this->addJqueryPlugin('smartWizard');
$this->addJqueryPlugin('typewatch');
$this->addJs(_PS_JS_DIR_ . 'admin/carrier_wizard.js');
}
public function initWizard()
{
$this->wizard_steps = [
'name' => 'carrier_wizard',
'steps' => [
[
'title' => $this->trans('General settings', [], 'Admin.Shipping.Feature'),
],
[
'title' => $this->trans('Shipping locations and costs', [], 'Admin.Shipping.Feature'),
],
[
'title' => $this->trans('Size, weight, and group access', [], 'Admin.Shipping.Feature'),
],
[
'title' => $this->trans('Summary', [], 'Admin.Global'),
], ],
];
if (Shop::isFeatureActive()) {
$multistore_step = [
[
'title' => $this->trans('MultiStore', [], 'Admin.Global'),
],
];
array_splice($this->wizard_steps['steps'], 1, 0, $multistore_step);
}
}
/**
* @return string|void
*
* @throws SmartyException
*/
public function renderView()
{
$this->initWizard();
if (Tools::getValue('id_carrier') && $this->access('edit')) {
/** @var Carrier $carrier */
$carrier = $this->loadObject();
} elseif ($this->access('add')) {
$carrier = new Carrier();
}
if (
(!$this->access('edit') && Tools::getValue('id_carrier'))
|| (!$this->access('add') && !Tools::getValue('id_carrier'))
|| !isset($carrier)
) {
$this->errors[] = $this->trans('You do not have permission to use this wizard.', [], 'Admin.Shipping.Notification');
return;
}
$currency = $this->getActualCurrency();
$this->tpl_view_vars = [
'currency_sign' => $currency->sign,
'PS_WEIGHT_UNIT' => Configuration::get('PS_WEIGHT_UNIT'),
'enableAllSteps' => Validate::isLoadedObject($carrier),
'wizard_steps' => $this->wizard_steps,
'validate_url' => $this->context->link->getAdminLink('AdminCarrierWizard'),
'carrierlist_url' => $this->context->link->getAdminLink('AdminCarriers') . '&conf=' . ((int) Validate::isLoadedObject($carrier) ? 4 : 3),
'multistore_enable' => Shop::isFeatureActive(),
'wizard_contents' => [
'contents' => [
0 => $this->renderStepOne($carrier),
1 => $this->renderStepThree($carrier),
2 => $this->renderStepFour($carrier),
3 => $this->renderStepFive($carrier),
],
],
'labels' => [
'next' => $this->trans('Next', [], 'Admin.Global'),
'previous' => $this->trans('Previous', [], 'Admin.Global'),
'finish' => $this->trans('Finish', [], 'Admin.Actions'), ],
];
if (Shop::isFeatureActive()) {
array_splice($this->tpl_view_vars['wizard_contents']['contents'], 1, 0, [0 => $this->renderStepTwo($carrier)]);
}
$this->context->smarty->assign([
'carrier_logo' => (Validate::isLoadedObject($carrier) && file_exists(_PS_SHIP_IMG_DIR_ . $carrier->id . '.jpg') ? _THEME_SHIP_DIR_ . $carrier->id . '.jpg' : false),
]);
$this->context->smarty->assign([
'logo_content' => $this->createTemplate('logo.tpl')->fetch(),
]);
$this->addjQueryPlugin(['ajaxfileupload']);
return parent::renderView();
}
public function initBreadcrumbs($tab_id = null, $tabs = null)
{
if (Tools::getValue('id_carrier')) {
$this->display = 'edit';
} else {
$this->display = 'add';
}
parent::initBreadcrumbs((int) Tab::getIdFromClassName('AdminCarriers'));
$this->display = 'view';
}
public function initPageHeaderToolbar()
{
parent::initPageHeaderToolbar();
$this->page_header_toolbar_btn['cancel'] = [
'href' => $this->context->link->getAdminLink('AdminCarriers'),
'desc' => $this->trans('Cancel', [], 'Admin.Actions'),
];
}
public function renderStepOne($carrier)
{
$this->fields_form = [
'form' => [
'id_form' => 'step_carrier_general',
'input' => [
[
'type' => 'text',
'label' => $this->trans('Carrier name', [], 'Admin.Shipping.Feature'),
'name' => 'name',
'required' => true,
'hint' => [
$this->trans('Allowed characters: letters, spaces and "%special_chars%".', ['%special_chars%' => '().-'], 'Admin.Shipping.Help'),
$this->trans('The carrier\'s name will be displayed during checkout.', [], 'Admin.Shipping.Help'),
$this->trans('For in-store pickup, enter 0 to replace the carrier name with your shop name.', [], 'Admin.Shipping.Help'),
],
],
[
'type' => 'text',
'label' => $this->trans('Transit time', [], 'Admin.Shipping.Feature'),
'name' => 'delay',
'lang' => true,
'required' => true,
'maxlength' => 512,
'hint' => $this->trans('The delivery time will be displayed during checkout.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'text',
'label' => $this->trans('Speed grade', [], 'Admin.Shipping.Feature'),
'name' => 'grade',
'required' => false,
'size' => 1,
'hint' => $this->trans('Enter "0" for a longest shipping delay, or "9" for the shortest shipping delay.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'logo',
'label' => $this->trans('Logo', [], 'Admin.Global'),
'name' => 'logo',
],
[
'type' => 'text',
'label' => $this->trans('Tracking URL', [], 'Admin.Shipping.Feature'),
'name' => 'url',
'hint' => $this->trans('Delivery tracking URL: Type \'@\' where the tracking number should appear. It will be automatically replaced by the tracking number.', [], 'Admin.Shipping.Help'),
'desc' => $this->trans('For example: \'http://example.com/track.php?num=@\' with \'@\' where the tracking number should appear.', [], 'Admin.Shipping.Help'),
],
],
],
];
$tpl_vars = ['max_image_size' => (int) Configuration::get('PS_PRODUCT_PICTURE_MAX_SIZE') / 1024 / 1024];
$fields_value = $this->getStepOneFieldsValues($carrier);
return $this->renderGenericForm(['form' => $this->fields_form], $fields_value, $tpl_vars);
}
public function renderStepTwo($carrier)
{
$this->fields_form = [
'form' => [
'id_form' => 'step_carrier_shops',
'force' => true,
'input' => [
[
'type' => 'shop',
'label' => $this->trans('Store association', [], 'Admin.Global'),
'name' => 'checkBoxShopAsso',
],
],
],
];
$fields_value = $this->getStepTwoFieldsValues($carrier);
return $this->renderGenericForm(['form' => $this->fields_form], $fields_value);
}
public function renderStepThree($carrier)
{
$this->fields_form = [
'form' => [
'id_form' => 'step_carrier_ranges',
'input' => [
'shipping_handling' => [
'type' => 'switch',
'label' => $this->trans('Add handling costs', [], 'Admin.Shipping.Feature'),
'name' => 'shipping_handling',
'required' => false,
'class' => 't',
'is_bool' => true,
'values' => [
[
'id' => 'shipping_handling_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'shipping_handling_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
'hint' => $this->trans('Include the handling costs (as set in Shipping > Preferences) in the final carrier price.', [], 'Admin.Shipping.Help'),
],
'is_free' => [
'type' => 'switch',
'label' => $this->trans('Free shipping', [], 'Admin.Shipping.Feature'),
'name' => 'is_free',
'required' => false,
'class' => 't',
'values' => [
[
'id' => 'is_free_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'is_free_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
],
'shipping_method' => [
'type' => 'radio',
'label' => $this->trans('Billing', [], 'Admin.Shipping.Feature'),
'name' => 'shipping_method',
'required' => false,
'class' => 't',
'br' => true,
'values' => [
[
'id' => 'billing_price',
'value' => Carrier::SHIPPING_METHOD_PRICE,
'label' => $this->trans('According to total price.', [], 'Admin.Shipping.Feature'),
],
[
'id' => 'billing_weight',
'value' => Carrier::SHIPPING_METHOD_WEIGHT,
'label' => $this->trans('According to total weight.', [], 'Admin.Shipping.Feature'),
],
],
],
'id_tax_rules_group' => [
'type' => 'select',
'label' => $this->trans('Tax', [], 'Admin.Global'),
'name' => 'id_tax_rules_group',
'options' => [
'query' => TaxRulesGroup::getTaxRulesGroups(true),
'id' => 'id_tax_rules_group',
'name' => 'name',
'default' => [
'label' => $this->trans('No tax', [], 'Admin.Global'),
'value' => 0,
],
],
],
'range_behavior' => [
'type' => 'select',
'label' => $this->trans('Out-of-range behavior', [], 'Admin.Shipping.Feature'),
'name' => 'range_behavior',
'options' => [
'query' => [
[
'id' => 0,
'name' => $this->trans('Apply the cost of the highest defined range', [], 'Admin.Shipping.Feature'),
],
[
'id' => 1,
'name' => $this->trans('Disable carrier', [], 'Admin.Shipping.Feature'),
],
],
'id' => 'id',
'name' => 'name',
],
'hint' => $this->trans('Out-of-range behavior occurs when no defined range matches the customer\'s cart (e.g. when the weight of the cart is greater than the highest weight limit defined by the weight ranges).', [], 'Admin.Shipping.Help'),
],
'zones' => [
'type' => 'zone',
'name' => 'zones',
],
],
],
];
if (Configuration::get('PS_ATCP_SHIPWRAP')) {
unset($this->fields_form['form']['input']['id_tax_rules_group']);
}
$tpl_vars = [];
$tpl_vars['PS_WEIGHT_UNIT'] = Configuration::get('PS_WEIGHT_UNIT');
$currency = $this->getActualCurrency();
$tpl_vars['currency_sign'] = $currency->sign;
$fields_value = $this->getStepThreeFieldsValues($carrier);
$this->getTplRangesVarsAndValues($carrier, $tpl_vars, $fields_value);
return $this->renderGenericForm(['form' => $this->fields_form], $fields_value, $tpl_vars);
}
/**
* @param Carrier $carrier
*
* @return string
*/
public function renderStepFour($carrier)
{
$this->fields_form = [
'form' => [
'id_form' => 'step_carrier_conf',
'input' => [
[
'type' => 'text',
'label' => $this->trans('Maximum package width (%s)', ['%s' => Configuration::get('PS_DIMENSION_UNIT')], 'Admin.Shipping.Feature'),
'name' => 'max_width',
'required' => false,
'hint' => $this->trans('Maximum width managed by this carrier. Set the value to "0", or leave this field blank to ignore.', [], 'Admin.Shipping.Help') . ' ' . $this->trans('The value must be an integer.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'text',
'label' => $this->trans('Maximum package height (%s)', ['%s' => Configuration::get('PS_DIMENSION_UNIT')], 'Admin.Shipping.Feature'),
'name' => 'max_height',
'required' => false,
'hint' => $this->trans('Maximum height managed by this carrier. Set the value to "0", or leave this field blank to ignore.', [], 'Admin.Shipping.Help') . ' ' . $this->trans('The value must be an integer.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'text',
'label' => $this->trans('Maximum package depth (%s)', ['%s' => Configuration::get('PS_DIMENSION_UNIT')], 'Admin.Shipping.Feature'),
'name' => 'max_depth',
'required' => false,
'hint' => $this->trans('Maximum depth managed by this carrier. Set the value to "0", or leave this field blank to ignore.', [], 'Admin.Shipping.Help') . ' ' . $this->trans('The value must be an integer.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'text',
'label' => $this->trans('Maximum package weight (%s)', ['%s' => Configuration::get('PS_WEIGHT_UNIT')], 'Admin.Shipping.Feature'),
'name' => 'max_weight',
'required' => false,
'hint' => $this->trans('Maximum weight managed by this carrier. Set the value to "0", or leave this field blank to ignore.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'group',
'label' => $this->trans('Group access', [], 'Admin.Shipping.Feature'),
'name' => 'groupBox',
'values' => Group::getGroups(Context::getContext()->language->id),
'hint' => $this->trans('Mark the groups that are allowed access to this carrier.', [], 'Admin.Shipping.Help'),
],
],
],
];
$fields_value = $this->getStepFourFieldsValues($carrier);
// Added values of object Group
$carrier_groups = $carrier->getGroups();
$carrier_groups_ids = [];
if (is_array($carrier_groups)) {
foreach ($carrier_groups as $carrier_group) {
$carrier_groups_ids[] = $carrier_group['id_group'];
}
}
$groups = Group::getGroups($this->context->language->id);
foreach ($groups as $group) {
$fields_value['groupBox_' . $group['id_group']] = Tools::getValue('groupBox_' . $group['id_group'], in_array($group['id_group'], $carrier_groups_ids) || empty($carrier_groups_ids) && !$carrier->id);
}
return $this->renderGenericForm(['form' => $this->fields_form], $fields_value);
}
public function renderStepFive($carrier)
{
$this->fields_form = [
'form' => [
'id_form' => 'step_carrier_summary',
'input' => [
[
'type' => 'switch',
'label' => $this->trans('Enabled', [], 'Admin.Global'),
'name' => 'active',
'required' => false,
'class' => 't',
'is_bool' => true,
'values' => [
[
'id' => 'active_on',
'value' => 1,
],
[
'id' => 'active_off',
'value' => 0,
],
],
'hint' => $this->trans('Enable the carrier in the front office.', [], 'Admin.Shipping.Help'),
],
],
],
];
$template = $this->createTemplate('controllers/carrier_wizard/summary.tpl');
$fields_value = $this->getStepFiveFieldsValues($carrier);
$active_form = $this->renderGenericForm(['form' => $this->fields_form], $fields_value);
$active_form = str_replace(['<fieldset id="fieldset_form">', '</fieldset>'], '', $active_form);
$template->assign('active_form', $active_form);
return $template->fetch();
}
/**
* @param Carrier $carrier
* @param array $tpl_vars
* @param array $fields_value
*/
protected function getTplRangesVarsAndValues(Carrier $carrier, array &$tpl_vars, array &$fields_value)
{
$tpl_vars['zones'] = Zone::getZones(false, true);
$carrier_zones = $carrier->getZones();
$carrier_zones_ids = [];
if (is_array($carrier_zones)) {
foreach ($carrier_zones as $carrier_zone) {
$carrier_zones_ids[] = $carrier_zone['id_zone'];
}
}
if ($fields_value['zones'] === false) {
$fields_value['zones'] = [];
}
$range_table = $carrier->getRangeTable();
$shipping_method = $carrier->getShippingMethod();
$zones = Zone::getZones(false);
foreach ($zones as $zone) {
$fields_value['zones'][$zone['id_zone']] = Tools::getValue('zone_' . $zone['id_zone'], in_array($zone['id_zone'], $carrier_zones_ids));
}
if ($shipping_method == Carrier::SHIPPING_METHOD_FREE) {
$range_obj = $carrier->getRangeObject($carrier->shipping_method);
$price_by_range = [];
} else {
$range_obj = $carrier->getRangeObject();
$price_by_range = Carrier::getDeliveryPriceByRanges($range_table, (int) $carrier->id);
}
foreach ($price_by_range as $price) {
$tpl_vars['price_by_range'][$price['id_' . $range_table]][$price['id_zone']] = $price['price'];
}
$tmp_range = $range_obj->getRanges((int) $carrier->id);
$tpl_vars['ranges'] = [];
if ($shipping_method != Carrier::SHIPPING_METHOD_FREE) {
foreach ($tmp_range as $id => $range) {
$tpl_vars['ranges'][$range['id_' . $range_table]] = $range;
$tpl_vars['ranges'][$range['id_' . $range_table]]['id_range'] = $range['id_' . $range_table];
}
}
// init blank range
if (!count($tpl_vars['ranges'])) {
$tpl_vars['ranges'][] = ['id_range' => 0, 'delimiter1' => 0, 'delimiter2' => 0];
}
}
public function renderGenericForm($fields_form, $fields_value, $tpl_vars = [])
{
$helper = new HelperForm();
$helper->show_toolbar = false;
$helper->table = $this->table;
$lang = new Language((int) Configuration::get('PS_LANG_DEFAULT'));
$helper->default_form_language = $lang->id;
$helper->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') ? Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') : 0;
$this->fields_form = [];
$helper->id = (int) Tools::getValue('id_carrier');
$helper->identifier = $this->identifier;
$helper->tpl_vars = array_merge([
'fields_value' => $fields_value,
'languages' => $this->getLanguages(),
'id_language' => $this->context->language->id,
], $tpl_vars);
$helper->override_folder = 'carrier_wizard/';
return $helper->generateForm($fields_form);
}
public function getStepOneFieldsValues($carrier)
{
return [
'id_carrier' => $this->getFieldValue($carrier, 'id_carrier'),
'name' => $this->getFieldValue($carrier, 'name'),
'delay' => $this->getFieldValue($carrier, 'delay'),
'grade' => $this->getFieldValue($carrier, 'grade'),
'url' => $this->getFieldValue($carrier, 'url'),
];
}
public function getStepTwoFieldsValues($carrier)
{
return ['shop' => $this->getFieldValue($carrier, 'shop')];
}
public function getStepThreeFieldsValues($carrier)
{
$id_tax_rules_group = (is_object($this->object) && !$this->object->id) ? Carrier::getIdTaxRulesGroupMostUsed() : Carrier::getIdTaxRulesGroupByIdCarrier($this->object->id);
$shipping_handling = (is_object($this->object) && !$this->object->id) ? 0 : $this->getFieldValue($carrier, 'shipping_handling');
return [
'is_free' => $this->getFieldValue($carrier, 'is_free'),
'id_tax_rules_group' => (int) $id_tax_rules_group,
'shipping_handling' => $shipping_handling,
'shipping_method' => $this->getFieldValue($carrier, 'shipping_method'),
'range_behavior' => $this->getFieldValue($carrier, 'range_behavior'),
'zones' => $this->getFieldValue($carrier, 'zones'),
];
}
public function getStepFourFieldsValues($carrier)
{
return [
'range_behavior' => $this->getFieldValue($carrier, 'range_behavior'),
'max_height' => $this->getFieldValue($carrier, 'max_height'),
'max_width' => $this->getFieldValue($carrier, 'max_width'),
'max_depth' => $this->getFieldValue($carrier, 'max_depth'),
'max_weight' => $this->getFieldValue($carrier, 'max_weight'),
'group' => $this->getFieldValue($carrier, 'group'),
];
}
public function getStepFiveFieldsValues($carrier)
{
return ['active' => $this->getFieldValue($carrier, 'active')];
}
public function ajaxProcessChangeRanges()
{
if ((Validate::isLoadedObject($this->object) && !$this->access('edit')) || !$this->access('add')) {
$this->errors[] = $this->trans('You do not have permission to use this wizard.', [], 'Admin.Shipping.Notification');
return;
}
if ((!(int) $shipping_method = Tools::getValue('shipping_method')) || !in_array($shipping_method, [Carrier::SHIPPING_METHOD_PRICE, Carrier::SHIPPING_METHOD_WEIGHT])) {
return;
}
/** @var Carrier $carrier */
$carrier = $this->loadObject(true);
$carrier->shipping_method = $shipping_method;
$tpl_vars = [];
$fields_value = $this->getStepThreeFieldsValues($carrier);
$this->getTplRangesVarsAndValues($carrier, $tpl_vars, $fields_value);
$template = $this->createTemplate('controllers/carrier_wizard/helpers/form/form_ranges.tpl');
$template->assign($tpl_vars);
$template->assign('change_ranges', 1);
$template->assign('fields_value', $fields_value);
$template->assign('input', ['type' => 'zone', 'name' => 'zones']);
$currency = $this->getActualCurrency();
$template->assign('currency_sign', $currency->sign);
$template->assign('PS_WEIGHT_UNIT', Configuration::get('PS_WEIGHT_UNIT'));
die($template->fetch());
}
protected function validateForm(bool $die = true)
{
$step_number = (int) Tools::getValue('step_number');
$return = ['has_error' => false];
if (!$this->access('edit')) {
$this->errors[] = $this->trans('You do not have permission to use this wizard.', [], 'Admin.Shipping.Notification');
} else {
if (Shop::isFeatureActive() && $step_number == 2) {
if (!Tools::getValue('checkBoxShopAsso_carrier')) {
$return['has_error'] = true;
$return['errors'][] = $this->trans('You must choose at least one shop or group shop.', [], 'Admin.Shipping.Notification');
}
} else {
$this->validateRules();
}
}
if (count($this->errors)) {
$return['has_error'] = true;
$return['errors'] = $this->errors;
}
if (count($this->errors) || $die) {
die(json_encode($return));
}
}
public function ajaxProcessValidateStep()
{
$this->validateForm(true);
}
public function processRanges($id_carrier)
{
if (!$this->access('edit') || !$this->access('add')) {
$this->errors[] = $this->trans('You do not have permission to use this wizard.', [], 'Admin.Shipping.Notification');
return;
}
$carrier = new Carrier((int) $id_carrier);
if (!Validate::isLoadedObject($carrier)) {
return false;
}
$range_inf = Tools::getValue('range_inf');
$range_sup = Tools::getValue('range_sup');
$range_type = Tools::getValue('shipping_method');
$fees = Tools::getValue('fees');
$carrier->deleteDeliveryPrice($carrier->getRangeTable());
if ($range_type != Carrier::SHIPPING_METHOD_FREE) {
foreach ($range_inf as $key => $delimiter1) {
if (!isset($range_sup[$key])) {
continue;
}
$range = $carrier->getRangeObject((int) $range_type);
$range->id_carrier = (int) $carrier->id;
$range->delimiter1 = (float) $delimiter1;
$range->delimiter2 = (float) $range_sup[$key];
$range->save();
if (!Validate::isLoadedObject($range)) {
return false;
}
$price_list = [];
if (is_array($fees) && count($fees)) {
foreach ($fees as $id_zone => $fee) {
$price_list[] = [
'id_range_price' => ($range_type == Carrier::SHIPPING_METHOD_PRICE ? (int) $range->id : null),
'id_range_weight' => ($range_type == Carrier::SHIPPING_METHOD_WEIGHT ? (int) $range->id : null),
'id_carrier' => (int) $carrier->id,
'id_zone' => (int) $id_zone,
'price' => isset($fee[$key]) ? (float) str_replace(',', '.', $fee[$key]) : 0,
];
}
}
if (count($price_list) && !$carrier->addDeliveryPrice($price_list, true)) {
return false;
}
}
}
return true;
}
public function ajaxProcessUploadLogo()
{
if (!$this->access('edit')) {
die('<return result="error" message="' . $this->trans('You do not have permission to use this wizard.', [], 'Admin.Shipping.Notification') . '" />');
}
$logo = ($_FILES['carrier_logo_input'] ?? false);
if ($logo
&& !empty($logo['tmp_name'])
&& $logo['tmp_name'] != 'none'
&& (!isset($logo['error']) || !$logo['error'])
&& ImageManager::isCorrectImageFileExt($logo['name'])
&& is_uploaded_file($logo['tmp_name'])
&& ImageManager::isRealImage($logo['tmp_name'], $logo['type'])
) {
$file = $logo['tmp_name'];
do {
$tmp_name = uniqid() . '.jpg';
} while (file_exists(_PS_TMP_IMG_DIR_ . $tmp_name));
if (!ImageManager::resize($file, _PS_TMP_IMG_DIR_ . $tmp_name)) {
die('<return result="error" message="Impossible to resize the image into ' . Tools::safeOutput(_PS_TMP_IMG_DIR_) . '" />');
}
@unlink($file);
die('<return result="success" message="' . Tools::safeOutput(_PS_TMP_IMG_ . $tmp_name) . '" />');
}
die('<return result="error" message="Cannot upload file" />');
}
public function ajaxProcessFinishStep()
{
$return = ['has_error' => false];
if (!$this->access('edit')) {
$return = [
'has_error' => true,
$return['errors'][] = $this->trans('You do not have permission to use this wizard.', [], 'Admin.Shipping.Notification'),
];
} else {
$this->validateForm(false);
if ($id_carrier = Tools::getValue('id_carrier')) {
$current_carrier = new Carrier((int) $id_carrier);
// if update we duplicate current Carrier
/** @var Carrier $new_carrier */
$new_carrier = $current_carrier->duplicateObject();
if (Validate::isLoadedObject($new_carrier)) {
// Set flag deteled to true for historization
$current_carrier->deleted = true;
$current_carrier->update();
// Fill the new carrier object
$this->copyFromPost($new_carrier, $this->table);
$new_carrier->position = $current_carrier->position;
$new_carrier->update();
$this->updateAssoShop((int) $new_carrier->id);
$this->duplicateLogo((int) $new_carrier->id, (int) $current_carrier->id);
$this->changeGroups((int) $new_carrier->id);
// Copy default carrier
if (Configuration::get('PS_CARRIER_DEFAULT') == $current_carrier->id) {
Configuration::updateValue('PS_CARRIER_DEFAULT', (int) $new_carrier->id);
}
// Call of hooks
Hook::exec('actionCarrierUpdate', [
'id_carrier' => (int) $current_carrier->id,
'carrier' => $new_carrier,
]);
$this->postImage($new_carrier->id);
$this->changeZones($new_carrier->id);
$new_carrier->setTaxRulesGroup((int) Tools::getValue('id_tax_rules_group'));
$carrier = $new_carrier;
}
} else {
$carrier = new Carrier();
$this->copyFromPost($carrier, $this->table);
if (!$carrier->add()) {
$return['has_error'] = true;
$return['errors'][] = $this->trans('An error occurred while saving this carrier.', [], 'Admin.Shipping.Notification');
}
}
if (isset($carrier)) {
if ($carrier->is_free) {
// if carrier is free delete shipping cost
$carrier->deleteDeliveryPrice('range_weight');
$carrier->deleteDeliveryPrice('range_price');
}
if (Validate::isLoadedObject($carrier)) {
if (!$this->changeGroups((int) $carrier->id)) {
$return['has_error'] = true;
$return['errors'][] = $this->trans('An error occurred while saving carrier groups.', [], 'Admin.Shipping.Notification');
}
if (!$this->changeZones((int) $carrier->id)) {
$return['has_error'] = true;
$return['errors'][] = $this->trans('An error occurred while saving carrier zones.', [], 'Admin.Shipping.Notification');
}
if (!$carrier->is_free) {
if (!$this->processRanges((int) $carrier->id)) {
$return['has_error'] = true;
$return['errors'][] = $this->trans('An error occurred while saving carrier ranges.', [], 'Admin.Shipping.Notification');
}
}
if (Shop::isFeatureActive() && !$this->updateAssoShop((int) $carrier->id)) {
$return['has_error'] = true;
$return['errors'][] = $this->trans('An error occurred while saving associations of shops.', [], 'Admin.Shipping.Notification');
}
if (!$carrier->setTaxRulesGroup((int) Tools::getValue('id_tax_rules_group'))) {
$return['has_error'] = true;
$return['errors'][] = $this->trans('An error occurred while saving the tax rules group.', [], 'Admin.Shipping.Notification');
}
if (Tools::getValue('logo')) {
if (Tools::getValue('logo') == 'null' && file_exists(_PS_SHIP_IMG_DIR_ . $carrier->id . '.jpg')) {
unlink(_PS_SHIP_IMG_DIR_ . $carrier->id . '.jpg');
} else {
$logo = basename(Tools::getValue('logo'));
if (!file_exists(_PS_TMP_IMG_DIR_ . $logo) || !copy(_PS_TMP_IMG_DIR_ . $logo, _PS_SHIP_IMG_DIR_ . $carrier->id . '.jpg')) {
$return['has_error'] = true;
$return['errors'][] = $this->trans('An error occurred while saving carrier logo.', [], 'Admin.Shipping.Notification');
}
}
}
$return['id_carrier'] = $carrier->id;
}
}
}
die(json_encode($return));
}
protected function changeGroups(int $id_carrier, bool $delete = true)
{
$carrier = new Carrier((int) $id_carrier);
if (!Validate::isLoadedObject($carrier)) {
return false;
}
return $carrier->setGroups(Tools::getValue('groupBox'));
}
public function changeZones($id)
{
$return = true;
$carrier = new Carrier($id);
if (!Validate::isLoadedObject($carrier)) {
die($this->trans('The object cannot be loaded.', [], 'Admin.Notifications.Error'));
}
$zones = Zone::getZones(false);
foreach ($zones as $zone) {
if (count($carrier->getZone($zone['id_zone']))) {
if (!isset($_POST['zone_' . $zone['id_zone']]) || !$_POST['zone_' . $zone['id_zone']]) {
$return &= $carrier->deleteZone((int) $zone['id_zone']);
}
} elseif (isset($_POST['zone_' . $zone['id_zone']]) && $_POST['zone_' . $zone['id_zone']]) {
$return &= $carrier->addZone((int) $zone['id_zone']);
}
}
return $return;
}
public function getValidationRules()
{
$step_number = (int) Tools::getValue('step_number');
if (!$step_number) {
return;
}
if ($step_number == 4 && !Shop::isFeatureActive() || $step_number == 5 && Shop::isFeatureActive()) {
return ['fields' => []];
}
$step_fields = [
1 => ['name', 'delay', 'grade', 'url'],
2 => ['is_free', 'id_tax_rules_group', 'shipping_handling', 'shipping_method', 'range_behavior'],
3 => ['range_behavior', 'max_height', 'max_width', 'max_depth', 'max_weight'],
4 => [],
];
if (Shop::isFeatureActive()) {
$tmp = $step_fields;
$step_fields = array_slice($tmp, 0, 1, true) + [2 => ['shop']];
$step_fields[3] = $tmp[2];
$step_fields[4] = $tmp[3];
}
$definition = ObjectModel::getDefinition('Carrier');
foreach ($definition['fields'] as $field => $def) {
if (is_array($step_fields[$step_number]) && !in_array($field, $step_fields[$step_number])) {
unset($definition['fields'][$field]);
}
}
return $definition;
}
public function duplicateLogo($new_id, $old_id)
{
$old_logo = _PS_SHIP_IMG_DIR_ . '/' . (int) $old_id . '.jpg';
if (file_exists($old_logo)) {
copy($old_logo, _PS_SHIP_IMG_DIR_ . '/' . (int) $new_id . '.jpg');
}
$old_tmp_logo = _PS_TMP_IMG_DIR_ . '/carrier_mini_' . (int) $old_id . '.jpg';
if (file_exists($old_tmp_logo)) {
if (!isset($_FILES['logo'])) {
copy($old_tmp_logo, _PS_TMP_IMG_DIR_ . '/carrier_mini_' . $new_id . '.jpg');
}
unlink($old_tmp_logo);
}
}
public function getActualCurrency()
{
if ($this->type_context == Shop::CONTEXT_SHOP) {
Shop::setContext($this->type_context, $this->old_context->shop->id);
} elseif ($this->type_context == Shop::CONTEXT_GROUP) {
Shop::setContext($this->type_context, $this->old_context->shop->id_shop_group);
}
$currency = Currency::getDefaultCurrency();
Shop::setContext(Shop::CONTEXT_ALL);
return $currency;
}
}

View File

@@ -0,0 +1,708 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property Carrier $object
*/
class AdminCarriersControllerCore extends AdminController
{
/** @var string */
protected $position_identifier = 'id_carrier';
public function __construct()
{
if ($id_carrier = Tools::getValue('id_carrier') && !Tools::isSubmit('deletecarrier') && !Tools::isSubmit('statuscarrier') && !Tools::isSubmit('isFreecarrier')) {
Tools::redirectAdmin(Context::getContext()->link->getAdminLink('AdminCarrierWizard', true, [], ['id_carrier' => (int) $id_carrier]));
}
$this->bootstrap = true;
$this->table = 'carrier';
$this->className = 'Carrier';
$this->lang = false;
$this->deleted = true;
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->_defaultOrderBy = 'position';
parent::__construct();
$this->bulk_actions = [
'delete' => [
'text' => $this->trans('Delete selected', [], 'Admin.Notifications.Info'),
'confirm' => $this->trans('Delete selected items?', [], 'Admin.Notifications.Info'),
'icon' => 'icon-trash',
],
];
$this->fieldImageSettings = [
'name' => 'logo',
'dir' => 's',
];
$this->fields_list = [
'id_carrier' => [
'title' => $this->trans('ID', [], 'Admin.Global'),
'align' => 'center',
'class' => 'fixed-width-xs',
],
'name' => [
'title' => $this->trans('Name', [], 'Admin.Global'),
],
'image' => [
'title' => $this->trans('Logo', [], 'Admin.Global'),
'align' => 'center',
'image' => 's',
'class' => 'fixed-width-xs',
'orderby' => false,
'search' => false,
],
'delay' => [
'title' => $this->trans('Delay', [], 'Admin.Shipping.Feature'),
'orderby' => false,
],
'active' => [
'title' => $this->trans('Status', [], 'Admin.Global'),
'align' => 'center',
'active' => 'status',
'type' => 'bool',
'class' => 'fixed-width-sm',
'orderby' => false,
],
'is_free' => [
'title' => $this->trans('Free Shipping', [], 'Admin.Shipping.Feature'),
'align' => 'center',
'active' => 'isFree',
'type' => 'bool',
'class' => 'fixed-width-sm',
'orderby' => false,
],
'position' => [
'title' => $this->trans('Position', [], 'Admin.Global'),
'filter_key' => 'a!position',
'align' => 'center',
'class' => 'fixed-width-sm',
'position' => 'position',
],
];
}
/**
* Extends the renderOptions method to add a notice about migration to Symfony.
*/
public function renderOptions()
{
// Get original options, probably none in case of this controller
$renderedOptions = parent::renderOptions();
// Add a notice about the new carrier page
$renderedOptions .= sprintf(
'<div class="alert alert-info"><p>%s</p><p>%s</p></div>',
$this->trans(
'[1]A new, improved version of this page is ready.[/1] You can enable it with a single click in the [2]New & Experimental Features[/2] section.',
[
'[1]' => '<strong>',
'[/1]' => '</strong>',
'[2]' => '<a href="' . $this->context->link->getAdminLink('AdminFeatureFlag') . '">',
'[/2]' => '</a>',
],
'Admin.Shipping.Feature'
),
$this->trans(
'For compatibility reasons, the original version remains enabled on your shop. If no incompatible modules are holding you back, feel free to switch to the new version. The legacy version will eventually be removed in a future release.',
[],
'Admin.Shipping.Feature'
)
);
return $renderedOptions;
}
public function initToolbar()
{
parent::initToolbar();
if (isset($this->toolbar_btn['new']) && $this->display != 'view') {
$this->toolbar_btn['new']['href'] = $this->context->link->getAdminLink('AdminCarrierWizard');
}
}
public function initPageHeaderToolbar()
{
$this->page_header_toolbar_title = $this->trans('Carriers', [], 'Admin.Shipping.Feature');
if ($this->display != 'view') {
$this->page_header_toolbar_btn['new_carrier'] = [
'href' => $this->context->link->getAdminLink('AdminCarrierWizard'),
'desc' => $this->trans('Add new carrier', [], 'Admin.Shipping.Feature'),
'icon' => 'process-icon-new',
];
}
parent::initPageHeaderToolbar();
}
public function renderList()
{
$this->_select = 'b.*';
$this->_join = 'INNER JOIN `' . _DB_PREFIX_ . 'carrier_lang` b ON a.id_carrier = b.id_carrier' . Shop::addSqlRestrictionOnLang('b') . ' AND b.id_lang = ' . (int) $this->context->language->id . ' LEFT JOIN `' . _DB_PREFIX_ . 'carrier_tax_rules_group_shop` ctrgs ON (a.`id_carrier` = ctrgs.`id_carrier` AND ctrgs.id_shop=' . (int) $this->context->shop->id . ')';
$this->_use_found_rows = false;
// test if need to show header alert.
$this->context->smarty->assign([
'showHeaderAlert' => (Db::getInstance()->executeS(
'SELECT COUNT(1) FROM `' . _DB_PREFIX_ . 'carrier` WHERE deleted = 0 AND id_reference > 2',
false
)->fetchColumn(0) == 0),
]);
return parent::renderList();
}
/**
* @return string|void
*
* @throws SmartyException
*/
public function renderForm()
{
$this->fields_form = [
'legend' => [
'title' => $this->trans('Carriers', [], 'Admin.Shipping.Feature'),
'icon' => 'icon-truck',
],
'input' => [
[
'type' => 'text',
'label' => $this->trans('Company', [], 'Admin.Global'),
'name' => 'name',
'required' => true,
'hint' => [
$this->trans('Allowed characters: letters, spaces and "%special_chars%".', ['%special_chars%' => '().-'], 'Admin.Shipping.Help'),
$this->trans('Carrier name displayed during checkout', [], 'Admin.Shipping.Help'),
$this->trans('For in-store pickup, enter 0 to replace the carrier name with your shop name.', [], 'Admin.Shipping.Help'),
],
],
[
'type' => 'file',
'label' => $this->trans('Logo', [], 'Admin.Global'),
'name' => 'logo',
'hint' => $this->trans('Upload a logo from your computer.', [], 'Admin.Shipping.Help') .
' (.gif, .jpg, .jpeg, .webp ' .
$this->trans('or', [], 'Admin.Shipping.Help') . ' .png)',
],
[
'type' => 'text',
'label' => $this->trans('Transit time', [], 'Admin.Shipping.Feature'),
'name' => 'delay',
'lang' => true,
'required' => true,
'maxlength' => 512,
'hint' => $this->trans('Estimated delivery time will be displayed during checkout.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'text',
'label' => $this->trans('Speed grade', [], 'Admin.Shipping.Feature'),
'name' => 'grade',
'required' => false,
'hint' => $this->trans('Enter "0" for a longest shipping delay, or "9" for the shortest shipping delay.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'text',
'label' => $this->trans('URL', [], 'Admin.Global'),
'name' => 'url',
'hint' => $this->trans('Delivery tracking URL: Type \'@\' where the tracking number should appear. It will then be automatically replaced by the tracking number.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'checkbox',
'label' => $this->trans('Zone', [], 'Admin.Global'),
'name' => 'zone',
'values' => [
'query' => Zone::getZones(false),
'id' => 'id_zone',
'name' => 'name',
],
'hint' => $this->trans('The zones in which this carrier will be used.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'group',
'label' => $this->trans('Group access', [], 'Admin.Shipping.Help'),
'name' => 'groupBox',
'values' => Group::getGroups(Context::getContext()->language->id),
'hint' => $this->trans('Mark the groups that are allowed access to this carrier.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'switch',
'label' => $this->trans('Status', [], 'Admin.Global'),
'name' => 'active',
'required' => false,
'class' => 't',
'is_bool' => true,
'values' => [
[
'id' => 'active_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'active_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
'hint' => $this->trans('Enable the carrier in the front office.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'switch',
'label' => $this->trans('Apply shipping cost', [], 'Admin.Shipping.Feature'),
'name' => 'is_free',
'required' => false,
'class' => 't',
'values' => [
[
'id' => 'is_free_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'is_free_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
'hint' => $this->trans('Apply both regular shipping cost and product-specific shipping costs.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'select',
'label' => $this->trans('Tax', [], 'Admin.Global'),
'name' => 'id_tax_rules_group',
'options' => [
'query' => TaxRulesGroup::getTaxRulesGroups(true),
'id' => 'id_tax_rules_group',
'name' => 'name',
'default' => [
'label' => $this->trans('No Tax', [], 'Admin.Global'),
'value' => 0,
],
],
],
[
'type' => 'switch',
'label' => $this->trans('Shipping and handling', [], 'Admin.Shipping.Feature'),
'name' => 'shipping_handling',
'required' => false,
'class' => 't',
'is_bool' => true,
'values' => [
[
'id' => 'shipping_handling_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'shipping_handling_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
'hint' => $this->trans('Include the shipping and handling costs in the carrier price.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'radio',
'label' => $this->trans('Billing', [], 'Admin.Shipping.Feature'),
'name' => 'shipping_method',
'required' => false,
'class' => 't',
'br' => true,
'values' => [
[
'id' => 'billing_default',
'value' => Carrier::SHIPPING_METHOD_DEFAULT,
'label' => $this->trans('Default behavior', [], 'Admin.Shipping.Feature'),
],
[
'id' => 'billing_price',
'value' => Carrier::SHIPPING_METHOD_PRICE,
'label' => $this->trans('According to total price', [], 'Admin.Shipping.Feature'),
],
[
'id' => 'billing_weight',
'value' => Carrier::SHIPPING_METHOD_WEIGHT,
'label' => $this->trans('According to total weight', [], 'Admin.Shipping.Feature'),
],
],
],
[
'type' => 'select',
'label' => $this->trans('Out-of-range behavior', [], 'Admin.Shipping.Feature'),
'name' => 'range_behavior',
'options' => [
'query' => [
[
'id' => 0,
'name' => $this->trans('Apply the cost of the highest defined range', [], 'Admin.Shipping.Help'),
],
[
'id' => 1,
'name' => $this->trans('Disable carrier', [], 'Admin.Shipping.Feature'),
],
],
'id' => 'id',
'name' => 'name',
],
'hint' => $this->trans('Out-of-range behavior occurs when none is defined (e.g. when a customer\'s cart weight is greater than the highest range limit).', [], 'Admin.Shipping.Help'),
],
[
'type' => 'text',
'label' => $this->trans('Maximum package height', [], 'Admin.Shipping.Feature'),
'name' => 'max_height',
'required' => false,
'hint' => $this->trans('Maximum height managed by this carrier. Set the value to "0," or leave this field blank to ignore.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'text',
'label' => $this->trans('Maximum package width', [], 'Admin.Shipping.Feature'),
'name' => 'max_width',
'required' => false,
'hint' => $this->trans('Maximum width managed by this carrier. Set the value to "0," or leave this field blank to ignore.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'text',
'label' => $this->trans('Maximum package depth', [], 'Admin.Shipping.Feature'),
'name' => 'max_depth',
'required' => false,
'hint' => $this->trans('Maximum depth managed by this carrier. Set the value to "0," or leave this field blank to ignore.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'text',
'label' => $this->trans('Maximum package weight', [], 'Admin.Shipping.Feature'),
'name' => 'max_weight',
'required' => false,
'hint' => $this->trans('Maximum weight managed by this carrier. Set the value to "0," or leave this field blank to ignore.', [], 'Admin.Shipping.Help'),
],
[
'type' => 'hidden',
'name' => 'is_module',
],
[
'type' => 'hidden',
'name' => 'external_module_name',
],
[
'type' => 'hidden',
'name' => 'shipping_external',
],
[
'type' => 'hidden',
'name' => 'need_range',
],
],
];
if (Shop::isFeatureActive()) {
$this->fields_form['input'][] = [
'type' => 'shop',
'label' => $this->trans('Store association', [], 'Admin.Global'),
'name' => 'checkBoxShopAsso',
];
}
$this->fields_form['submit'] = [
'title' => $this->trans('Save', [], 'Admin.Actions'),
];
if (!($obj = $this->loadObject(true))) {
return;
}
$this->getFieldsValues($obj);
return parent::renderForm();
}
public function postProcess()
{
if (Tools::getValue('submitAdd' . $this->table)) {
/* Checking fields validity */
$this->validateRules();
if (!count($this->errors)) {
$id = (int) Tools::getValue('id_' . $this->table);
/* Object update */
if (!empty($id)) {
try {
if ($this->access('edit')) {
$current_carrier = new Carrier($id);
if (!Validate::isLoadedObject($current_carrier)) {
throw new PrestaShopException('Cannot load Carrier object');
}
/** @var Carrier $new_carrier */
// Duplicate current Carrier
$new_carrier = $current_carrier->duplicateObject();
if (Validate::isLoadedObject($new_carrier)) {
// Set flag deteled to true for historization
$current_carrier->deleted = true;
$current_carrier->update();
// Fill the new carrier object
$this->copyFromPost($new_carrier, $this->table);
$new_carrier->position = $current_carrier->position;
$new_carrier->update();
$this->updateAssoShop($new_carrier->id);
$new_carrier->copyCarrierData((int) $current_carrier->id);
$this->changeGroups($new_carrier->id);
// Call of hooks
Hook::exec('actionCarrierUpdate', [
'id_carrier' => (int) $current_carrier->id,
'carrier' => $new_carrier,
]);
$this->postImage($new_carrier->id);
$this->changeZones($new_carrier->id);
$new_carrier->setTaxRulesGroup((int) Tools::getValue('id_tax_rules_group'));
Tools::redirectAdmin(self::$currentIndex . '&id_' . $this->table . '=' . $current_carrier->id . '&conf=4&token=' . $this->token);
} else {
$this->errors[] = $this->trans('An error occurred while updating an object.', [], 'Admin.Notifications.Error') . ' <b>' . $this->table . '</b>';
}
} else {
$this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error');
}
} catch (PrestaShopException $e) {
$this->errors[] = $e->getMessage();
}
} else {
// Object creation
if ($this->access('add')) {
// Create new Carrier
$carrier = new Carrier();
$this->copyFromPost($carrier, $this->table);
$carrier->position = Carrier::getHigherPosition() + 1;
if ($carrier->add()) {
if (($_POST['id_' . $this->table] = $carrier->id /* voluntary */) && $this->postImage($carrier->id) && $this->_redirect) {
$carrier->setTaxRulesGroup((int) Tools::getValue('id_tax_rules_group'), true);
$this->changeZones($carrier->id);
$this->changeGroups($carrier->id);
$this->updateAssoShop($carrier->id);
Tools::redirectAdmin(self::$currentIndex . '&id_' . $this->table . '=' . $carrier->id . '&conf=3&token=' . $this->token);
}
} else {
$this->errors[] = $this->trans('An error occurred while creating an object.', [], 'Admin.Notifications.Error') . ' <b>' . $this->table . '</b>';
}
} else {
$this->errors[] = $this->trans('You do not have permission to add this.', [], 'Admin.Notifications.Error');
}
}
}
parent::postProcess();
} elseif (isset($_GET['isFree' . $this->table])) {
if (!$this->access('edit')) {
$this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error');
return;
}
$this->processIsFree();
} else {
parent::postProcess();
Carrier::cleanPositions();
}
}
public function processIsFree()
{
$carrier = new Carrier($this->id_object);
if (!Validate::isLoadedObject($carrier)) {
$this->errors[] = $this->trans('An error occurred while updating carrier information.', [], 'Admin.Shipping.Notification');
}
$carrier->is_free = !$carrier->is_free;
if (!$carrier->update()) {
$this->errors[] = $this->trans('An error occurred while updating carrier information.', [], 'Admin.Shipping.Notification');
}
Tools::redirectAdmin(self::$currentIndex . '&conf=5&token=' . $this->token);
}
/**
* Overload the property $fields_value.
*
* @param object $obj
*/
public function getFieldsValues($obj)
{
if ($this->getFieldValue($obj, 'is_module')) {
$this->fields_value['is_module'] = 1;
}
if ($this->getFieldValue($obj, 'shipping_external')) {
$this->fields_value['shipping_external'] = 1;
}
if ($this->getFieldValue($obj, 'need_range')) {
$this->fields_value['need_range'] = 1;
}
// Added values of object Zone
$carrier_zones = $obj->getZones();
$carrier_zones_ids = [];
if (is_array($carrier_zones)) {
foreach ($carrier_zones as $carrier_zone) {
$carrier_zones_ids[] = $carrier_zone['id_zone'];
}
}
$zones = Zone::getZones(false);
foreach ($zones as $zone) {
$this->fields_value['zone_' . $zone['id_zone']] = Tools::getValue('zone_' . $zone['id_zone'], in_array($zone['id_zone'], $carrier_zones_ids));
}
// Added values of object Group
$carrier_groups = $obj->getGroups();
$carrier_groups_ids = [];
if (is_array($carrier_groups)) {
foreach ($carrier_groups as $carrier_group) {
$carrier_groups_ids[] = $carrier_group['id_group'];
}
}
$groups = Group::getGroups($this->context->language->id);
foreach ($groups as $group) {
$this->fields_value['groupBox_' . $group['id_group']] = Tools::getValue('groupBox_' . $group['id_group'], in_array($group['id_group'], $carrier_groups_ids) || empty($carrier_groups_ids) && !$obj->id);
}
$this->fields_value['id_tax_rules_group'] = $this->object->getIdTaxRulesGroup($this->context);
}
/**
* @param Carrier $object
*
* @return bool
*/
protected function beforeDelete($object)
{
return (bool) $object->isUsed();
}
protected function changeGroups(int $id_carrier, bool $delete = true)
{
if ($delete) {
Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'carrier_group WHERE id_carrier = ' . (int) $id_carrier);
}
$groups = Db::getInstance()->executeS('SELECT id_group FROM `' . _DB_PREFIX_ . 'group`');
foreach ($groups as $group) {
if (Tools::getIsset('groupBox') && in_array($group['id_group'], Tools::getValue('groupBox'))) {
Db::getInstance()->execute('
INSERT INTO ' . _DB_PREFIX_ . 'carrier_group (id_group, id_carrier)
VALUES(' . (int) $group['id_group'] . ',' . (int) $id_carrier . ')
');
}
}
}
public function changeZones($id)
{
/** @var Carrier $carrier */
$carrier = new $this->className($id);
if (!Validate::isLoadedObject($carrier)) {
die($this->trans('The object cannot be loaded.', [], 'Admin.Notifications.Error'));
}
$zones = Zone::getZones(false);
foreach ($zones as $zone) {
if (count($carrier->getZone($zone['id_zone']))) {
if (!isset($_POST['zone_' . $zone['id_zone']]) || !$_POST['zone_' . $zone['id_zone']]) {
$carrier->deleteZone($zone['id_zone']);
}
} elseif (isset($_POST['zone_' . $zone['id_zone']]) && $_POST['zone_' . $zone['id_zone']]) {
$carrier->addZone($zone['id_zone']);
}
}
}
public function ajaxProcessUpdatePositions()
{
$way = (bool) Tools::getValue('way');
$id_carrier = (int) Tools::getValue('id');
$positions = Tools::getValue($this->table);
foreach ($positions as $position => $value) {
$pos = explode('_', $value);
if (isset($pos[2]) && (int) $pos[2] === $id_carrier) {
$carrier = new Carrier((int) $pos[2]);
if (Validate::isLoadedObject($carrier)) {
if (isset($position) && $carrier->updatePosition($way, $position)) {
echo 'ok position ' . (int) $position . ' for carrier ' . (int) $pos[1] . '\r\n';
} else {
echo '{"hasError" : true, "errors" : "Can not update carrier ' . (int) $id_carrier . ' to position ' . (int) $position . ' "}';
}
} else {
echo '{"hasError" : true, "errors" : "This carrier (' . (int) $id_carrier . ') can t be loaded"}';
}
break;
}
}
}
public function displayEditLink($token, $id, $name = null)
{
if ($this->access('edit')) {
$tpl = $this->createTemplate('helpers/list/list_action_edit.tpl');
if (!array_key_exists('Edit', self::$cache_lang)) {
self::$cache_lang['Edit'] = $this->trans('Edit', [], 'Admin.Actions');
}
$tpl->assign([
'href' => $this->context->link->getAdminLink('AdminCarrierWizard', true, [], ['id_carrier' => (int) $id]),
'action' => self::$cache_lang['Edit'],
'id' => $id,
]);
return $tpl->fetch();
} else {
return;
}
}
public function displayDeleteLink($token, $id, $name = null)
{
if ($this->access('delete')) {
$tpl = $this->createTemplate('helpers/list/list_action_delete.tpl');
if (!array_key_exists('Delete', self::$cache_lang)) {
self::$cache_lang['Delete'] = $this->trans('Delete', [], 'Admin.Actions');
}
if (!array_key_exists('DeleteItem', self::$cache_lang)) {
self::$cache_lang['DeleteItem'] = $this->trans('Delete selected item?', [], 'Admin.Notifications.Info');
}
if (!array_key_exists('Name', self::$cache_lang)) {
self::$cache_lang['Name'] = $this->trans('Name:', [], 'Admin.Shipping.Feature');
}
if (null !== $name) {
$name = '\n\n' . self::$cache_lang['Name'] . ' ' . $name;
}
$data = [
$this->identifier => $id,
'href' => $this->context->link->getAdminLink('AdminCarriers', true, [], ['id_carrier' => (int) $id, 'deletecarrier' => 1]),
'action' => self::$cache_lang['Delete'],
];
if ($this->specificConfirmDelete !== false) {
$data['confirm'] = null !== $this->specificConfirmDelete ? '\r' . $this->specificConfirmDelete : addcslashes(Tools::htmlentitiesDecodeUTF8(self::$cache_lang['DeleteItem'] . $name), '\'');
}
$tpl->assign(array_merge($this->tpl_delete_link_vars, $data));
return $tpl->fetch();
} else {
return;
}
}
}

View File

@@ -0,0 +1,877 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property CartRule $object
*/
class AdminCartRulesControllerCore extends AdminController
{
public function __construct()
{
$this->bootstrap = true;
$this->table = 'cart_rule';
$this->className = 'CartRule';
$this->lang = true;
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->_orderWay = 'DESC';
parent::__construct();
$this->bulk_actions = [
'delete' => [
'text' => $this->trans('Delete selected', [], 'Admin.Actions'),
'icon' => 'icon-trash',
'confirm' => $this->trans('Delete selected items?', [], 'Admin.Notifications.Warning'),
],
];
$this->fields_list = [
'id_cart_rule' => ['title' => $this->trans('ID', [], 'Admin.Global'), 'align' => 'center', 'class' => 'fixed-width-xs'],
'name' => ['title' => $this->trans('Name', [], 'Admin.Global')],
'priority' => ['title' => $this->trans('Priority', [], 'Admin.Global'), 'align' => 'center', 'class' => 'fixed-width-xs'],
'code' => ['title' => $this->trans('Code', [], 'Admin.Global'), 'class' => 'fixed-width-sm'],
'quantity' => ['title' => $this->trans('Quantity', [], 'Admin.Catalog.Feature'), 'align' => 'center', 'class' => 'fixed-width-xs'],
'date_to' => ['title' => $this->trans('Expiration date', [], 'Admin.Catalog.Feature'), 'type' => 'datetime', 'class' => 'fixed-width-lg'],
'active' => ['title' => $this->trans('Status', [], 'Admin.Global'), 'active' => 'status', 'type' => 'bool', 'align' => 'center', 'class' => 'fixed-width-xs', 'orderby' => false],
];
}
public function ajaxProcessLoadCartRules()
{
if (!$this->access('view')) {
return die(json_encode(['error' => 'You do not have the right permission']));
}
$type = $token = $search = '';
$limit = $count = $id_cart_rule = 0;
if (Tools::getIsset('limit')) {
$limit = Tools::getValue('limit');
}
if (Tools::getIsset('type')) {
$type = Tools::getValue('type');
}
if (Tools::getIsset('count')) {
$count = Tools::getValue('count');
}
if (Tools::getIsset('id_cart_rule')) {
$id_cart_rule = Tools::getValue('id_cart_rule');
}
if (Tools::getIsset('search')) {
$search = Tools::getValue('search');
}
$page = (int) floor($count / $limit);
$html = '';
$next_link = '';
if (($page * $limit) + 1 == $count || $count == 0) {
if ($count == 0) {
$count = 1;
}
/** @var CartRule $current_object */
$current_object = $this->loadObject(true);
$cart_rules = $current_object->getAssociatedRestrictions('cart_rule', false, true, $page * $limit, $limit, $search);
if ($type == 'selected') {
$i = 1;
foreach ($cart_rules['selected'] as $cart_rule) {
$html .= '<option value="' . (int) $cart_rule['id_cart_rule'] . '">&nbsp;' . Tools::safeOutput($cart_rule['name']) . '</option>';
if ($i == $limit) {
break;
}
++$i;
}
if ($i == $limit) {
$next_link = Context::getContext()->link->getAdminLink('AdminCartRules') . '&ajaxMode=1&ajax=1&id_cart_rule=' . (int) $id_cart_rule . '&action=loadCartRules&limit=' . (int) $limit . '&type=selected&count=' . ($count - 1 + count($cart_rules['selected']) . '&search=' . urlencode($search));
}
} else {
$i = 1;
foreach ($cart_rules['unselected'] as $cart_rule) {
$html .= '<option value="' . (int) $cart_rule['id_cart_rule'] . '">&nbsp;' . Tools::safeOutput($cart_rule['name']) . '</option>';
if ($i == $limit) {
break;
}
++$i;
}
if ($i == $limit) {
$next_link = Context::getContext()->link->getAdminLink('AdminCartRules') . '&ajaxMode=1&ajax=1&id_cart_rule=' . (int) $id_cart_rule . '&action=loadCartRules&limit=' . (int) $limit . '&type=unselected&count=' . ($count - 1 + count($cart_rules['unselected']) . '&search=' . urlencode($search));
}
}
}
echo json_encode(['html' => $html, 'next_link' => $next_link]);
}
public function setMedia($isNewTheme = false)
{
parent::setMedia($isNewTheme);
$this->addJqueryPlugin(['typewatch', 'fancybox', 'autocomplete']);
}
public function initPageHeaderToolbar()
{
if (empty($this->display)) {
$this->page_header_toolbar_btn['new_cart_rule'] = [
'href' => self::$currentIndex . '&addcart_rule&token=' . $this->token,
'desc' => $this->trans('Add new cart rule', [], 'Admin.Catalog.Feature'),
'icon' => 'process-icon-new',
];
}
parent::initPageHeaderToolbar();
}
public function postProcess()
{
if (Tools::isSubmit('submitAddcart_rule') || Tools::isSubmit('submitAddcart_ruleAndStay')) {
// If the reduction is associated to a specific product, then it must be part of the product restrictions
if ((int) Tools::getValue('reduction_product') && Tools::getValue('apply_discount_to') == 'specific' && Tools::getValue('apply_discount') != 'off') {
$reduction_product = (int) Tools::getValue('reduction_product');
// First, check if it is not already part of the restrictions
$already_restricted = false;
if (is_array($rule_group_array = Tools::getValue('product_rule_group')) && count($rule_group_array) && Tools::getValue('product_restriction')) {
foreach ($rule_group_array as $rule_group_id) {
if (is_array($rule_array = Tools::getValue('product_rule_' . $rule_group_id)) && count($rule_array)) {
foreach ($rule_array as $rule_id) {
if (Tools::getValue('product_rule_' . $rule_group_id . '_' . $rule_id . '_type') == 'products'
&& in_array($reduction_product, Tools::getValue('product_rule_select_' . $rule_group_id . '_' . $rule_id))) {
$already_restricted = true;
break 2;
}
}
}
}
}
if ($already_restricted == false) {
// Check the product restriction
$_POST['product_restriction'] = 1;
// Add a new rule group
$rule_group_id = 1;
if (is_array($rule_group_array)) {
// Find the first rule_group_id that is not available in the array
while (in_array($rule_group_id, $rule_group_array)) {
++$rule_group_id;
}
$_POST['product_rule_group'][] = $rule_group_id;
} else {
$_POST['product_rule_group'] = [$rule_group_id];
}
// Set a quantity of 1 for this new rule group
$_POST['product_rule_group_' . $rule_group_id . '_quantity'] = 1;
// Add one rule to the new rule group
$_POST['product_rule_' . $rule_group_id] = [1];
// Set a type 'product' for this 1 rule
$_POST['product_rule_' . $rule_group_id . '_1_type'] = 'products';
// Add the product in the selected products
$_POST['product_rule_select_' . $rule_group_id . '_1'] = [$reduction_product];
}
}
// These are checkboxes (which aren't sent through POST when they are not checked), so they are forced to 0
foreach (['country', 'carrier', 'group', 'cart_rule', 'product', 'shop'] as $type) {
if (!Tools::getValue($type . '_restriction')) {
$_POST[$type . '_restriction'] = 0;
}
}
// If the restriction is checked, but no item is selected, raise an error
foreach (['country', 'carrier', 'group', 'shop'] as $type) {
if (Tools::getValue($type . '_restriction') && empty(Tools::getValue($type . '_select'))) {
switch ($type) {
case 'country':
$restriction_name = $this->trans('Country selection', [], 'Admin.Catalog.Feature');
break;
case 'carrier':
$restriction_name = $this->trans('Carrier selection', [], 'Admin.Catalog.Feature');
break;
case 'group':
$restriction_name = $this->trans('Customer group selection', [], 'Admin.Catalog.Feature');
break;
case 'shop':
default:
$restriction_name = $this->trans('Store selection', [], 'Admin.Catalog.Feature');
break;
}
$this->errors[] = $this->trans('The "%s" restriction is checked, but no item is selected.', [$restriction_name], 'Admin.Catalog.Notification');
}
}
// Remove the gift if the radio button is set to "no"
if (!(int) Tools::getValue('free_gift')) {
$_POST['gift_product'] = 0;
}
// Retrieve the product attribute id of the gift (if available)
if ($id_product = (int) Tools::getValue('gift_product')) {
$_POST['gift_product_attribute'] = (int) Tools::getValue('ipa_' . $id_product);
}
// Do not allow products with required customization
if (!empty($_POST['gift_product']) && count(Product::getRequiredCustomizableFieldsStatic((int) $_POST['gift_product']))) {
$this->errors[] = $this->trans('Product with required customization fields cannot be used as a gift.', [], 'Admin.Catalog.Notification');
}
// Idiot-proof control
if (strtotime(Tools::getValue('date_from')) > strtotime(Tools::getValue('date_to'))) {
$this->errors[] = $this->trans('The voucher cannot end before it begins.', [], 'Admin.Catalog.Notification');
}
if ((int) Tools::getValue('minimum_amount') < 0) {
$this->errors[] = $this->trans('The minimum amount cannot be lower than zero.', [], 'Admin.Catalog.Notification');
}
if ((float) Tools::getValue('reduction_percent') < 0 || (float) Tools::getValue('reduction_percent') > 100) {
$this->errors[] = $this->trans('Reduction percentage must be between 0% and 100%', [], 'Admin.Catalog.Notification');
}
if ((int) Tools::getValue('reduction_amount') < 0) {
$this->errors[] = $this->trans('Reduction amount cannot be lower than zero.', [], 'Admin.Catalog.Notification');
}
if (Tools::getValue('code') && ($same_code = (int) CartRule::getIdByCode(Tools::getValue('code'))) && $same_code != Tools::getValue('id_cart_rule')) {
$this->errors[] = $this->trans('This cart rule code is already used (conflict with cart rule %rulename%)', ['%rulename%' => $same_code], 'Admin.Catalog.Notification');
}
if (Tools::getValue('apply_discount') == 'off' && !Tools::getValue('free_shipping') && !Tools::getValue('free_gift')) {
$this->errors[] = $this->trans('An action is required for this cart rule.', [], 'Admin.Catalog.Notification');
}
}
return parent::postProcess();
}
public function processDelete()
{
$res = parent::processDelete();
if (Tools::isSubmit('delete' . $this->table)) {
$back = rawurldecode(Tools::getValue('back', ''));
if (!empty($back)) {
$back .= (strpos($back, '?') === false ? '?' : '&') . 'conf=1';
$this->redirect_after = $back;
}
}
return $res;
}
/**
* @param CartRule $current_object
*
* @return bool|void
*
* @throws PrestaShopDatabaseException
*/
protected function afterUpdate($current_object)
{
// All the associations are deleted for an update, then recreated when we call the "afterAdd" method
$id_cart_rule = Tools::getValue('id_cart_rule');
foreach (['country', 'carrier', 'group', 'product_rule_group', 'shop'] as $type) {
Db::getInstance()->delete('cart_rule_' . $type, '`id_cart_rule` = ' . (int) $id_cart_rule);
}
Db::getInstance()->delete('cart_rule_product_rule', 'NOT EXISTS (SELECT 1 FROM `' . _DB_PREFIX_ . 'cart_rule_product_rule_group`
WHERE `' . _DB_PREFIX_ . 'cart_rule_product_rule`.`id_product_rule_group` = `' . _DB_PREFIX_ . 'cart_rule_product_rule_group`.`id_product_rule_group`)');
Db::getInstance()->delete('cart_rule_product_rule_value', 'NOT EXISTS (SELECT 1 FROM `' . _DB_PREFIX_ . 'cart_rule_product_rule`
WHERE `' . _DB_PREFIX_ . 'cart_rule_product_rule_value`.`id_product_rule` = `' . _DB_PREFIX_ . 'cart_rule_product_rule`.`id_product_rule`)');
Db::getInstance()->delete('cart_rule_combination', '`id_cart_rule_1` = ' . (int) $id_cart_rule . ' OR `id_cart_rule_2` = ' . (int) $id_cart_rule);
$this->afterAdd($current_object);
}
public function processAdd()
{
if ($cart_rule = parent::processAdd()) {
$this->context->smarty->assign('new_cart_rule', $cart_rule);
}
if (Tools::getValue('submitFormAjax')) {
$this->redirect_after = null;
if ($cart_rule) {
$this->context->smarty->assign('refresh_cart', true);
$this->display = 'edit';
}
}
return $cart_rule;
}
/**
* @TODO Move this function into CartRule
*
* @param CartRule $currentObject
*
* @return bool|void
*
* @throws PrestaShopDatabaseException
*/
protected function afterAdd($currentObject)
{
// Add shop restrictions if employee has not access to all shops
$context = Context::getContext();
$all_shops = Shop::getCompleteListOfShopsID();
if ($context->employee->isSuperAdmin()) {
$employee_shops = $all_shops;
} else {
$employee_shops = $context->employee->getAssociatedShops();
}
if (count($all_shops) > count($employee_shops) && Tools::getValue('shop_restriction') == '0') {
$values = [];
foreach ($employee_shops as $id) {
$values[] = '(' . (int) $currentObject->id . ',' . (int) $id . ')';
}
Db::getInstance()->execute('INSERT INTO `' . _DB_PREFIX_ . 'cart_rule_shop` (`id_cart_rule`, `id_shop`) VALUES ' . implode(',', $values));
}
// Add restrictions for generic entities like country, carrier and group
foreach (['country', 'carrier', 'group', 'shop'] as $type) {
if (Tools::getValue($type . '_restriction') && is_array($array = Tools::getValue($type . '_select')) && count($array)) {
$values = [];
foreach ($array as $id) {
$values[] = '(' . (int) $currentObject->id . ',' . (int) $id . ')';
}
Db::getInstance()->execute('INSERT INTO `' . _DB_PREFIX_ . 'cart_rule_' . $type . '` (`id_cart_rule`, `id_' . $type . '`) VALUES ' . implode(',', $values));
}
}
// Add cart rule restrictions
if (Tools::getValue('cart_rule_restriction') && is_array($array = Tools::getValue('cart_rule_select')) && count($array)) {
$values = [];
foreach ($array as $id) {
$values[] = '(' . (int) $currentObject->id . ',' . (int) $id . ')';
}
Db::getInstance()->execute('INSERT INTO `' . _DB_PREFIX_ . 'cart_rule_combination` (`id_cart_rule_1`, `id_cart_rule_2`) VALUES ' . implode(',', $values));
}
// Add product rule restrictions
if (Tools::getValue('product_restriction') && is_array($ruleGroupArray = Tools::getValue('product_rule_group')) && count($ruleGroupArray)) {
foreach ($ruleGroupArray as $ruleGroupId) {
Db::getInstance()->execute('INSERT INTO `' . _DB_PREFIX_ . 'cart_rule_product_rule_group` (`id_cart_rule`, `quantity`)
VALUES (' . (int) $currentObject->id . ', ' . (int) Tools::getValue('product_rule_group_' . $ruleGroupId . '_quantity') . ')');
$id_product_rule_group = Db::getInstance()->Insert_ID();
if (is_array($ruleArray = Tools::getValue('product_rule_' . $ruleGroupId)) && count($ruleArray)) {
foreach ($ruleArray as $ruleId) {
Db::getInstance()->execute('INSERT INTO `' . _DB_PREFIX_ . 'cart_rule_product_rule` (`id_product_rule_group`, `type`)
VALUES (' . (int) $id_product_rule_group . ', "' . pSQL(Tools::getValue('product_rule_' . $ruleGroupId . '_' . $ruleId . '_type')) . '")');
$id_product_rule = Db::getInstance()->Insert_ID();
$values = [];
foreach (Tools::getValue('product_rule_select_' . $ruleGroupId . '_' . $ruleId) as $id) {
$values[] = '(' . (int) $id_product_rule . ',' . (int) $id . ')';
}
$values = array_unique($values);
if (count($values)) {
Db::getInstance()->execute('INSERT INTO `' . _DB_PREFIX_ . 'cart_rule_product_rule_value` (`id_product_rule`, `id_item`) VALUES ' . implode(',', $values));
}
}
}
}
}
// If the new rule has no cart rule restriction, then it must be added to the white list of the other cart rules that have restrictions
if (!Tools::getValue('cart_rule_restriction')) {
Db::getInstance()->execute('
INSERT INTO `' . _DB_PREFIX_ . 'cart_rule_combination` (`id_cart_rule_1`, `id_cart_rule_2`) (
SELECT id_cart_rule, ' . (int) $currentObject->id . ' FROM `' . _DB_PREFIX_ . 'cart_rule` WHERE cart_rule_restriction = 1
)');
} else {
// And if the new cart rule has restrictions, previously unrestricted cart rules may now be restricted (a mug of coffee is strongly advised to understand this sentence)
$ruleCombinations = Db::getInstance()->executeS('
SELECT cr.id_cart_rule
FROM ' . _DB_PREFIX_ . 'cart_rule cr
WHERE cr.id_cart_rule != ' . (int) $currentObject->id . '
AND cr.cart_rule_restriction = 0
AND NOT EXISTS (
SELECT 1
FROM ' . _DB_PREFIX_ . 'cart_rule_combination
WHERE cr.id_cart_rule = ' . _DB_PREFIX_ . 'cart_rule_combination.id_cart_rule_2 AND ' . (int) $currentObject->id . ' = id_cart_rule_1
)
AND NOT EXISTS (
SELECT 1
FROM ' . _DB_PREFIX_ . 'cart_rule_combination
WHERE cr.id_cart_rule = ' . _DB_PREFIX_ . 'cart_rule_combination.id_cart_rule_1 AND ' . (int) $currentObject->id . ' = id_cart_rule_2
)
');
foreach ($ruleCombinations as $incompatibleRule) {
Db::getInstance()->execute('UPDATE `' . _DB_PREFIX_ . 'cart_rule` SET cart_rule_restriction = 1 WHERE id_cart_rule = ' . (int) $incompatibleRule['id_cart_rule'] . ' LIMIT 1');
Db::getInstance()->execute('
INSERT IGNORE INTO `' . _DB_PREFIX_ . 'cart_rule_combination` (`id_cart_rule_1`, `id_cart_rule_2`) (
SELECT id_cart_rule, ' . (int) $incompatibleRule['id_cart_rule'] . ' FROM `' . _DB_PREFIX_ . 'cart_rule`
WHERE active = 1
AND id_cart_rule != ' . (int) $currentObject->id . '
AND id_cart_rule != ' . (int) $incompatibleRule['id_cart_rule'] . '
)');
}
}
}
/**
* Retrieve the cart rule product rule groups in the POST data
* if available, and in the database if there is none.
*
* @param CartRule $cart_rule
*
* @return array
*/
public function getProductRuleGroupsDisplay($cart_rule)
{
$productRuleGroupsArray = [];
if (Tools::getValue('product_restriction') && is_array($array = Tools::getValue('product_rule_group')) && count($array)) {
$i = 1;
foreach ($array as $ruleGroupId) {
$productRulesArray = [];
$arrayPRGroupId = Tools::getValue('product_rule_' . $ruleGroupId);
if (is_array($arrayPRGroupId) && count($arrayPRGroupId)) {
foreach ($arrayPRGroupId as $ruleId) {
$productRulesArray[] = $this->getProductRuleDisplay(
$ruleGroupId,
$ruleId,
Tools::getValue('product_rule_' . $ruleGroupId . '_' . $ruleId . '_type'),
Tools::getValue('product_rule_select_' . $ruleGroupId . '_' . $ruleId)
);
}
}
$productRuleGroupsArray[] = $this->getProductRuleGroupDisplay(
$i++,
(int) Tools::getValue('product_rule_group_' . $ruleGroupId . '_quantity'),
$productRulesArray
);
}
} else {
$i = 1;
foreach ($cart_rule->getProductRuleGroups() as $productRuleGroup) {
$j = 1;
$productRulesDisplay = [];
foreach ($productRuleGroup['product_rules'] as $productRule) {
$productRulesDisplay[] = $this->getProductRuleDisplay($i, $j++, $productRule['type'], $productRule['values']);
}
$productRuleGroupsArray[] = $this->getProductRuleGroupDisplay($i++, $productRuleGroup['quantity'], $productRulesDisplay);
}
}
return $productRuleGroupsArray;
}
/* Return the form for a single cart rule group either with or without product_rules set up */
public function getProductRuleGroupDisplay($product_rule_group_id, $product_rule_group_quantity = 1, $product_rules = null)
{
Context::getContext()->smarty->assign('product_rule_group_id', $product_rule_group_id);
Context::getContext()->smarty->assign('product_rule_group_quantity', $product_rule_group_quantity);
Context::getContext()->smarty->assign('product_rules', $product_rules);
return $this->createTemplate('product_rule_group.tpl')->fetch();
}
public function getProductRuleDisplay($product_rule_group_id, $product_rule_id, $product_rule_type, $selected = [])
{
Context::getContext()->smarty->assign(
[
'product_rule_group_id' => (int) $product_rule_group_id,
'product_rule_id' => (int) $product_rule_id,
'product_rule_type' => $product_rule_type,
]
);
switch ($product_rule_type) {
case 'attributes':
$attributes = ['selected' => [], 'unselected' => []];
$results = Db::getInstance()->executeS('
SELECT CONCAT(agl.name, " - ", al.name) as name, a.id_attribute as id
FROM ' . _DB_PREFIX_ . 'attribute_group_lang agl
LEFT JOIN ' . _DB_PREFIX_ . 'attribute a ON a.id_attribute_group = agl.id_attribute_group
LEFT JOIN ' . _DB_PREFIX_ . 'attribute_lang al ON (a.id_attribute = al.id_attribute AND al.id_lang = ' . (int) Context::getContext()->language->id . ')
WHERE agl.id_lang = ' . (int) Context::getContext()->language->id . '
ORDER BY agl.name, al.name');
foreach ($results as $row) {
$attributes[in_array($row['id'], $selected) ? 'selected' : 'unselected'][] = $row;
}
Context::getContext()->smarty->assign('product_rule_itemlist', $attributes);
$choose_content = $this->createTemplate('controllers/cart_rules/product_rule_itemlist.tpl')->fetch();
Context::getContext()->smarty->assign('product_rule_choose_content', $choose_content);
break;
case 'products':
$products = ['selected' => [], 'unselected' => []];
$results = Db::getInstance()->executeS('
SELECT DISTINCT name, p.id_product as id
FROM ' . _DB_PREFIX_ . 'product p
LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl
ON (p.`id_product` = pl.`id_product`
AND pl.`id_lang` = ' . (int) Context::getContext()->language->id . Shop::addSqlRestrictionOnLang('pl') . ')
' . Shop::addSqlAssociation('product', 'p') . '
WHERE id_lang = ' . (int) Context::getContext()->language->id . '
ORDER BY name');
foreach ($results as $row) {
$products[in_array($row['id'], $selected) ? 'selected' : 'unselected'][] = $row;
}
Context::getContext()->smarty->assign('product_rule_itemlist', $products);
$choose_content = $this->createTemplate('product_rule_itemlist.tpl')->fetch();
Context::getContext()->smarty->assign('product_rule_choose_content', $choose_content);
break;
case 'manufacturers':
$products = ['selected' => [], 'unselected' => []];
$results = Db::getInstance()->executeS('
SELECT name, id_manufacturer as id
FROM ' . _DB_PREFIX_ . 'manufacturer
ORDER BY name');
foreach ($results as $row) {
$products[in_array($row['id'], $selected) ? 'selected' : 'unselected'][] = $row;
}
Context::getContext()->smarty->assign('product_rule_itemlist', $products);
$choose_content = $this->createTemplate('product_rule_itemlist.tpl')->fetch();
Context::getContext()->smarty->assign('product_rule_choose_content', $choose_content);
break;
case 'suppliers':
$products = ['selected' => [], 'unselected' => []];
$results = Db::getInstance()->executeS('
SELECT name, id_supplier as id
FROM ' . _DB_PREFIX_ . 'supplier
ORDER BY name');
foreach ($results as $row) {
$products[in_array($row['id'], $selected) ? 'selected' : 'unselected'][] = $row;
}
Context::getContext()->smarty->assign('product_rule_itemlist', $products);
$choose_content = $this->createTemplate('product_rule_itemlist.tpl')->fetch();
Context::getContext()->smarty->assign('product_rule_choose_content', $choose_content);
break;
case 'categories':
$categories = ['selected' => [], 'unselected' => []];
$categoryTree = Category::getNestedCategories(Category::getRootCategory()->id, (int) Context::getContext()->language->id, false);
$flatCategories = $this->populateCategories([], $categoryTree);
foreach ($flatCategories as $row) {
$categories[in_array($row['id'], $selected) ? 'selected' : 'unselected'][] = $row;
}
Context::getContext()->smarty->assign('product_rule_itemlist', $categories);
$choose_content = $this->createTemplate('product_rule_itemlist.tpl')->fetch();
Context::getContext()->smarty->assign('product_rule_choose_content', $choose_content);
break;
default:
Context::getContext()->smarty->assign('product_rule_itemlist', ['selected' => [], 'unselected' => []]);
Context::getContext()->smarty->assign('product_rule_choose_content', '');
}
return $this->createTemplate('product_rule.tpl')->fetch();
}
public function populateCategories(array $flatCategories, array $currentCategoryTree, string $currentPath = ''): array
{
if (!$currentCategoryTree) {
return $flatCategories;
}
$separator = ' > ';
foreach ($currentCategoryTree as $categoryArray) {
$fullName = ($currentPath ? $currentPath . $separator : '') . $categoryArray['name'];
$flatCategories[] = ['id' => $categoryArray['id_category'], 'name' => $fullName];
// recursive call for childrens
if (!empty($categoryArray['children'])) {
$flatCategories = $this->populateCategories($flatCategories, $categoryArray['children'], $fullName);
}
}
return $flatCategories;
}
public function ajaxProcess()
{
if (Tools::isSubmit('newProductRule')) {
die($this->getProductRuleDisplay(Tools::getValue('product_rule_group_id'), Tools::getValue('product_rule_id'), Tools::getValue('product_rule_type')));
}
if (Tools::isSubmit('newProductRuleGroup') && $product_rule_group_id = Tools::getValue('product_rule_group_id')) {
die($this->getProductRuleGroupDisplay($product_rule_group_id, Tools::getValue('product_rule_group_' . $product_rule_group_id . '_quantity', 1)));
}
if (Tools::isSubmit('customerFilter')) {
$display_shop_name = Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_SHOP;
$search_query = trim(Tools::getValue('q'));
$customers = Db::getInstance()->executeS('
SELECT c.`id_customer`, c.`email`, CONCAT(c.`firstname`, \' \', c.`lastname`) as cname' .
($display_shop_name ? ', s.name as shop_name' : '') . '
FROM `' . _DB_PREFIX_ . 'customer` c ' .
($display_shop_name ? ' INNER JOIN `' . _DB_PREFIX_ . 'shop` s ON s.id_shop = c.id_shop' : '') . '
WHERE c.`deleted` = 0 AND c.is_guest = 0 AND c.active = 1
AND (
c.`id_customer` = ' . (int) $search_query . '
OR c.`email` LIKE "%' . pSQL($search_query) . '%"
OR c.`firstname` LIKE "%' . pSQL($search_query) . '%"
OR c.`lastname` LIKE "%' . pSQL($search_query) . '%"
)
' . Shop::addSqlRestriction(Shop::SHARE_CUSTOMER, 'c') . '
ORDER BY c.`firstname`, c.`lastname`
LIMIT 50');
die(json_encode($customers));
}
// Both product filter (free product and product discount) search for products
if (Tools::isSubmit('giftProductFilter') || Tools::isSubmit('reductionProductFilter')) {
$products = Product::searchByName(Context::getContext()->language->id, trim(Tools::getValue('q')));
die(json_encode($products));
}
}
protected function searchProducts(string $searchString)
{
if ($products = Product::searchByName((int) $this->context->language->id, $searchString)) {
foreach ($products as &$product) {
$combinations = [];
$productObj = new Product((int) $product['id_product'], false, (int) $this->context->language->id);
$attributes = $productObj->getAttributesGroups((int) $this->context->language->id);
$product['formatted_price'] = $product['price_tax_incl']
? $this->context->getCurrentLocale()->formatPrice(Tools::convertPrice($product['price_tax_incl'], $this->context->currency), $this->context->currency->iso_code)
: '';
foreach ($attributes as $attribute) {
if (!isset($combinations[$attribute['id_product_attribute']]['attributes'])) {
$combinations[$attribute['id_product_attribute']]['attributes'] = '';
}
$combinations[$attribute['id_product_attribute']]['attributes'] .= $attribute['attribute_name'] . ' - ';
$combinations[$attribute['id_product_attribute']]['id_product_attribute'] = $attribute['id_product_attribute'];
$combinations[$attribute['id_product_attribute']]['default_on'] = $attribute['default_on'];
$price_tax_incl = Product::getPriceStatic((int) $product['id_product'], true, $attribute['id_product_attribute']);
$combinations[$attribute['id_product_attribute']]['formatted_price'] = $price_tax_incl
? $this->context->getCurrentLocale()->formatPrice(Tools::convertPrice($price_tax_incl, $this->context->currency), $this->context->currency->iso_code)
: '';
}
foreach ($combinations as &$combination) {
$combination['attributes'] = rtrim($combination['attributes'], ' - ');
}
$product['combinations'] = $combinations;
}
return [
'products' => $products,
'found' => true,
];
} else {
return ['found' => false, 'notfound' => $this->trans('No product has been found.', [], 'Admin.Catalog.Notification')];
}
}
public function ajaxProcessSearchProducts()
{
$array = $this->searchProducts(Tools::getValue('product_search'));
$this->content = trim(json_encode($array));
}
public function renderForm()
{
$limit = 40;
$this->toolbar_btn['save'] = [
'href' => '#',
'desc' => $this->trans('Save', [], 'Admin.Actions'),
];
$this->toolbar_btn['save-and-stay'] = [
'href' => '#',
'desc' => $this->trans('Save and stay', [], 'Admin.Actions'),
];
/** @var CartRule $current_object */
$current_object = $this->loadObject(true);
// All the filter are prefilled with the correct information
$customer_filter = '';
if (Validate::isUnsignedId($current_object->id_customer)
&& Validate::isLoadedObject($customer = new Customer($current_object->id_customer))
) {
$customer_filter = $customer->firstname . ' ' . $customer->lastname . ' (' . $customer->email . ')';
}
$gift_product_filter = '';
if (Validate::isUnsignedId($current_object->gift_product)
&& Validate::isLoadedObject($product = new Product($current_object->gift_product, false, $this->context->language->id))
) {
$gift_product_filter = (!empty($product->reference) ? $product->reference : $product->name);
}
$reduction_product_filter = '';
if (Validate::isUnsignedId($current_object->reduction_product)
&& Validate::isLoadedObject($product = new Product($current_object->reduction_product, false, $this->context->language->id))
) {
$reduction_product_filter = (!empty($product->reference) ? $product->reference : $product->name);
}
$product_rule_groups = $this->getProductRuleGroupsDisplay($current_object);
$attribute_groups = AttributeGroup::getAttributesGroups($this->context->language->id);
$currencies = Currency::getCurrencies(false, true, true);
$languages = Language::getLanguages();
$countries = $current_object->getAssociatedRestrictions('country', true, true);
$groups = $current_object->getAssociatedRestrictions('group', false, true);
$shops = $current_object->getAssociatedRestrictions('shop', false, false);
$cart_rules = $current_object->getAssociatedRestrictions('cart_rule', false, true, 0, $limit);
$carriers = $current_object->getAssociatedRestrictions('carrier', true, true);
foreach ($carriers as &$carriers2) {
$prev_id_carrier = 0;
foreach ($carriers2 as $key => &$carrier) {
if ($prev_id_carrier == $carrier['id_carrier']) {
unset($carriers2[$key]);
continue;
}
foreach ($carrier as $field => &$value) {
if ($field == 'name') {
if ($value == '0') {
$value = $carrier['id_carrier'] . ' - ' . Configuration::get('PS_SHOP_NAME');
} else {
$value = $carrier['id_carrier'] . ' - ' . $carrier['name'];
if (!empty($carrier['delay'])) {
$value .= ' (' . $carrier['delay'] . ')';
}
}
}
}
$prev_id_carrier = $carrier['id_carrier'];
}
}
$gift_product_select = '';
$gift_product_attribute_select = '';
if ((int) $current_object->gift_product) {
$search_products = $this->searchProducts($gift_product_filter);
if (isset($search_products['products']) && is_array($search_products['products'])) {
foreach ($search_products['products'] as $product) {
$gift_product_select .= '
<option value="' . $product['id_product'] . '" ' . ($product['id_product'] == $current_object->gift_product ? 'selected="selected"' : '') . '>
' . $product['name'] . (count($product['combinations']) == 0 ? ' - ' . $product['formatted_price'] : '') . '
</option>';
if (count($product['combinations'])) {
$gift_product_attribute_select .= '<select class="control-form id_product_attribute" id="ipa_' . $product['id_product'] . '" name="ipa_' . $product['id_product'] . '">';
foreach ($product['combinations'] as $combination) {
$gift_product_attribute_select .= '
<option ' . ($combination['id_product_attribute'] == $current_object->gift_product_attribute ? 'selected="selected"' : '') . ' value="' . $combination['id_product_attribute'] . '">
' . $combination['attributes'] . ' - ' . $combination['formatted_price'] . '
</option>';
}
$gift_product_attribute_select .= '</select>';
}
}
}
}
$product = new Product($current_object->gift_product);
$this->context->smarty->assign(
[
'show_toolbar' => true,
'toolbar_btn' => $this->toolbar_btn,
'toolbar_scroll' => $this->toolbar_scroll,
'title' => $this->trans('Cart Rules', [], 'Admin.Catalog.Feature'),
'defaultDateFrom' => date('Y-m-d H:00:00'),
'defaultDateTo' => date('Y-m-d H:00:00', strtotime('+1 month')),
'customerFilter' => $customer_filter,
'giftProductFilter' => $gift_product_filter,
'gift_product_select' => $gift_product_select,
'gift_product_attribute_select' => $gift_product_attribute_select,
'reductionProductFilter' => $reduction_product_filter,
'defaultCurrency' => Currency::getDefaultCurrencyId(),
'id_lang_default' => Configuration::get('PS_LANG_DEFAULT'),
'languages' => $languages,
'currencies' => $currencies,
'countries' => $countries,
'carriers' => $carriers,
'groups' => $groups,
'shops' => $shops,
'all_shops' => Shop::getCompleteListOfShopsID(),
'cart_rules' => $cart_rules,
'product_rule_groups' => $product_rule_groups,
'product_rule_groups_counter' => count($product_rule_groups),
'attribute_groups' => $attribute_groups,
'currentIndex' => self::$currentIndex,
'currentToken' => $this->token,
'currentObject' => $current_object,
'currentTab' => $this,
'hasAttribute' => $product->hasAttributes(),
]
);
Media::addJsDef(['baseHref' => $this->context->link->getAdminLink('AdminCartRules') . '&ajaxMode=1&ajax=1&id_cart_rule=' .
(int) Tools::getValue('id_cart_rule') . '&action=loadCartRules&limit=' . (int) $limit . '&count=0', ]);
$this->content .= $this->createTemplate('form.tpl')->fetch();
$this->addJqueryUI('ui.datepicker');
$this->addJqueryPlugin(['jscroll', 'typewatch']);
return parent::renderForm();
}
public function displayAjaxSearchCartRuleVouchers()
{
$found = false;
if ($vouchers = CartRule::getCartsRuleByCode(Tools::getValue('q'), (int) $this->context->language->id, true)) {
$found = true;
}
echo json_encode(['found' => $found, 'vouchers' => $vouchers]);
}
/**
* For the listing, Override the method displayDeleteLink for the HelperList
* That allows to have links with all characters (like < & >)
*
* @param string $token
* @param string $id
* @param string|null $name
*
* @return string
*/
public function displayDeleteLink(string $token, string $id, ?string $name = null): string
{
if (!$this->access('delete')) {
return '';
}
$tpl = $this->createTemplate('helpers/list/list_action_delete.tpl');
if (!array_key_exists('Delete', self::$cache_lang)) {
self::$cache_lang['Delete'] = $this->trans('Delete', [], 'Admin.Actions');
}
if (!array_key_exists('DeleteItem', self::$cache_lang)) {
self::$cache_lang['DeleteItem'] = $this->trans('Delete selected item?', [], 'Admin.Notifications.Info');
}
if (!array_key_exists('Name', self::$cache_lang)) {
self::$cache_lang['Name'] = $this->trans('Name:', [], 'Admin.Shipping.Feature');
}
if (null !== $name) {
// \n\n is not between double quotes because in js/jquery/plugins/alerts/jquery.alerts.js, \n is replaced by <br>.
$name = '\n\n' . self::$cache_lang['Name'] . ' ' . $name;
}
$data = [
$this->identifier => $id,
'href' => $this->context->link->getAdminLink(
'AdminCartRules',
true, [],
[
'id_cart_rule' => (int) $id,
'deletecart_rule' => 1,
]
),
'action' => self::$cache_lang['Delete'],
];
if ($this->specificConfirmDelete !== false) {
$data['confirm'] = null !== $this->specificConfirmDelete
? '\r' . $this->specificConfirmDelete
: Tools::htmlentitiesDecodeUTF8(self::$cache_lang['DeleteItem'] . $name);
}
$tpl->assign(array_merge($this->tpl_delete_link_vars, $data));
return $tpl->fetch();
}
}

View File

@@ -0,0 +1,516 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property Country $object
*/
class AdminCountriesControllerCore extends AdminController
{
public function __construct()
{
$this->bootstrap = true;
$this->table = 'country';
$this->className = 'Country';
$this->lang = true;
$this->deleted = false;
$this->_defaultOrderBy = 'name';
$this->_defaultOrderWay = 'ASC';
$this->explicitSelect = true;
$this->addRowAction('edit');
parent::__construct();
$this->bulk_actions = [
'delete' => ['text' => $this->trans('Delete selected', [], 'Admin.Actions'), 'confirm' => $this->trans('Delete selected items?', [], 'Admin.Actions')],
'AffectZone' => ['text' => $this->trans('Assign to a new zone', [], 'Admin.International.Feature')],
];
$this->fieldImageSettings = [
'name' => 'logo',
'dir' => 'st',
];
$this->fields_options = [
'general' => [
'title' => $this->trans('Country options', [], 'Admin.International.Feature'),
'fields' => [
'PS_RESTRICT_DELIVERED_COUNTRIES' => [
'title' => $this->trans('Restrict country selections in front office to those covered by active carriers', [], 'Admin.International.Help'),
'cast' => 'intval',
'type' => 'bool',
'default' => '0',
],
],
'submit' => ['title' => $this->trans('Save', [], 'Admin.Actions')],
],
];
$zones_array = [];
foreach (Zone::getZones() as $zone) {
$zones_array[$zone['id_zone']] = $zone['name'];
}
$this->fields_list = [
'id_country' => [
'title' => $this->trans('ID', [], 'Admin.Global'),
'align' => 'center',
'class' => 'fixed-width-xs',
],
'name' => [
'title' => $this->trans('Country', [], 'Admin.Global'),
'filter_key' => 'b!name',
],
'iso_code' => [
'title' => $this->trans('ISO code', [], 'Admin.International.Feature'),
'align' => 'center',
'class' => 'fixed-width-xs',
],
'call_prefix' => [
'title' => $this->trans('Call prefix', [], 'Admin.International.Feature'),
'align' => 'center',
'callback' => 'displayCallPrefix',
'class' => 'fixed-width-sm',
],
'zone' => [
'title' => $this->trans('Zone', [], 'Admin.Global'),
'type' => 'select',
'list' => $zones_array,
'filter_key' => 'z!id_zone',
'filter_type' => 'int',
'order_key' => 'z!name',
],
'active' => [
'title' => $this->trans('Enabled', [], 'Admin.Global'),
'align' => 'center',
'active' => 'status',
'type' => 'bool',
'orderby' => false,
'filter_key' => 'a!active',
'class' => 'fixed-width-sm',
],
];
}
public function initPageHeaderToolbar()
{
if (empty($this->display)) {
$this->page_header_toolbar_btn['new_country'] = [
'href' => $this->context->link->getAdminLink('AdminCountries', true, [], ['addcountry' => 1]),
'desc' => $this->trans('Add new country', [], 'Admin.International.Feature'),
'icon' => 'process-icon-new',
];
}
parent::initPageHeaderToolbar();
}
/**
* AdminController::setMedia() override.
*
* @see AdminController::setMedia()
*/
public function setMedia($isNewTheme = false)
{
parent::setMedia($isNewTheme);
$this->addJqueryPlugin('fieldselection');
}
public function renderList()
{
$this->_select = 'z.`name` AS zone';
$this->_join = 'LEFT JOIN `' . _DB_PREFIX_ . 'zone` z ON (z.`id_zone` = a.`id_zone`)';
$this->_use_found_rows = false;
$this->tpl_list_vars['zones'] = Zone::getZones();
$this->tpl_list_vars['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
$this->tpl_list_vars['POST'] = $_POST;
return parent::renderList();
}
/**
* @return string|void
*
* @throws SmartyException
*/
public function renderForm()
{
if (!($obj = $this->loadObject(true))) {
return;
}
$address_layout = AddressFormat::getAddressCountryFormat($obj->id);
if ($value = Tools::getValue('address_layout')) {
$address_layout = $value;
}
$default_layout = '';
// TODO: Use format from XML
$default_layout_tab = [
['firstname', 'lastname'],
['company'],
['vat_number'],
['address1'],
['address2'],
['postcode', 'city'],
['Country:name'],
['phone'],
];
foreach ($default_layout_tab as $line) {
$default_layout .= implode(' ', $line) . AddressFormat::FORMAT_NEW_LINE;
}
$this->fields_form = [
'legend' => [
'title' => $this->trans('Countries', [], 'Admin.International.Feature'),
'icon' => 'icon-globe',
],
'input' => [
[
'type' => 'text',
'label' => $this->trans('Country', [], 'Admin.Global'),
'name' => 'name',
'lang' => true,
'required' => true,
'hint' => $this->trans('Country name', [], 'Admin.International.Feature') . ' - ' . $this->trans('Invalid characters:', [], 'Admin.Global') . ' &lt;&gt;{} ',
],
[
'type' => 'text',
'label' => $this->trans('ISO code', [], 'Admin.International.Feature'),
'name' => 'iso_code',
'maxlength' => 3,
'class' => 'uppercase',
'required' => true,
'hint' => $this->trans('Two -- or three -- letter ISO code (e.g. "us" for United States).', [], 'Admin.International.Help'),
/* @TODO - add two lines for the hint? */
/*'desc' => $this->trans('Two -- or three -- letter ISO code (e.g. U.S. for United States)', [], 'Admin.International.Help').'.
<a href="http://www.iso.org/iso/country_codes/iso_3166_code_lists/country_names_and_code_elements.htm" target="_blank">'.
$this->trans('Official list here', [], 'Admin.International.Feature').'
</a>.'*/
],
[
'type' => 'text',
'label' => $this->trans('Call prefix', [], 'Admin.International.Feature'),
'name' => 'call_prefix',
'maxlength' => 3,
'class' => 'uppercase',
'hint' => $this->trans('International call prefix, (e.g. 1 for United States).', [], 'Admin.International.Help'),
],
[
'type' => 'select',
'label' => $this->trans('Default currency', [], 'Admin.International.Feature'),
'name' => 'id_currency',
'options' => [
'query' => Currency::getCurrencies(false, true, true),
'id' => 'id_currency',
'name' => 'name',
'default' => [
'label' => $this->trans('Default store currency', [], 'Admin.International.Feature'),
'value' => 0,
],
],
],
[
'type' => 'select',
'label' => $this->trans('Zone', [], 'Admin.Global'),
'name' => 'id_zone',
'options' => [
'query' => Zone::getZones(),
'id' => 'id_zone',
'name' => 'name',
],
'hint' => $this->trans('Geographical region.', [], 'Admin.International.Help'),
],
[
'type' => 'switch',
'label' => $this->trans('Does it need a ZIP/Postal code?', [], 'Admin.International.Feature'),
'name' => 'need_zip_code',
'required' => false,
'is_bool' => true,
'values' => [
[
'id' => 'need_zip_code_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'need_zip_code_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
],
[
'type' => 'text',
'label' => $this->trans('ZIP/Postal code format', [], 'Admin.International.Feature'),
'name' => 'zip_code_format',
'desc' => $this->trans('Indicate the format of the postal code: use L for a letter, N for a number, and C for the country\'s ISO 3166-1 alpha-2 code. For example, NNNNN for the United States, France, Poland and many other; LNNNNLLL for Argentina, etc. If you do not want PrestaShop to verify the postal code for this country, leave it blank.', [], 'Admin.International.Help'),
],
[
'type' => 'address_layout',
'label' => $this->trans('Address format', [], 'Admin.International.Feature'),
'name' => 'address_layout',
'address_layout' => $address_layout,
'encoding_address_layout' => urlencode($address_layout),
'encoding_default_layout' => urlencode($default_layout),
'display_valid_fields' => $this->displayValidFields(),
],
[
'type' => 'switch',
'label' => $this->trans('Active', [], 'Admin.Global'),
'name' => 'active',
'required' => false,
'is_bool' => true,
'values' => [
[
'id' => 'active_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'active_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
'hint' => $this->trans('Display this country to your customers (the selected country will always be displayed in the Back Office).', [], 'Admin.International.Help'),
],
[
'type' => 'switch',
'label' => $this->trans('Contains states', [], 'Admin.International.Feature'),
'name' => 'contains_states',
'required' => false,
'values' => [
[
'id' => 'contains_states_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'contains_states_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
],
[
'type' => 'switch',
'label' => $this->trans('Do you need a tax identification number?', [], 'Admin.International.Feature'),
'name' => 'need_identification_number',
'required' => false,
'values' => [
[
'id' => 'need_identification_number_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'need_identification_number_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
],
[
'type' => 'switch',
'label' => $this->trans('Display tax label (e.g. "Tax incl.")', [], 'Admin.International.Feature'),
'name' => 'display_tax_label',
'required' => false,
'values' => [
[
'id' => 'display_tax_label_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'display_tax_label_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
],
],
];
if (Shop::isFeatureActive()) {
$this->fields_form['input'][] = [
'type' => 'shop',
'label' => $this->trans('Store association', [], 'Admin.Global'),
'name' => 'checkBoxShopAsso',
];
}
$this->fields_form['submit'] = [
'title' => $this->trans('Save', [], 'Admin.Actions'),
];
return parent::renderForm();
}
public function processUpdate()
{
/** @var Country $country */
$country = $this->loadObject();
if (Validate::isLoadedObject($country) && Tools::getValue('id_zone')) {
$old_id_zone = $country->id_zone;
$results = Db::getInstance()->executeS('SELECT `id_state` FROM `' . _DB_PREFIX_ . 'state` WHERE `id_country` = ' . (int) $country->id . ' AND `id_zone` = ' . (int) $old_id_zone);
if ($results && count($results)) {
$ids = [];
foreach ($results as $res) {
$ids[] = (int) $res['id_state'];
}
if (count($ids)) {
$res = Db::getInstance()->execute(
'UPDATE `' . _DB_PREFIX_ . 'state`
SET `id_zone` = ' . (int) Tools::getValue('id_zone') . '
WHERE `id_state` IN (' . implode(',', $ids) . ')'
);
}
}
}
return parent::processUpdate();
}
public function postProcess()
{
if (!Tools::getValue('id_' . $this->table)) {
if (Validate::isLanguageIsoCode(Tools::getValue('iso_code')) && (int) Country::getByIso(Tools::getValue('iso_code'))) {
$this->errors[] = $this->trans('This ISO code already exists.You cannot create two countries with the same ISO code.', [], 'Admin.International.Notification');
}
} elseif (Validate::isLanguageIsoCode(Tools::getValue('iso_code'))) {
$id_country = (int) Country::getByIso(Tools::getValue('iso_code'));
if ($id_country != 0 && $id_country != Tools::getValue('id_' . $this->table)) {
$this->errors[] = $this->trans('This ISO code already exists.You cannot create two countries with the same ISO code.', [], 'Admin.International.Notification');
}
}
return parent::postProcess();
}
public function processSave()
{
if (!$this->id_object) {
$tmp_addr_format = new AddressFormat();
} else {
$tmp_addr_format = new AddressFormat($this->id_object);
}
$tmp_addr_format->format = Tools::getValue('address_layout');
if (!$tmp_addr_format->checkFormatFields()) {
$error_list = $tmp_addr_format->getErrorList();
foreach ($error_list as $error) {
$this->errors[] = $error;
}
}
if (strlen($tmp_addr_format->format) <= 0) {
$this->errors[] = $this->trans('Address format invalid', [], 'Admin.Notifications.Error');
}
$country = parent::processSave();
if (!count($this->errors)) {
if (null === $tmp_addr_format->id_country) {
$tmp_addr_format->id_country = $country->id;
}
if (!$tmp_addr_format->save()) {
$this->errors[] = $this->trans('Invalid address layout %s', [Db::getInstance()->getMsgError()], 'Admin.International.Notification');
}
}
return $country;
}
/**
* @return bool|ObjectModel|null
*
* @throws PrestaShopException
*/
public function processStatus()
{
parent::processStatus();
$object = $this->loadObject();
/** @var Country $object */
if (Validate::isLoadedObject($object) && $object->active == 1) {
return Country::addModuleRestrictions([], [['id_country' => $object->id]], []);
}
return false;
}
/**
* Allow the assignation of zone only if the form is displayed.
*
* @return bool|void
*/
protected function processBulkAffectZone()
{
$zone_to_affect = Tools::getValue('zone_to_affect');
if ($zone_to_affect && $zone_to_affect !== 0) {
parent::processBulkAffectZone();
}
if (Tools::getIsset('submitBulkAffectZonecountry')) {
$this->tpl_list_vars['assign_zone'] = true;
}
}
protected function displayValidFields()
{
/* The following translations are needed later - don't remove the comments!
$this->trans('Customer', [], 'Admin.Global');
$this->trans('Country', [], 'Admin.Global');
$this->trans('State', [], 'Admin.Global');
$this->trans('Address', [], 'Admin.Global');
*/
$html_tabnav = '<ul class="nav nav-tabs" id="custom-address-fields">';
$html_tabcontent = '<div class="tab-content" >';
$object_list = AddressFormat::getLiableClass('Address');
$object_list['Address'] = null;
// Get the available properties for each class
$i = 0;
$class_tab_active = 'active';
foreach ($object_list as $class_name => &$object) {
if ($i != 0) {
$class_tab_active = '';
}
$fields = [];
$html_tabnav .= '<li' . ($class_tab_active ? ' class="' . $class_tab_active . '"' : '') . '>
<a href="#availableListFieldsFor_' . $class_name . '"><i class="icon-caret-down"></i>&nbsp;' . Context::getContext()->getTranslator()->trans($class_name, [], 'AdminCountries') . '</a></li>';
foreach (AddressFormat::getValidateFields($class_name) as $name) {
$fields[] = '<a href="javascript:void(0);" class="addPattern btn btn-default btn-xs" id="' . ($class_name == 'Address' ? $name : $class_name . ':' . $name) . '">
<i class="icon-plus-sign"></i>&nbsp;' . ObjectModel::displayFieldName($name, $class_name) . '</a>';
}
$html_tabcontent .= '
<div class="tab-pane availableFieldsList panel ' . $class_tab_active . '" id="availableListFieldsFor_' . $class_name . '">
' . implode(' ', $fields) . '</div>';
unset($object);
++$i;
}
$html_tabnav .= '</ul>';
$html_tabcontent .= '</div>';
return $html_tabnav . $html_tabcontent;
}
public static function displayCallPrefix($prefix)
{
return (int) $prefix ? '+' . $prefix : '-';
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,435 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class AdminDashboardControllerCore extends AdminController
{
private const DASHBOARD_ALLOWED_HOOKS = ['dashboardData', 'dashboardZoneOne', 'dashboardZoneTwo', 'displayDashboardToolbarIcons', 'displayDashboardToolbarTopMenu', 'displayDashboardTop'];
public function __construct()
{
$this->bootstrap = true;
$this->display = 'view';
parent::__construct();
if (Tools::isSubmit('profitability_conf') || Tools::isSubmit('submitOptionsconfiguration')) {
$this->fields_options = $this->getOptionFields();
}
}
public function setMedia($isNewTheme = false)
{
parent::setMedia($isNewTheme);
$this->addJqueryUI('ui.datepicker');
$this->addJS([
_PS_JS_DIR_ . 'vendor/d3.v3.min.js',
__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $this->bo_theme . '/js/vendor/nv.d3.min.js',
_PS_JS_DIR_ . '/admin/dashboard.js',
]);
$this->addCSS(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $this->bo_theme . '/css/vendor/nv.d3.css');
}
public function initPageHeaderToolbar()
{
$this->page_header_toolbar_title = $this->trans('Dashboard', [], 'Admin.Dashboard.Feature');
$this->page_header_toolbar_btn['switch_demo'] = [
'desc' => $this->trans('Demo mode', [], 'Admin.Dashboard.Feature'),
'icon' => 'process-icon-toggle-' . (Configuration::get('PS_DASHBOARD_SIMULATION') ? 'on' : 'off'),
'help' => $this->trans('This mode displays sample data so you can try your dashboard without real numbers.', [], 'Admin.Dashboard.Help'),
];
parent::initPageHeaderToolbar();
// Remove the last element on this controller to match the title with the rule of the others
array_pop($this->meta_title);
}
protected function getOptionFields()
{
$currency = Currency::getDefaultCurrency();
$carriers = Carrier::getCarriers((int) $this->context->language->id, true, false, false, null, Carrier::ALL_CARRIERS);
$modules = Module::getModulesOnDisk(true);
$forms = [
'payment' => ['title' => $this->trans('Average bank fees per payment method', [], 'Admin.Dashboard.Feature'), 'id' => 'payment'],
'carriers' => ['title' => $this->trans('Average shipping fees per shipping method', [], 'Admin.Dashboard.Feature'), 'id' => 'carriers'],
'other' => ['title' => $this->trans('Other settings', [], 'Admin.Dashboard.Feature'), 'id' => 'other'],
];
foreach ($forms as &$form) {
$form['icon'] = 'tab-preferences';
$form['fields'] = [];
$form['submit'] = ['title' => $this->trans('Save', [], 'Admin.Actions')];
}
foreach ($modules as $module) {
if (isset($module->tab) && $module->tab == 'payments_gateways' && $module->id) {
$moduleClass = Module::getInstanceByName($module->name);
if (!$moduleClass->isEnabledForShopContext()) {
continue;
}
$forms['payment']['fields']['CONF_' . strtoupper($module->name) . '_FIXED'] = [
'title' => $module->displayName,
'desc' => $this->trans(
'Choose a fixed fee for each order placed in %currency% with %module%.',
[
'%currency' => $currency->iso_code,
'%module%' => $module->displayName,
],
'Admin.Dashboard.Help'
),
'validation' => 'isPrice',
'cast' => 'floatval',
'type' => 'text',
'defaultValue' => '0',
'suffix' => $currency->iso_code,
];
$forms['payment']['fields']['CONF_' . strtoupper($module->name) . '_VAR'] = [
'title' => $module->displayName,
'desc' => $this->trans(
'Choose a variable fee for each order placed in %currency% with %module%. It will be applied on the total paid with taxes.',
[
'%currency' => $currency->iso_code,
'%module%' => $module->displayName,
],
'Admin.Dashboard.Help'
),
'validation' => 'isPercentage',
'cast' => 'floatval',
'type' => 'text',
'defaultValue' => '0',
'suffix' => '%',
];
if (Currency::isMultiCurrencyActivated()) {
$forms['payment']['fields']['CONF_' . strtoupper($module->name) . '_FIXED_FOREIGN'] = [
'title' => $module->displayName,
'desc' => $this->trans(
'Choose a fixed fee for each order placed with a foreign currency with %module%.',
[
'%module%' => $module->displayName,
],
'Admin.Dashboard.Help'
),
'validation' => 'isPrice',
'cast' => 'floatval',
'type' => 'text',
'defaultValue' => '0',
'suffix' => $currency->iso_code,
];
$forms['payment']['fields']['CONF_' . strtoupper($module->name) . '_VAR_FOREIGN'] = [
'title' => $module->displayName,
'desc' => $this->trans(
'Choose a variable fee for each order placed with a foreign currency with %module%. It will be applied on the total paid with taxes.',
['%module%' => $module->displayName],
'Admin.Dashboard.Help'
),
'validation' => 'isPercentage',
'cast' => 'floatval',
'type' => 'text',
'defaultValue' => '0',
'suffix' => '%',
];
}
}
}
foreach ($carriers as $carrier) {
$forms['carriers']['fields']['CONF_' . strtoupper($carrier['id_reference']) . '_SHIP'] = [
'title' => $carrier['name'],
'desc' => $this->trans(
'For the carrier named %s, indicate the domestic delivery costs in percentage of the price charged to customers.',
[
'%s' => $carrier['name'],
],
'Admin.Dashboard.Help'
),
'validation' => 'isPercentage',
'cast' => 'floatval',
'type' => 'text',
'defaultValue' => '0',
'suffix' => '%',
];
$forms['carriers']['fields']['CONF_' . strtoupper($carrier['id_reference']) . '_SHIP_OVERSEAS'] = [
'title' => $carrier['name'],
'desc' => $this->trans(
'For the carrier named %s, indicate the overseas delivery costs in percentage of the price charged to customers.',
[
'%s' => $carrier['name'],
],
'Admin.Dashboard.Help'
),
'validation' => 'isPercentage',
'cast' => 'floatval',
'type' => 'text',
'defaultValue' => '0',
'suffix' => '%',
];
}
$forms['carriers']['description'] = $this->trans('Method: Indicate the percentage of your carrier margin. For example, if you charge $10 of shipping fees to your customer for each shipment, but you really pay $4 to this carrier, then you should indicate "40" in the percentage field.', [], 'Admin.Dashboard.Help');
$forms['other']['fields']['CONF_AVERAGE_PRODUCT_MARGIN'] = [
'title' => $this->trans('Average gross margin percentage', [], 'Admin.Dashboard.Feature'),
'desc' => $this->trans('You should calculate this percentage as follows: ((total sales revenue) - (cost of goods sold)) / (total sales revenue) * 100. This value is only used to calculate the Dashboard approximate gross margin, if you do not specify the wholesale price for each product.', [], 'Admin.Dashboard.Help'),
'validation' => 'isPercentage',
'cast' => 'intval',
'type' => 'text',
'defaultValue' => '0',
'suffix' => '%',
];
$forms['other']['fields']['CONF_ORDER_FIXED'] = [
'title' => $this->trans('Other fees per order', [], 'Admin.Dashboard.Feature'),
'desc' => $this->trans('You should calculate this value by making the sum of all of your additional costs per order.', [], 'Admin.Dashboard.Help'),
'validation' => 'isPrice',
'cast' => 'floatval',
'type' => 'text',
'defaultValue' => '0',
'suffix' => $currency->iso_code,
];
Media::addJsDef([
'dashboard_ajax_url' => $this->context->link->getAdminLink('AdminDashboard'),
'read_more' => '',
]);
return $forms;
}
public function renderView()
{
if (Tools::isSubmit('profitability_conf')) {
return parent::renderOptions();
}
$testStatsDateUpdate = $this->context->cookie->__get('stats_date_update');
if (!empty($testStatsDateUpdate) && $this->context->cookie->__get('stats_date_update') < strtotime(date('Y-m-d'))) {
switch ($this->context->employee->preselect_date_range) {
case 'day':
$date_from = date('Y-m-d');
$date_to = date('Y-m-d');
break;
case 'prev-day':
$date_from = date('Y-m-d', strtotime('-1 day'));
$date_to = date('Y-m-d', strtotime('-1 day'));
break;
case 'month':
default:
$date_from = date('Y-m-01');
$date_to = date('Y-m-d');
break;
case 'prev-month':
$date_from = date('Y-m-01', strtotime('-1 month'));
$date_to = date('Y-m-t', strtotime('-1 month'));
break;
case 'year':
$date_from = date('Y-01-01');
$date_to = date('Y-m-d');
break;
case 'prev-year':
$date_from = date('Y-m-01', strtotime('-1 year'));
$date_to = date('Y-12-t', strtotime('-1 year'));
break;
}
$this->context->employee->stats_date_from = $date_from;
$this->context->employee->stats_date_to = $date_to;
$this->context->employee->update();
$this->context->cookie->__set('stats_date_update', strtotime(date('Y-m-d')));
$this->context->cookie->write();
}
$calendar_helper = new HelperCalendar();
$calendar_helper->setDateFrom(Tools::getValue('date_from', $this->context->employee->stats_date_from));
$calendar_helper->setDateTo(Tools::getValue('date_to', $this->context->employee->stats_date_to));
$stats_compare_from = $this->context->employee->stats_compare_from;
$stats_compare_to = $this->context->employee->stats_compare_to;
if (null === $stats_compare_from || $stats_compare_from == '0000-00-00') {
$stats_compare_from = null;
}
if (null === $stats_compare_to || $stats_compare_to == '0000-00-00') {
$stats_compare_to = null;
}
$calendar_helper->setCompareDateFrom($stats_compare_from);
$calendar_helper->setCompareDateTo($stats_compare_to);
$calendar_helper->setCompareOption(Tools::getValue('compare_date_option', $this->context->employee->stats_compare_option));
$params = [
'date_from' => $this->context->employee->stats_date_from,
'date_to' => $this->context->employee->stats_date_to,
];
$this->tpl_view_vars = [
'date_from' => $this->context->employee->stats_date_from,
'date_to' => $this->context->employee->stats_date_to,
'hookDashboardZoneOne' => Hook::exec('dashboardZoneOne', $params),
'hookDashboardZoneTwo' => Hook::exec('dashboardZoneTwo', $params),
'hookDashboardZoneThree' => Hook::exec('dashboardZoneThree', $params),
'action' => '#',
'warning' => $this->getWarningDomainName(),
'calendar' => $calendar_helper->generate(),
'PS_DASHBOARD_SIMULATION' => Configuration::get('PS_DASHBOARD_SIMULATION'),
'datepickerFrom' => Tools::getValue('datepickerFrom', $this->context->employee->stats_date_from),
'datepickerTo' => Tools::getValue('datepickerTo', $this->context->employee->stats_date_to),
'preselect_date_range' => Tools::getValue('preselectDateRange', $this->context->employee->preselect_date_range),
];
return parent::renderView();
}
public function postProcess()
{
if (Tools::isSubmit('submitDateRange')) {
if (!Validate::isDate(Tools::getValue('date_from'))
|| !Validate::isDate(Tools::getValue('date_to'))) {
$this->errors[] = $this->trans('The selected date range is not valid.', [], 'Admin.Notifications.Error');
}
if (Tools::getValue('datepicker_compare')) {
if (!Validate::isDate(Tools::getValue('compare_date_from'))
|| !Validate::isDate(Tools::getValue('compare_date_to'))) {
$this->errors[] = $this->trans('The selected date range is not valid.', [], 'Admin.Notifications.Error');
}
}
if (!count($this->errors)) {
$this->context->employee->stats_date_from = Tools::getValue('date_from');
$this->context->employee->stats_date_to = Tools::getValue('date_to');
$this->context->employee->preselect_date_range = Tools::getValue('preselectDateRange');
if (Tools::getValue('datepicker_compare')) {
$this->context->employee->stats_compare_from = Tools::getValue('compare_date_from');
$this->context->employee->stats_compare_to = Tools::getValue('compare_date_to');
$this->context->employee->stats_compare_option = Tools::getValue('compare_date_option');
} else {
$this->context->employee->stats_compare_from = null;
$this->context->employee->stats_compare_to = null;
$this->context->employee->stats_compare_option = HelperCalendar::DEFAULT_COMPARE_OPTION;
}
$this->context->employee->update();
}
}
parent::postProcess();
}
protected function getWarningDomainName()
{
$warning = false;
if (Shop::isFeatureActive()) {
return;
}
$shop = Context::getContext()->shop;
if ($_SERVER['HTTP_HOST'] != $shop->domain && $_SERVER['HTTP_HOST'] != $shop->domain_ssl && Tools::getValue('ajax') == false) {
$warning = $this->trans('You are currently connected under the following domain name:', [], 'Admin.Dashboard.Notification') . ' <span style="color: #CC0000;">' . $_SERVER['HTTP_HOST'] . '</span><br />';
if (Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE')) {
$warning .= $this->trans(
'This is different from the shop domain name set in the Multistore settings: "%s".',
[
'%s' => $shop->domain,
],
'Admin.Dashboard.Notification'
) . $this->trans(
'If this is your main domain, please {link}change it now{/link}.',
[
'{link}' => '<a href="' . $this->context->link->getAdminLink('AdminShopUrl', true, [], ['id_shop_url' => (int) $shop->id, 'updateshop_url' => 1]) . '">',
'{/link}' => '</a>',
],
'Admin.Dashboard.Notification'
);
} else {
$warning .= $this->trans('This is different from the domain name set in the "SEO & URLs" tab.', [], 'Admin.Dashboard.Notification') . '
' . $this->trans(
'If this is your main domain, please {link}change it now{/link}.',
[
'{link}' => '<a href="' . $this->context->link->getAdminLink('AdminMeta') . '#meta_fieldset_shop_url">',
'{/link}' => '</a>',
],
'Admin.Dashboard.Notification'
);
}
}
return $warning;
}
public function ajaxProcessRefreshDashboard()
{
$id_module = null;
if ($module = Tools::getValue('module')) {
$module_obj = Module::getInstanceByName($module);
if (Validate::isLoadedObject($module_obj)) {
$id_module = $module_obj->id;
}
}
$params = [
'date_from' => $this->context->employee->stats_date_from,
'date_to' => $this->context->employee->stats_date_to,
'compare_from' => $this->context->employee->stats_compare_from,
'compare_to' => $this->context->employee->stats_compare_to,
'extra' => (int) Tools::getValue('extra'),
];
// Hook called only for the module concerned
// An array [module_name => module_output] will be returned
die(json_encode(Hook::exec('dashboardData', $params, $id_module, true)));
}
public function ajaxProcessSetSimulationMode()
{
Configuration::updateValue('PS_DASHBOARD_SIMULATION', (int) Tools::getValue('PS_DASHBOARD_SIMULATION'));
die('k' . Configuration::get('PS_DASHBOARD_SIMULATION') . 'k');
}
public function ajaxProcessSaveDashConfig()
{
$return = ['has_errors' => false, 'errors' => []];
$module = Tools::getValue('module');
$hook = Tools::getValue('hook');
$configs = Tools::getValue('configs');
if (!in_array(lcfirst(str_replace('hook', '', $hook)), self::DASHBOARD_ALLOWED_HOOKS)) {
$return['has_errors'] = true;
$return['errors'][] = 'This hook is not allowed here.';
die(json_encode($return));
}
$params = [
'date_from' => $this->context->employee->stats_date_from,
'date_to' => $this->context->employee->stats_date_to,
];
$module_obj = Module::getInstanceByName($module);
if (Validate::isModuleName($module) && $module_obj) {
$return['errors'] = $module_obj->validateDashConfig($configs);
if (count($return['errors'])) {
$return['has_errors'] = true;
} else {
$return['has_errors'] = $module_obj->saveDashConfig($configs);
}
}
if (method_exists($module_obj, $hook)) {
$return['widget_html'] = $module_obj->$hook($params);
}
die(json_encode($return));
}
}

View File

@@ -0,0 +1,705 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property Group $object
*/
class AdminGroupsControllerCore extends AdminController
{
public function __construct()
{
$this->bootstrap = true;
$this->table = 'group';
$this->className = 'Group';
$this->list_id = 'group';
$this->lang = true;
parent::__construct();
$this->addRowAction('edit');
$this->addRowAction('view');
$this->addRowAction('delete');
$this->bulk_actions = [
'delete' => [
'text' => $this->trans('Delete selected', [], 'Admin.Actions'),
'confirm' => $this->trans('Delete selected items?', [], 'Admin.Notifications.Warning'),
'icon' => 'icon-trash',
],
];
$groups_to_keep = [
Configuration::get('PS_UNIDENTIFIED_GROUP'),
Configuration::get('PS_GUEST_GROUP'),
Configuration::get('PS_CUSTOMER_GROUP'),
];
$this->fields_list = [
'id_group' => [
'title' => $this->trans('ID', [], 'Admin.Global'),
'align' => 'center',
'class' => 'fixed-width-xs',
],
'name' => [
'title' => $this->trans('Group name', [], 'Admin.Shopparameters.Feature'),
'filter_key' => 'b!name',
],
'reduction' => [
'title' => $this->trans('Discount (%)', [], 'Admin.Shopparameters.Feature'),
'align' => 'right',
'type' => 'percent',
],
'nb' => [
'title' => $this->trans('Members', [], 'Admin.Shopparameters.Feature'),
'align' => 'center',
'havingFilter' => true,
],
'show_prices' => [
'title' => $this->trans('Show prices', [], 'Admin.Shopparameters.Feature'),
'align' => 'center',
'type' => 'bool',
'orderby' => false,
],
'date_add' => [
'title' => $this->trans('Creation date', [], 'Admin.Shopparameters.Feature'),
'type' => 'date',
'align' => 'right',
],
];
$this->addRowActionSkipList('delete', $groups_to_keep);
$this->_select .= '(SELECT COUNT(jcg.`id_customer`)
FROM `' . _DB_PREFIX_ . 'customer_group` jcg
LEFT JOIN `' . _DB_PREFIX_ . 'customer` jc ON (jc.`id_customer` = jcg.`id_customer`)
WHERE jc.`deleted` != 1
' . Shop::addSqlRestriction(Shop::SHARE_CUSTOMER) . '
AND jcg.`id_group` = a.`id_group`) AS nb';
$this->_use_found_rows = false;
$groups = Group::getGroups(Context::getContext()->language->id, true);
if (Group::isFeatureActive()) {
$this->fields_options = [
'general' => [
'title' => $this->trans('Default groups options', [], 'Admin.Shopparameters.Feature'),
'fields' => [
'PS_UNIDENTIFIED_GROUP' => [
'title' => $this->trans('Visitors group', [], 'Admin.Shopparameters.Feature'),
'desc' => $this->trans('The group defined for your un-identified visitors.', [], 'Admin.Shopparameters.Help'),
'cast' => 'intval',
'type' => 'select',
'list' => $groups,
'identifier' => 'id_group',
],
'PS_GUEST_GROUP' => [
'title' => $this->trans('Guests group', [], 'Admin.Shopparameters.Feature'),
'desc' => $this->trans('The group defined for your identified guest customers (used in guest checkout).', [], 'Admin.Shopparameters.Help'),
'cast' => 'intval',
'type' => 'select',
'list' => $groups,
'identifier' => 'id_group',
],
'PS_CUSTOMER_GROUP' => [
'title' => $this->trans('Customers group', [], 'Admin.Shopparameters.Feature'),
'desc' => $this->trans('The group defined for your identified registered customers.', [], 'Admin.Shopparameters.Help'),
'cast' => 'intval',
'type' => 'select',
'list' => $groups,
'identifier' => 'id_group',
],
],
'submit' => [
'title' => $this->trans('Save', [], 'Admin.Actions'),
],
],
];
}
}
public function setMedia($isNewTheme = false)
{
parent::setMedia($isNewTheme);
$this->addJqueryPlugin('fancybox');
$this->addJqueryUi('ui.sortable');
}
public function initToolbar()
{
if ($this->display == 'add' || $this->display == 'edit') {
$this->toolbar_btn['save-and-stay'] = [
'short' => 'SaveAndStay',
'href' => '#',
'desc' => $this->trans('Save, then add a category reduction.', [], 'Admin.Shopparameters.Feature'),
'force_desc' => true,
];
}
parent::initToolbar();
}
public function initPageHeaderToolbar()
{
if (Group::isFeatureActive() && empty($this->display)) {
$this->page_header_toolbar_btn['new_group'] = [
'href' => $this->context->link->getAdminLink('AdminGroups', true, [], ['addgroup' => 1]),
'desc' => $this->trans('Add new group', [], 'Admin.Shopparameters.Feature'),
'icon' => 'process-icon-new',
];
}
parent::initPageHeaderToolbar();
}
public function initProcess()
{
$this->id_object = Tools::getValue('id_' . $this->table);
if (Tools::isSubmit('changeShowPricesVal') && $this->id_object) {
$this->action = 'change_show_prices_val';
}
if (Tools::getIsset('viewgroup')) {
$this->list_id = 'customer_group';
if (isset($_POST['submitReset' . $this->list_id])) {
$this->processResetFilters();
}
if (Tools::isSubmit('submitFilter')) {
self::$currentIndex .= '&id_group=' . (int) Tools::getValue('id_group') . '&viewgroup';
}
} else {
$this->list_id = 'group';
}
parent::initProcess();
// This is a composite page, we don't want the "options" display mode
if ($this->display == 'options') {
$this->display = '';
}
}
public function postProcess(): void
{
if (!Group::isFeatureActive()) {
return;
}
$tableCustomerGroup = 'customer_group';
if (!empty($_POST[$tableCustomerGroup . 'Box'])
&& is_array($_POST[$tableCustomerGroup . 'Box'])
&& (
Tools::isSubmit('submitBulkenableSelection' . $tableCustomerGroup)
|| Tools::isSubmit('submitBulkdisableSelection' . $tableCustomerGroup)
)
) {
$status = Tools::isSubmit('submitBulkenableSelection' . $tableCustomerGroup);
foreach ($_POST[$tableCustomerGroup . 'Box'] as $customerId) {
$customer = new Customer((int) $customerId);
$customer->setFieldsToUpdate(['active' => true]);
$customer->active = $status;
if (!$customer->update(false)) {
$this->errors[] = $this->trans('Failed to update the status', [], 'Admin.Notifications.Error');
break;
}
}
}
if (!count($this->errors)) {
parent::postProcess();
}
}
/**
* @return string|void
*/
public function renderView()
{
$this->context = Context::getContext();
if (!($group = $this->loadObject(true))) {
return;
}
$this->tpl_view_vars = [
'group' => $group,
'language' => $this->context->language,
// @phpstan-ignore-next-line
'customerList' => $this->renderCustomersList($group),
'categorieReductions' => $this->formatCategoryDiscountList($group->id),
];
return parent::renderView();
}
protected function renderCustomersList(Group $group)
{
$genders = [0 => '?'];
$genders_icon = ['default' => 'unknown.gif'];
foreach (Gender::getGenders() as $gender) {
/* @var Gender $gender */
$genders_icon[$gender->id] = '../genders/' . (int) $gender->id . '.jpg';
$genders[$gender->id] = $gender->name;
}
$this->table = 'customer_group';
$this->lang = false;
$this->list_id = 'customer_group';
$this->actions = [];
$this->addRowAction('edit');
$this->identifier = 'id_customer';
$this->bulk_actions = null;
$this->list_no_link = true;
$this->explicitSelect = true;
$this->list_skip_actions = [];
$this->fields_list = [
'id_customer' => [
'title' => $this->trans('ID', [], 'Admin.Global'),
'align' => 'center',
'filter_key' => 'c!id_customer',
'class' => 'fixed-width-xs',
],
'id_gender' => [
'title' => $this->trans('Social title', [], 'Admin.Global'),
'icon' => $genders_icon,
'list' => $genders,
],
'firstname' => [
'title' => $this->trans('First name', [], 'Admin.Global'),
'maxlength' => 30,
],
'lastname' => [
'title' => $this->trans('Last name', [], 'Admin.Global'),
'maxlength' => 30,
],
'email' => [
'title' => $this->trans('Email address', [], 'Admin.Global'),
'filter_key' => 'c!email',
'orderby' => true,
'maxlength' => 50,
],
'birthday' => [
'title' => $this->trans('Date of birth', [], 'Admin.Global'),
'type' => 'date',
'class' => 'fixed-width-md',
'align' => 'center',
],
'date_add' => [
'title' => $this->trans('Registration date', [], 'Admin.Shopparameters.Feature'),
'type' => 'date',
'class' => 'fixed-width-md',
'align' => 'center',
],
'active' => [
'title' => $this->trans('Enabled', [], 'Admin.Global'),
'align' => 'center',
'class' => 'fixed-width-sm',
'type' => 'bool',
'search' => false,
'orderby' => false,
'filter_key' => 'c!active',
'callback' => 'printOptinIcon',
],
];
$this->_select = 'c.*, a.id_group';
$this->_join = 'LEFT JOIN `' . _DB_PREFIX_ . 'customer` c ON (a.`id_customer` = c.`id_customer`)';
$this->_where = 'AND a.`id_group` = ' . (int) $group->id . ' AND c.`deleted` != 1';
$this->_where .= Shop::addSqlRestriction(Shop::SHARE_CUSTOMER, 'c');
self::$currentIndex = self::$currentIndex . '&id_group=' . (int) $group->id . '&viewgroup';
$this->processFilter();
return parent::renderList();
}
public function printOptinIcon($value, $customer)
{
return $value ? '<i class="icon-check"></i>' : '<i class="icon-remove"></i>';
}
/**
* @return string|void
*
* @throws PrestaShopException
* @throws SmartyException
*/
public function renderForm()
{
if (!($group = $this->loadObject(true))) {
return;
}
$this->fields_form = [
'legend' => [
'title' => $this->trans('Customer group', [], 'Admin.Shopparameters.Feature'),
'icon' => 'icon-group',
],
'submit' => [
'title' => $this->trans('Save', [], 'Admin.Actions'),
],
'input' => [
[
'type' => 'text',
'label' => $this->trans('Name', [], 'Admin.Global'),
'name' => 'name',
'required' => true,
'lang' => true,
'col' => 4,
'hint' => $this->trans('Forbidden characters:', [], 'Admin.Notifications.Info') . ' 0-9!&amp;lt;&amp;gt;,;?=+()@#"<22>{}_$%:',
],
[
'type' => 'text',
'label' => $this->trans('Discount', [], 'Admin.Global'),
'name' => 'reduction',
'suffix' => '%',
'col' => 3,
'hint' => $this->trans('Automatically apply this value as a discount on all products for members of this customer group.', [], 'Admin.Shopparameters.Help'),
],
[
'type' => 'select',
'label' => $this->trans('Price display method', [], 'Admin.Shopparameters.Feature'),
'name' => 'price_display_method',
'col' => 2,
'hint' => $this->trans('How prices are displayed in the order summary for this customer group.', [], 'Admin.Shopparameters.Help'),
'options' => [
'query' => [
[
'id_method' => PS_TAX_EXC,
'name' => $this->trans('Tax excluded', [], 'Admin.Global'),
],
[
'id_method' => PS_TAX_INC,
'name' => $this->trans('Tax included', [], 'Admin.Global'),
],
],
'id' => 'id_method',
'name' => 'name',
],
],
[
'type' => 'switch',
'label' => $this->trans('Show prices', [], 'Admin.Shopparameters.Feature'),
'name' => 'show_prices',
'required' => false,
'class' => 't',
'is_bool' => true,
'values' => [
[
'id' => 'show_prices_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'show_prices_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
'hint' => $this->trans('Customers in this group can view prices.', [], 'Admin.Shopparameters.Help'),
'desc' => $this->trans('Need to hide prices for all groups? Save time, enable catalog mode in Product Settings instead.', [], 'Admin.Shopparameters.Help'),
],
[
'type' => 'group_discount_category',
'label' => $this->trans('Category discount', [], 'Admin.Shopparameters.Feature'),
'name' => 'reduction',
'values' => ($group->id ? $this->formatCategoryDiscountList((int) $group->id) : []),
],
[
'type' => 'modules',
'label' => $this->trans('Modules authorization', [], 'Admin.Shopparameters.Feature'),
'name' => 'auth_modules',
'values' => $this->formatModuleListAuth($group->id),
],
],
];
if (Shop::isFeatureActive()) {
$this->fields_form['input'][] = [
'type' => 'shop',
'label' => $this->trans('Store association', [], 'Admin.Global'),
'name' => 'checkBoxShopAsso',
];
}
if (Tools::getIsset('addgroup')) {
$this->fields_value['price_display_method'] = Configuration::get('PRICE_DISPLAY_METHOD');
}
$this->fields_value['reduction'] = isset($group->reduction) ? $group->reduction : 0;
$tree = new HelperTreeCategories('categories-tree');
$this->tpl_form_vars['categoryTreeView'] = $tree->setRootCategory((int) Category::getRootCategory()->id)->render();
return parent::renderForm();
}
protected function formatCategoryDiscountList(int $id_group)
{
$group_reductions = GroupReduction::getGroupReductions((int) $id_group, $this->context->language->id);
$category_reductions = [];
$category_reduction = Tools::getValue('category_reduction');
foreach ($group_reductions as $category) {
if (is_array($category_reduction) && array_key_exists($category['id_category'], $category_reduction)) {
$category['reduction'] = $category_reduction[$category['id_category']];
}
$category_reductions[(int) $category['id_category']] = [
'path' => Tools::getPath(Context::getContext()->link->getAdminLink('AdminCategories'), (int) $category['id_category']),
'reduction' => (float) $category['reduction'] * 100,
'id_category' => (int) $category['id_category'],
];
}
if (is_array($category_reduction)) {
foreach ($category_reduction as $key => $val) {
if (!array_key_exists($key, $category_reductions)) {
$category_reductions[(int) $key] = [
'path' => Tools::getPath(Context::getContext()->link->getAdminLink('AdminCategories'), $key),
'reduction' => (float) $val * 100,
'id_category' => (int) $key,
];
}
}
}
return $category_reductions;
}
public function formatModuleListAuth($id_group)
{
$modules = Module::getModulesInstalled();
$authorized_modules = '';
$auth_modules = [];
$unauth_modules = [];
$shops = Shop::getContextListShopID();
if ($id_group) {
$authorized_modules = Module::getAuthorizedModules($id_group, $shops);
}
if (is_array($authorized_modules)) {
foreach ($modules as $module) {
$authorized = false;
foreach ($authorized_modules as $auth_module) {
if ($module['id_module'] == $auth_module['id_module']) {
$authorized = true;
}
}
if ($authorized) {
$auth_modules[] = $module;
} else {
$unauth_modules[] = $module;
}
}
} else {
$auth_modules = $modules;
}
$auth_modules_tmp = [];
foreach ($auth_modules as $key => $val) {
if ($module = Module::getInstanceById($val['id_module'])) {
$auth_modules_tmp[] = $module;
}
}
$auth_modules = $auth_modules_tmp;
$unauth_modules_tmp = [];
foreach ($unauth_modules as $key => $val) {
if ($tmp_obj = Module::getInstanceById($val['id_module'])) {
$unauth_modules_tmp[] = $tmp_obj;
}
}
$unauth_modules = $unauth_modules_tmp;
return ['unauth_modules' => $unauth_modules, 'auth_modules' => $auth_modules];
}
public function processSave()
{
if (!$this->validateDiscount(Tools::getValue('reduction'))) {
$this->errors[] = $this->trans('The discount value is incorrect (must be a percentage).', [], 'Admin.Shopparameters.Notification');
} else {
$this->updateCategoryReduction();
$object = parent::processSave();
$this->updateRestrictions();
return $object;
}
}
protected function validateDiscount($reduction)
{
if (!Validate::isPrice($reduction) || $reduction > 100 || $reduction < 0) {
return false;
} else {
return true;
}
}
public function ajaxProcessAddCategoryReduction()
{
$category_reduction = Tools::getValue('category_reduction');
$id_category = Tools::getValue('id_category'); // no cast validation is done with Validate::isUnsignedId($id_category)
$result = [];
if (!Validate::isUnsignedId($id_category)) {
$result['errors'][] = $this->trans('Wrong category ID.', [], 'Admin.Shopparameters.Notification');
$result['hasError'] = true;
} elseif (!$this->validateDiscount($category_reduction)) {
$result['errors'][] = $this->trans('The discount value is incorrect (must be a percentage).', [], 'Admin.Shopparameters.Notification');
$result['hasError'] = true;
} else {
$result['id_category'] = (int) $id_category;
$result['catPath'] = Tools::getPath(self::$currentIndex . '?controller=AdminCategories', (int) $id_category);
$result['discount'] = $category_reduction;
$result['hasError'] = false;
}
die(json_encode($result));
}
/**
* Update (or create) restrictions for modules by group.
*/
protected function updateRestrictions()
{
$id_group = Tools::getValue('id_group');
$auth_modules = Tools::getValue('modulesBoxAuth');
$return = true;
if ($id_group) {
$shops = Shop::getContextListShopID();
if (is_array($auth_modules)) {
$return &= Group::addModulesRestrictions($id_group, $auth_modules, $shops);
}
}
// update module list by hook cache
Cache::clean(Hook::MODULE_LIST_BY_HOOK_KEY . '*');
return $return;
}
protected function updateCategoryReduction()
{
$category_reduction = Tools::getValue('category_reduction');
Db::getInstance()->execute(
'
DELETE FROM `' . _DB_PREFIX_ . 'group_reduction`
WHERE `id_group` = ' . (int) Tools::getValue('id_group')
);
Db::getInstance()->execute(
'
DELETE FROM `' . _DB_PREFIX_ . 'product_group_reduction_cache`
WHERE `id_group` = ' . (int) Tools::getValue('id_group')
);
if (is_array($category_reduction) && count($category_reduction)) {
if (!Configuration::getGlobalValue('PS_GROUP_FEATURE_ACTIVE')) {
Configuration::updateGlobalValue('PS_GROUP_FEATURE_ACTIVE', 1);
}
foreach ($category_reduction as $cat => $reduction) {
if (!Validate::isUnsignedId($cat) || !$this->validateDiscount($reduction)) {
$this->errors[] = $this->trans('The discount value is incorrect.', [], 'Admin.Shopparameters.Notification');
} else {
$category = new Category((int) $cat);
$category->addGroupsIfNoExist((int) Tools::getValue('id_group'));
$group_reduction = new GroupReduction();
$group_reduction->id_group = (int) Tools::getValue('id_group');
$group_reduction->reduction = (float) ($reduction / 100);
$group_reduction->id_category = (int) $cat;
if (!$group_reduction->save()) {
$this->errors[] = $this->trans('You cannot save group reductions.', [], 'Admin.Shopparameters.Notification');
}
}
}
}
}
/**
* Toggle show prices flag.
*/
public function processChangeShowPricesVal()
{
$group = new Group($this->id_object);
if (!Validate::isLoadedObject($group)) {
$this->errors[] = $this->trans('An error occurred while updating this group.', [], 'Admin.Shopparameters.Notification');
}
$update = Db::getInstance()->execute('UPDATE `' . _DB_PREFIX_ . 'group` SET show_prices = ' . ($group->show_prices ? 0 : 1) . ' WHERE `id_group` = ' . (int) $group->id);
if (!$update) {
$this->errors[] = $this->trans('An error occurred while updating this group.', [], 'Admin.Shopparameters.Notification');
}
Tools::clearSmartyCache();
Tools::redirectAdmin(self::$currentIndex . '&token=' . $this->token);
}
public function renderList()
{
$unidentified = new Group((int) Configuration::get('PS_UNIDENTIFIED_GROUP'));
$guest = new Group((int) Configuration::get('PS_GUEST_GROUP'));
$default = new Group((int) Configuration::get('PS_CUSTOMER_GROUP'));
$unidentified_group_information = $this->trans('%group_name% - All persons without a customer account or customers that are not logged in.', ['%group_name%' => '<b>' . htmlentities($unidentified->name[$this->context->language->id]) . '</b>'], 'Admin.Shopparameters.Help');
$guest_group_information = $this->trans('%group_name% - All persons who placed an order through Guest Checkout.', ['%group_name%' => '<b>' . htmlentities($guest->name[$this->context->language->id]) . '</b>'], 'Admin.Shopparameters.Help');
$default_group_information = $this->trans('%group_name% - All persons who created an account on this site.', ['%group_name%' => '<b>' . htmlentities($default->name[$this->context->language->id]) . '</b>'], 'Admin.Shopparameters.Help');
$this->displayInformation($this->trans('PrestaShop has three default customer groups:', [], 'Admin.Shopparameters.Help'));
$this->displayInformation($unidentified_group_information);
$this->displayInformation($guest_group_information);
$this->displayInformation($default_group_information);
return parent::renderList();
}
public function displayEditLink($token, $id)
{
$tpl = $this->createTemplate('helpers/list/list_action_edit.tpl');
if (!array_key_exists('Edit', self::$cache_lang)) {
self::$cache_lang['Edit'] = $this->trans('Edit', [], 'Admin.Actions');
}
$href = self::$currentIndex . '&' . $this->identifier . '=' . $id . '&update' . $this->table . '&token=' . ($token != null ? $token : $this->token);
if ($this->display == 'view') {
$href = Context::getContext()->link->getAdminLink('AdminCustomers', true, [], [
'id_customer' => $id,
'updatecustomer' => 1,
'back' => urlencode($href),
]);
}
$tpl->assign([
'href' => $href,
'action' => self::$cache_lang['Edit'],
'id' => $id,
]);
return $tpl->fetch();
}
/**
* AdminController::initContent() override.
*
* @see AdminController::initContent()
*/
public function initContent()
{
if (!Group::isFeatureActive()) {
$adminPerformanceUrl = $this->context->link->getAdminLink('AdminPerformance');
$url = '<a href="' . $adminPerformanceUrl . '">' . $this->trans('Performance', [], 'Admin.Global') . '</a>';
$this->displayWarning($this->trans('This feature has been disabled. You can activate it here: %url%.', ['%url%' => $url], 'Admin.Catalog.Notification'));
$this->context->smarty->assign([
'content' => $this->content,
]);
return;
}
parent::initContent();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,550 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class AdminModulesPositionsControllerCore extends AdminController
{
protected $display_key = 0;
public function __construct()
{
$this->bootstrap = true;
parent::__construct();
}
public function postProcess()
{
// Getting key value for display
if (Tools::getValue('show_modules') && (string) Tools::getValue('show_modules') != 'all') {
$this->display_key = (int) Tools::getValue('show_modules');
}
$this->addjQueryPlugin([
'select2',
]);
$this->addJS([
_PS_JS_DIR_ . 'admin/modules-position.js',
_PS_JS_DIR_ . 'jquery/plugins/select2/select2_locale_' . $this->context->language->iso_code . '.js',
]);
$baseUrl = $this->context->link->getAdminLink('AdminModulesPositions');
if (strpos($baseUrl, '?') === false) {
$baseUrl .= '?';
}
// Change position in hook
if (array_key_exists('changePosition', $_GET)) {
if ($this->access('edit')) {
$id_module = (int) Tools::getValue('id_module');
$id_hook = (int) Tools::getValue('id_hook');
$module = Module::getInstanceById($id_module);
if (Validate::isLoadedObject($module)) {
$module->updatePosition($id_hook, (bool) Tools::getValue('direction'));
Tools::redirectAdmin($baseUrl . ($this->display_key ? '&show_modules=' . $this->display_key : '') . '&token=' . $this->token);
} else {
$this->errors[] = $this->trans('This module cannot be loaded.', [], 'Admin.Modules.Notification');
}
} else {
$this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error');
}
} elseif (Tools::isSubmit('submitAddToHook')) {
// Add new module in hook
if ($this->access('add')) {
// Getting vars...
$id_module = (int) Tools::getValue('id_module');
$module = Module::getInstanceById($id_module);
$id_hook = (int) Tools::getValue('id_hook');
$hook = new Hook($id_hook);
if (!$id_module || !Validate::isLoadedObject($module)) {
$this->errors[] = $this->trans('This module cannot be loaded.', [], 'Admin.Modules.Notification');
} elseif (!$id_hook || !Validate::isLoadedObject($hook)) {
$this->errors[] = $this->trans('Hook cannot be loaded.', [], 'Admin.Modules.Notification');
} elseif (Hook::getModulesFromHook($id_hook, $id_module)) {
$this->errors[] = $this->trans('This module has already been transplanted to this hook.', [], 'Admin.Modules.Notification');
} elseif (!$module->isHookableOn($hook->name)) {
$this->errors[] = $this->trans('This module cannot be transplanted to this hook.', [], 'Admin.Modules.Notification');
} else {
// Adding vars...
if (!$module->registerHook($hook->name, Shop::getContextListShopID())) {
$this->errors[] = $this->trans('An error occurred while transplanting the module to its hook.', [], 'Admin.Modules.Notification');
} else {
$exceptions = Tools::getValue('exceptions');
$exceptions = (isset($exceptions[0])) ? $exceptions[0] : [];
$exceptions = explode(',', str_replace(' ', '', $exceptions));
$exceptions = array_unique($exceptions);
foreach ($exceptions as $key => $except) {
if (empty($except)) {
unset($exceptions[$key]);
} elseif (!Validate::isFileName($except)) {
$this->errors[] = $this->trans('No valid value for field exceptions has been defined.', [], 'Admin.Notifications.Error');
}
}
if (!$this->errors && !$module->registerExceptions($id_hook, $exceptions, Shop::getContextListShopID())) {
$this->errors[] = $this->trans('An error occurred while transplanting the module to its hook.', [], 'Admin.Notifications.Error');
}
}
if (!$this->errors) {
Tools::redirectAdmin($baseUrl . '&conf=16' . ($this->display_key ? '&show_modules=' . $this->display_key : '') . '&token=' . $this->token);
}
}
} else {
$this->errors[] = $this->trans('You do not have permission to add this.', [], 'Admin.Notifications.Error');
}
} elseif (Tools::isSubmit('submitEditGraft')) {
// Edit module from hook
if ($this->access('add')) {
// Getting vars...
$id_module = (int) Tools::getValue('id_module');
$module = Module::getInstanceById($id_module);
$id_hook = (int) Tools::getValue('id_hook');
$new_hook = (int) Tools::getValue('new_hook');
$hook = new Hook($new_hook);
if (!$id_module || !Validate::isLoadedObject($module)) {
$this->errors[] = $this->trans('This module cannot be loaded.', [], 'Admin.Modules.Notification');
} elseif (!$id_hook || !Validate::isLoadedObject($hook)) {
$this->errors[] = $this->trans('Hook cannot be loaded.', [], 'Admin.Modules.Notification');
} else {
if ($new_hook !== $id_hook) {
/* Connect module to a newer hook */
if (!$module->registerHook($hook->name, Shop::getContextListShopID())) {
$this->errors[] = $this->trans('An error occurred while transplanting the module to its hook.', [], 'Admin.Modules.Notification');
}
/* Unregister module from hook & exceptions linked to module */
if (!$module->unregisterHook($id_hook, Shop::getContextListShopID())
|| !$module->unregisterExceptions($id_hook, Shop::getContextListShopID())) {
$this->errors[] = $this->trans('An error occurred while deleting the module from its hook.', [], 'Admin.Modules.Notification');
}
$id_hook = $new_hook;
}
$exceptions = Tools::getValue('exceptions');
if (is_array($exceptions)) {
foreach ($exceptions as $id => $exception) {
$exception = explode(',', str_replace(' ', '', $exception));
$exception = array_unique($exception);
// Check files name
foreach ($exception as $except) {
if (!empty($except) && !Validate::isFileName($except)) {
$this->errors[] = $this->trans('No valid value for field exceptions has been defined.', [], 'Admin.Notifications.Error');
}
}
$exceptions[$id] = $exception;
}
// Add files exceptions
if (!$module->editExceptions($id_hook, $exceptions)) {
$this->errors[] = $this->trans('An error occurred while transplanting the module to its hook.', [], 'Admin.Modules.Notification');
}
if (!$this->errors) {
Tools::redirectAdmin($baseUrl . '&conf=16' . ($this->display_key ? '&show_modules=' . $this->display_key : '') . '&token=' . $this->token);
}
} else {
$exceptions = explode(',', str_replace(' ', '', $exceptions));
$exceptions = array_unique($exceptions);
// Check files name
foreach ($exceptions as $except) {
if (!empty($except) && !Validate::isFileName($except)) {
$this->errors[] = $this->trans('No valid value for field exceptions has been defined.', [], 'Admin.Notifications.Error');
}
}
// Add files exceptions
if (!$module->editExceptions($id_hook, $exceptions)) {
$this->errors[] = $this->trans('An error occurred while transplanting the module to its hook.', [], 'Admin.Modules.Notification');
} else {
Tools::redirectAdmin($baseUrl . '&conf=16' . ($this->display_key ? '&show_modules=' . $this->display_key : '') . '&token=' . $this->token);
}
}
}
} else {
$this->errors[] = $this->trans('You do not have permission to add this.', [], 'Admin.Notifications.Error');
}
} elseif (array_key_exists('deleteGraft', $_GET)) {
// Delete module from hook
if ($this->access('delete')) {
$id_module = (int) Tools::getValue('id_module');
$module = Module::getInstanceById($id_module);
$id_hook = (int) Tools::getValue('id_hook');
$hook = new Hook($id_hook);
if (!Validate::isLoadedObject($module)) {
$this->errors[] = $this->trans('This module cannot be loaded.', [], 'Admin.Modules.Notification');
} elseif (!$id_hook || !Validate::isLoadedObject($hook)) {
$this->errors[] = $this->trans('Hook cannot be loaded.', [], 'Admin.Modules.Notification');
} else {
if (!$module->unregisterHook($id_hook, Shop::getContextListShopID())
|| !$module->unregisterExceptions($id_hook, Shop::getContextListShopID())) {
$this->errors[] = $this->trans('An error occurred while deleting the module from its hook.', [], 'Admin.Modules.Notification');
} else {
Tools::redirectAdmin($baseUrl . '&conf=17' . ($this->display_key ? '&show_modules=' . $this->display_key : '') . '&token=' . $this->token);
}
}
} else {
$this->errors[] = $this->trans('You do not have permission to delete this.', [], 'Admin.Notifications.Error');
}
} elseif (Tools::isSubmit('unhookform')) {
if (!($unhooks = Tools::getValue('unhooks')) || !is_array($unhooks)) {
$this->errors[] = $this->trans('Please select a module to unhook.', [], 'Admin.Modules.Notification');
} else {
foreach ($unhooks as $unhook) {
$explode = explode('_', $unhook);
$id_hook = $explode[0];
$id_module = $explode[1];
$module = Module::getInstanceById((int) $id_module);
$hook = new Hook((int) $id_hook);
if (!Validate::isLoadedObject($module)) {
$this->errors[] = $this->trans('This module cannot be loaded.', [], 'Admin.Modules.Notification');
} elseif (!$id_hook || !Validate::isLoadedObject($hook)) {
$this->errors[] = $this->trans('Hook cannot be loaded.', [], 'Admin.Modules.Notification');
} else {
if (!$module->unregisterHook((int) $id_hook) || !$module->unregisterExceptions((int) $id_hook)) {
$this->errors[] = $this->trans('An error occurred while deleting the module from its hook.', [], 'Admin.Modules.Notification');
}
}
}
if (!count($this->errors)) {
Tools::redirectAdmin($baseUrl . '&conf=17' . ($this->display_key ? '&show_modules=' . $this->display_key : '') . '&token=' . $this->token);
}
}
} else {
parent::postProcess();
}
}
/**
* AdminController::initContent() override.
*
* @see AdminController::initContent()
*/
public function initContent()
{
$this->addjqueryPlugin('sortable');
if (array_key_exists('addToHook', $_GET) || array_key_exists('editGraft', $_GET) || (Tools::isSubmit('submitAddToHook') && $this->errors)) {
$this->display = 'edit';
$this->content .= $this->renderForm();
}
$this->context->smarty->assign([
'content' => $this->content,
]);
}
public function initPageHeaderToolbar()
{
return parent::initPageHeaderToolbar();
}
public function renderForm()
{
// Init toolbar
$this->initToolbarTitle();
// toolbar (save, cancel, new, ..)
$this->initToolbar();
$id_module = (int) Tools::getValue('id_module');
$id_hook = (int) Tools::getValue('id_hook');
$show_modules = (int) Tools::getValue('show_modules');
if (Tools::isSubmit('editGraft')) {
// Check auth for this page
if (!$id_module || !$id_hook) {
Tools::redirectAdmin(self::$currentIndex . '&token=' . $this->token);
}
$sql = 'SELECT id_module
FROM ' . _DB_PREFIX_ . 'hook_module
WHERE id_module = ' . $id_module . '
AND id_hook = ' . $id_hook . '
AND id_shop IN(' . implode(', ', Shop::getContextListShopID()) . ')';
if (!Db::getInstance()->getValue($sql)) {
Tools::redirectAdmin(self::$currentIndex . '&token=' . $this->token);
}
$sl_module = Module::getInstanceById($id_module);
$excepts_list = $sl_module->getExceptions($id_hook, true);
$excepts_diff = false;
$excepts = '';
if ($excepts_list) {
$first = current($excepts_list);
foreach ($excepts_list as $k => $v) {
if (array_diff($v, $first) || array_diff($first, $v)) {
$excepts_diff = true;
}
}
if (!$excepts_diff) {
$excepts = implode(', ', $first);
}
}
} else {
$excepts_diff = false;
$excepts_list = Tools::getValue('exceptions', [[]]);
}
$modules = Module::getModulesInstalled(0);
$instances = [];
foreach ($modules as $module) {
if ($tmp_instance = Module::getInstanceById($module['id_module'])) {
$instances[$tmp_instance->displayName] = $tmp_instance;
}
}
ksort($instances);
$modules = $instances;
$hooks = [];
if ($show_modules || (Tools::getValue('id_hook') > 0)) {
$module_instance = Module::getInstanceById((int) Tools::getValue('id_module', $show_modules));
$hooks = $module_instance->getPossibleHooksList();
}
$exception_list_diff = [];
foreach ($excepts_list as $shop_id => $file_list) {
$exception_list_diff[] = $this->displayModuleExceptionList($file_list, $shop_id);
}
$tpl = $this->createTemplate('form.tpl');
$tpl->assign([
'url_submit' => self::$currentIndex . '&token=' . $this->token,
'edit_graft' => Tools::isSubmit('editGraft'),
'id_module' => (int) Tools::getValue('id_module'),
'id_hook' => (int) Tools::getValue('id_hook'),
'show_modules' => $show_modules,
'hooks' => $hooks,
'exception_list' => $this->displayModuleExceptionList(array_shift($excepts_list), 0),
'exception_list_diff' => $exception_list_diff,
'except_diff' => $excepts_diff,
'display_key' => $this->display_key,
'modules' => $modules,
'show_toolbar' => true,
'toolbar_btn' => $this->toolbar_btn,
'toolbar_scroll' => $this->toolbar_scroll,
'title' => $this->toolbar_title,
'table' => 'hook_module',
]);
return $tpl->fetch();
}
public function displayModuleExceptionList($file_list, $shop_id)
{
if (!is_array($file_list)) {
$file_list = ($file_list) ? [$file_list] : [];
}
$content = '<p><input type="text" name="exceptions[' . $shop_id . ']" value="' . implode(', ', $file_list) . '" id="em_text_' . $shop_id . '" placeholder="' . $this->trans('E.g. address, addresses, attachment', [], 'Admin.Design.Help') . '"/></p>';
if ($shop_id) {
$shop = new Shop($shop_id);
$content .= ' (' . $shop->name . ')';
}
$content .= '<p>
<select size="25" id="em_list_' . $shop_id . '" multiple="multiple">
<option disabled="disabled">'
. $this->trans('___________ CUSTOM ___________', [], 'Admin.Design.Feature')
. '</option>';
$controllers = Dispatcher::getControllersPhpselfList(_PS_FRONT_CONTROLLER_DIR_);
asort($controllers);
foreach ($file_list as $k => $v) {
if (!in_array($v, $controllers)) {
$content .= '<option value="' . $v . '">' . $v . '</option>';
}
}
$content .= '<option disabled="disabled">' . $this->trans('____________ CORE ____________', [], 'Admin.Design.Feature') . '</option>';
foreach ($controllers as $k => $v) {
$content .= '<option value="' . $v . '">' . $v . '</option>';
}
$modules_controllers_type = ['admin' => $this->trans('Admin modules controller', [], 'Admin.Design.Feature'), 'front' => $this->trans('Front modules controller', [], 'Admin.Design.Feature')];
foreach ($modules_controllers_type as $type => $label) {
$content .= '<option disabled="disabled">____________ ' . $label . ' ____________</option>';
$all_modules_controllers = Dispatcher::getModuleControllers($type);
foreach ($all_modules_controllers as $module => $modules_controllers) {
foreach ($modules_controllers as $cont) {
$content .= '<option value="module-' . $module . '-' . $cont . '">module-' . $module . '-' . $cont . '</option>';
}
}
}
$content .= '</select>
</p>';
return $content;
}
public function ajaxProcessUpdatePositions()
{
if ($this->access('edit')) {
$id_module = (int) Tools::getValue('id_module');
$id_hook = (int) Tools::getValue('id_hook');
$way = (bool) Tools::getValue('way');
$positions = Tools::getValue((string) $id_hook);
$position = (is_array($positions)) ? array_search($id_hook . '_' . $id_module, $positions) : null;
$module = Module::getInstanceById($id_module);
if (Validate::isLoadedObject($module)) {
if ($module->updatePosition($id_hook, $way, $position)) {
die(true);
} else {
die('{"hasError" : true, "errors" : "Cannot update module position."}');
}
} else {
die('{"hasError" : true, "errors" : "This module cannot be loaded."}');
}
}
}
public function ajaxProcessGetHookableList()
{
if ($this->access('view')) {
/* PrestaShop demo mode */
if (_PS_MODE_DEMO_) {
die('{"hasError" : true, "errors" : ["Live Edit: This functionality has been disabled."]}');
}
if (!count(Tools::getValue('hooks_list'))) {
die('{"hasError" : true, "errors" : ["Live Edit: no module on this page."]}');
}
$modules_list = Tools::getValue('modules_list');
$hooks_list = Tools::getValue('hooks_list');
$hookableList = [];
foreach ($modules_list as $module) {
$module = trim($module);
if (!$module) {
continue;
}
if (!Validate::isModuleName($module)) {
die('{"hasError" : true, "errors" : ["Live Edit: module is invalid."]}');
}
$moduleInstance = Module::getInstanceByName($module);
foreach ($hooks_list as $hook_name) {
$hook_name = trim($hook_name);
if (!$hook_name) {
continue;
}
if (!array_key_exists($hook_name, $hookableList)) {
$hookableList[$hook_name] = [];
}
if ($moduleInstance->isHookableOn($hook_name)) {
$hookableList[$hook_name][] = str_replace('_', '-', $module);
}
}
}
$hookableList['hasError'] = false;
die(json_encode($hookableList));
}
}
public function ajaxProcessGetHookableModuleList()
{
if ($this->access('view')) {
/* PrestaShop demo mode */
if (_PS_MODE_DEMO_) {
die('{"hasError" : true, "errors" : ["Live Edit: This functionality has been disabled."]}');
}
/* PrestaShop demo mode */
$hook_name = Tools::getValue('hook');
$hookableModulesList = [];
$modules = Db::getInstance()->executeS('SELECT id_module, name FROM `' . _DB_PREFIX_ . 'module` ');
foreach ($modules as $module) {
if (!Validate::isModuleName($module['name'])) {
continue;
}
if (file_exists(_PS_MODULE_DIR_ . $module['name'] . '/' . $module['name'] . '.php')) {
include_once _PS_MODULE_DIR_ . $module['name'] . '/' . $module['name'] . '.php';
/** @var Module $mod */
$mod = new $module['name']();
if ($mod->isHookableOn($hook_name)) {
$hookableModulesList[] = ['id' => (int) $mod->id, 'name' => $mod->displayName, 'display' => Hook::exec($hook_name, [], (int) $mod->id)];
}
}
}
die(json_encode($hookableModulesList));
}
}
public function ajaxProcessSaveHook()
{
if ($this->access('edit')) {
/* PrestaShop demo mode */
if (_PS_MODE_DEMO_) {
die('{"hasError" : true, "errors" : ["Live Edit: This functionality has been disabled."]}');
}
$hooks_list = explode(',', Tools::getValue('hooks_list'));
$id_shop = (int) Tools::getValue('id_shop');
if (!$id_shop) {
$id_shop = Context::getContext()->shop->id;
}
$res = true;
$hookableList = [];
// $_POST['hook'] is an array of id_module
$hooks_list = Tools::getValue('hook');
foreach ($hooks_list as $id_hook => $modules) {
// 1st, drop all previous hooked modules
$sql = 'DELETE FROM `' . _DB_PREFIX_ . 'hook_module` WHERE `id_hook` = ' . (int) $id_hook . ' AND id_shop = ' . (int) $id_shop;
$res &= Db::getInstance()->execute($sql);
$i = 1;
$value = '';
$ids = [];
// then prepare sql query to rehook all chosen modules(id_module, id_shop, id_hook, position)
// position is i (autoincremented)
if (is_array($modules) && count($modules)) {
foreach ($modules as $id_module) {
if ($id_module && !in_array($id_module, $ids)) {
$ids[] = (int) $id_module;
$value .= '(' . (int) $id_module . ', ' . (int) $id_shop . ', ' . (int) $id_hook . ', ' . (int) $i . '),';
}
++$i;
}
if ($value) {
$value = rtrim($value, ',');
$res &= Db::getInstance()->execute('INSERT INTO `' . _DB_PREFIX_ . 'hook_module` (id_module, id_shop, id_hook, position) VALUES ' . $value);
}
}
}
if ($res) {
$hasError = true;
} else {
$hasError = false;
}
die('{"hasError" : false, "errors" : ""}');
}
}
/**
* Return a json array containing the possible hooks for a module.
*/
public function ajaxProcessGetPossibleHookingListForModule()
{
if ($this->access('view')) {
$module_id = (int) Tools::getValue('module_id');
if ($module_id == 0) {
die('{"hasError" : true, "errors" : ["Wrong module ID."]}');
}
$module_instance = Module::getInstanceById($module_id);
die(json_encode($module_instance->getPossibleHooksList()));
}
}
}

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 AdminNotFoundControllerCore extends AdminController
{
public function __construct()
{
$this->bootstrap = true;
parent::__construct();
}
public function checkAccess()
{
return true;
}
public function viewAccess($disable = false)
{
return true;
}
/**
* AdminController::initContent() override.
*
* @see AdminController::initContent()
*/
public function initContent()
{
$this->errors[] = $this->trans('Page not found', [], 'Admin.Notifications.Error');
$tpl_vars['controller'] = Tools::getValue('controllerUri', Tools::getValue('controller'));
$this->context->smarty->assign($tpl_vars);
parent::initContent();
}
}

View File

@@ -0,0 +1,179 @@
<?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\SymfonyContainer;
class AdminPdfControllerCore extends AdminController
{
public function postProcess()
{
parent::postProcess();
// We want to be sure that displaying PDF is the last thing this controller will do
exit;
}
public function initProcess()
{
parent::initProcess();
$this->checkCacheFolder();
$access = Profile::getProfileAccess($this->context->employee->id_profile, (int) Tab::getIdFromClassName('AdminOrders'));
if ($access['view'] === '1' && ($action = Tools::getValue('submitAction'))) {
$this->action = $action;
} else {
$this->errors[] = $this->trans('You do not have permission to view this.', [], 'Admin.Notifications.Error');
}
}
public function checkCacheFolder()
{
if (!is_dir(_PS_CACHE_DIR_ . 'tcpdf/')) {
mkdir(_PS_CACHE_DIR_ . 'tcpdf/');
}
}
public function processGenerateInvoicePdf()
{
if (Tools::isSubmit('id_order')) {
$sfContainer = SymfonyContainer::getInstance();
$sfRouter = $sfContainer->get('router');
Tools::redirectAdmin($sfRouter->generate(
'admin_orders_generate_invoice_pdf',
['orderId' => (int) Tools::getValue('id_order')]
));
} elseif (Tools::isSubmit('id_order_invoice')) {
$this->generateInvoicePDFByIdOrderInvoice(Tools::getValue('id_order_invoice'));
} else {
die($this->trans('The order ID -- or the invoice order ID -- is missing.', [], 'Admin.Orderscustomers.Notification'));
}
}
public function processGenerateOrderSlipPDF()
{
$order_slip = new OrderSlip((int) Tools::getValue('id_order_slip'));
$order = new Order((int) $order_slip->id_order);
if (!Validate::isLoadedObject($order)) {
die($this->trans('The order cannot be found within your database.', [], 'Admin.Orderscustomers.Notification'));
}
$this->generatePDF($order_slip, PDF::TEMPLATE_ORDER_SLIP);
}
public function processGenerateDeliverySlipPDF()
{
$sfContainer = SymfonyContainer::getInstance();
$sfRouter = $sfContainer->get('router');
if (Tools::isSubmit('id_order')) {
Tools::redirectAdmin($sfRouter->generate(
'admin_orders_generate_delivery_slip_pdf',
['orderId' => (int) Tools::getValue('id_order')]
));
} elseif (Tools::isSubmit('id_order_invoice')) {
$this->generateDeliverySlipPDFByIdOrderInvoice((int) Tools::getValue('id_order_invoice'));
} elseif (Tools::isSubmit('id_delivery')) {
$order = Order::getByDelivery((int) Tools::getValue('id_delivery'));
Tools::redirectAdmin($sfRouter->generate(
'admin_orders_generate_delivery_slip_pdf',
['orderId' => (int) $order->id]
));
} else {
die($this->trans('The order ID -- or the invoice order ID -- is missing.', [], 'Admin.Orderscustomers.Notification'));
}
}
public function processGenerateInvoicesPDF()
{
$order_invoice_collection = OrderInvoice::getByDateInterval(Tools::getValue('date_from'), Tools::getValue('date_to'));
if (!count($order_invoice_collection)) {
die($this->trans('No invoice was found.', [], 'Admin.Orderscustomers.Notification'));
}
$this->generatePDF($order_invoice_collection, PDF::TEMPLATE_INVOICE);
}
public function processGenerateInvoicesPDF2()
{
$order_invoice_collection = [];
foreach (explode('-', Tools::getValue('id_order_state')) as $id_order_state) {
if (is_array($order_invoices = OrderInvoice::getByStatus((int) $id_order_state))) {
$order_invoice_collection = array_merge($order_invoices, $order_invoice_collection);
}
}
if (!count($order_invoice_collection)) {
die($this->trans('No invoice was found.', [], 'Admin.Orderscustomers.Notification'));
}
$this->generatePDF($order_invoice_collection, PDF::TEMPLATE_INVOICE);
}
public function processGenerateOrderSlipsPDF()
{
$id_order_slips_list = OrderSlip::getSlipsIdByDate(Tools::getValue('date_from'), Tools::getValue('date_to'));
if (!count($id_order_slips_list)) {
die($this->trans('No order slips were found.', [], 'Admin.Orderscustomers.Notification'));
}
$order_slips = [];
foreach ($id_order_slips_list as $id_order_slips) {
$order_slips[] = new OrderSlip((int) $id_order_slips);
}
$this->generatePDF($order_slips, PDF::TEMPLATE_ORDER_SLIP);
}
public function processGenerateDeliverySlipsPDF()
{
$order_invoice_collection = OrderInvoice::getByDeliveryDateInterval(Tools::getValue('date_from'), Tools::getValue('date_to'));
if (!count($order_invoice_collection)) {
die($this->trans('No invoice was found.', [], 'Admin.Orderscustomers.Notification'));
}
$this->generatePDF($order_invoice_collection, PDF::TEMPLATE_DELIVERY_SLIP);
}
/**
* @deprecated Since 9.0 and will be removed in 10.0
*/
public function processGenerateSupplyOrderFormPDF()
{
@trigger_error(sprintf(
'%s is deprecated since 9.0 and will be removed in 10.0.',
__METHOD__
), E_USER_DEPRECATED);
die;
}
public function generateDeliverySlipPDFByIdOrderInvoice($id_order_invoice)
{
$order_invoice = new OrderInvoice((int) $id_order_invoice);
if (!Validate::isLoadedObject($order_invoice)) {
throw new PrestaShopException('Can\'t load Order Invoice object');
}
$this->generatePDF($order_invoice, PDF::TEMPLATE_DELIVERY_SLIP);
}
public function generateInvoicePDFByIdOrderInvoice($id_order_invoice)
{
$order_invoice = new OrderInvoice((int) $id_order_invoice);
if (!Validate::isLoadedObject($order_invoice)) {
die($this->trans('The order invoice cannot be found within your database.', [], 'Admin.Orderscustomers.Notification'));
}
Hook::exec('actionPDFInvoiceRender', ['order_invoice_list' => [$order_invoice]]);
$this->generatePDF($order_invoice, PDF::TEMPLATE_INVOICE);
}
public function generatePDF($object, $template)
{
$pdf = new PDF($object, $template, Context::getContext()->smarty);
$pdf->render();
}
}

View File

@@ -0,0 +1,221 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property QuickAccess $object
*/
class AdminQuickAccessesControllerCore extends AdminController
{
public function __construct()
{
$this->bootstrap = true;
$this->table = 'quick_access';
$this->className = 'QuickAccess';
$this->lang = true;
$this->addRowAction('edit');
$this->addRowAction('delete');
parent::__construct();
if (!Tools::getValue('realedit')) {
$this->deleted = false;
}
$this->bulk_actions = [
'delete' => [
'text' => $this->trans('Delete selected', [], 'Admin.Actions'),
'confirm' => $this->trans('Delete selected items?', [], 'Admin.Notifications.Warning'),
'icon' => 'icon-trash',
],
];
$this->fields_list = [
'id_quick_access' => [
'title' => $this->trans('ID', [], 'Admin.Global'),
'align' => 'center',
'class' => 'fixed-width-xs',
],
'name' => [
'title' => $this->trans('Name', [], 'Admin.Global'),
],
'link' => [
'title' => $this->trans('Link', [], 'Admin.Navigation.Header'),
],
'new_window' => [
'title' => $this->trans('New window', [], 'Admin.Navigation.Header'),
'align' => 'center',
'type' => 'bool',
'active' => 'new_window',
'class' => 'fixed-width-sm',
],
];
$this->fields_form = [
'legend' => [
'title' => $this->trans('Quick Access menu', [], 'Admin.Navigation.Header'),
'icon' => 'icon-align-justify',
],
'input' => [
[
'type' => 'text',
'label' => $this->trans('Name', [], 'Admin.Global'),
'name' => 'name',
'lang' => true,
'maxlength' => 32,
'required' => true,
'hint' => $this->trans('Forbidden characters:', [], 'Admin.Notifications.Info') . ' &lt;&gt;{}',
],
[
'type' => 'text',
'label' => $this->trans('URL', [], 'Admin.Global'),
'name' => 'link',
'maxlength' => 128,
'required' => true,
'hint' => $this->trans('If it\'s a URL that comes from your back office, you MUST remove the security token.', [], 'Admin.Navigation.Header'),
],
[
'type' => 'switch',
'label' => $this->trans('Open in new window', [], 'Admin.Navigation.Header'),
'name' => 'new_window',
'required' => false,
'values' => [
[
'id' => 'new_window_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'new_window_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
],
],
'submit' => [
'title' => $this->trans('Save', [], 'Admin.Actions'),
],
];
}
public function getTabSlug()
{
return 'ROLE_MOD_TAB_ADMINACCESS_';
}
public function initPageHeaderToolbar()
{
if (empty($this->display)) {
$this->page_header_toolbar_btn['new_quick_access'] = [
'href' => self::$currentIndex . '&addquick_access&token=' . $this->token,
'desc' => $this->trans('Add new quick access', [], 'Admin.Navigation.Header'),
'icon' => 'process-icon-new',
];
}
parent::initPageHeaderToolbar();
}
public function initProcess()
{
if ((isset($_GET['new_window' . $this->table]) || isset($_GET['new_window'])) && Tools::getValue($this->identifier)) {
if ($this->access('edit')) {
$this->action = 'newWindow';
} else {
$this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error');
}
}
parent::initProcess();
}
public function getQuickAccessesList()
{
$links = QuickAccess::getQuickAccessesWithToken($this->context->language->id, (int) $this->context->employee->id);
return json_encode($links);
}
public function addQuickLink()
{
if (empty($this->className)) {
return false;
}
$this->validateRules();
if (count($this->errors) <= 0) {
$this->object = new $this->className();
$this->copyFromPost($this->object, $this->table);
$exists = Db::getInstance()->getValue('SELECT id_quick_access FROM ' . _DB_PREFIX_ . 'quick_access WHERE link = "' . pSQL($this->object->link) . '"');
if ($exists) {
return true;
}
$this->beforeAdd($this->object);
if (method_exists($this->object, 'add') && !$this->object->add()) {
$this->errors[] = $this->trans('An error occurred while creating an object.', [], 'Admin.Notifications.Error') .
' <b>' . $this->table . ' (' . Db::getInstance()->getMsgError() . ')</b>';
} elseif (($_POST[$this->identifier] = $this->object->id) && $this->postImage($this->object->id) && empty($this->errors) && $this->_redirect) {
// voluntary do affectation here
PrestaShopLogger::addLog($this->trans('%class_name% addition', ['%class_name%' => $this->className], 'Admin.Advparameters.Feature'), 1, null, $this->className, (int) $this->object->id, true, (int) $this->context->employee->id);
$this->afterAdd($this->object);
}
}
$this->errors = array_unique($this->errors);
if (!empty($this->errors)) {
$this->errors['has_errors'] = true;
$this->ajaxRender(json_encode($this->errors));
return false;
}
return $this->getQuickAccessesList();
}
public function processDelete()
{
parent::processDelete();
return $this->getQuickAccessesList();
}
public function ajaxProcessGetUrl()
{
if (Tools::strtolower(Tools::getValue('method')) === 'add') {
$params['new_window'] = 0;
$params['name_' . (int) Configuration::get('PS_LANG_DEFAULT')] = Tools::getValue('name');
$params['link'] = Tools::getValue('url');
$params['submitAddquick_access'] = 1;
unset($_POST['name']);
$_POST = array_merge($_POST, $params);
die($this->addQuickLink());
} elseif (Tools::strtolower(Tools::getValue('method')) === 'remove') {
$params['deletequick_access'] = 1;
$_POST = array_merge($_POST, $params);
die($this->processDelete());
}
}
public function processNewWindow()
{
if (Validate::isLoadedObject($object = $this->loadObject())) {
/** @var QuickAccess $object */
if ($object->toggleNewWindow()) {
$this->redirect_after = self::$currentIndex . '&conf=5&token=' . $this->token;
} else {
$this->errors[] = $this->trans('An error occurred while updating new window property.', [], 'Admin.Navigation.Notification');
}
} else {
$this->errors[] = $this->trans('An error occurred while updating the new window property for this object.', [], 'Admin.Navigation.Notification') .
' <b>' . $this->table . '</b> ' .
$this->trans('(cannot load object)', [], 'Admin.Notifications.Error');
}
return $object;
}
}

View File

@@ -0,0 +1,287 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property OrderReturn $object
*/
class AdminReturnControllerCore extends AdminController
{
public function __construct()
{
$this->bootstrap = true;
$this->table = 'order_return';
$this->className = 'OrderReturn';
$this->colorOnBackground = true;
parent::__construct();
$this->_select = 'ors.color, orsl.`name`, o.`id_shop`';
$this->_join = 'LEFT JOIN ' . _DB_PREFIX_ . 'order_return_state ors ON (ors.`id_order_return_state` = a.`state`)';
$this->_join .= 'LEFT JOIN ' . _DB_PREFIX_ . 'order_return_state_lang orsl ON (orsl.`id_order_return_state` = a.`state` AND orsl.`id_lang` = ' . (int) $this->context->language->id . ')';
$this->_join .= ' LEFT JOIN ' . _DB_PREFIX_ . 'orders o ON (o.`id_order` = a.`id_order`)';
$this->_orderBy = 'id_order_return';
$this->_orderWay = 'DESC';
$this->fields_list = [
'id_order_return' => ['title' => $this->trans('ID', [], 'Admin.Global'), 'align' => 'center', 'width' => 25],
'id_order' => ['title' => $this->trans('Order ID', [], 'Admin.Orderscustomers.Feature'), 'width' => 100, 'align' => 'center', 'filter_key' => 'a!id_order', 'havingFilter' => true],
'name' => ['title' => $this->trans('Status', [], 'Admin.Global'), 'color' => 'color', 'width' => 'auto', 'align' => 'left'],
'date_add' => ['title' => $this->trans('Date issued', [], 'Admin.Orderscustomers.Feature'), 'width' => 150, 'type' => 'date', 'align' => 'right', 'filter_key' => 'a!date_add'],
];
$this->fields_options = [
'general' => [
'title' => $this->trans('Merchandise return (RMA) options', [], 'Admin.Orderscustomers.Feature'),
'fields' => [
'PS_ORDER_RETURN' => [
'title' => $this->trans('Enable returns', [], 'Admin.Orderscustomers.Feature'),
'desc' => $this->trans('Would you like to allow merchandise returns in your shop?', [], 'Admin.Orderscustomers.Help'),
'cast' => 'intval', 'type' => 'bool', ],
'PS_ORDER_RETURN_NB_DAYS' => [
'title' => $this->trans('Time limit of validity', [], 'Admin.Orderscustomers.Feature'),
'desc' => $this->trans('How many days after the delivery date does the customer have to return a product?', [], 'Admin.Orderscustomers.Help'),
'cast' => 'intval',
'type' => 'text',
'size' => '2', ],
'PS_RETURN_PREFIX' => [
'title' => $this->trans('Returns prefix', [], 'Admin.Orderscustomers.Feature'),
'desc' => $this->trans('Prefix used for return name (e.g. RE00001).', [], 'Admin.Orderscustomers.Help'),
'size' => 6,
'type' => 'textLang',
],
],
'submit' => ['title' => $this->trans('Save', [], 'Admin.Actions')],
],
];
$this->_where = Shop::addSqlRestriction(false, 'o');
$this->_use_found_rows = false;
}
public function renderForm()
{
$this->fields_form = [
'legend' => [
'title' => $this->trans('Return Merchandise Authorization (RMA)', [], 'Admin.Orderscustomers.Feature'),
'icon' => 'icon-clipboard',
],
'input' => [
[
'type' => 'hidden',
'name' => 'id_order',
],
[
'type' => 'hidden',
'name' => 'id_customer',
],
[
'type' => 'text_customer',
'label' => $this->trans('Customer', [], 'Admin.Global'),
'name' => '',
'size' => '',
'required' => false,
],
[
'type' => 'text_order',
'label' => $this->trans('Order', [], 'Admin.Global'),
'name' => '',
'size' => '',
'required' => false,
],
[
'type' => 'free',
'label' => $this->trans('Customer explanation', [], 'Admin.Orderscustomers.Feature'),
'name' => 'question',
'size' => '',
'required' => false,
],
[
'type' => 'select',
'label' => $this->trans('Status', [], 'Admin.Global'),
'name' => 'state',
'required' => false,
'options' => [
'query' => OrderReturnState::getOrderReturnStates($this->context->language->id),
'id' => 'id_order_return_state',
'name' => 'name',
],
'desc' => $this->trans('Merchandise return (RMA) status.', [], 'Admin.Orderscustomers.Help'),
],
[
'type' => 'list_products',
'label' => $this->trans('Products', [], 'Admin.Global'),
'name' => '',
'size' => '',
'required' => false,
'desc' => $this->trans('List of products in return package.', [], 'Admin.Orderscustomers.Help'),
],
[
'type' => 'pdf_order_return',
'label' => $this->trans('Returns form', [], 'Admin.Orderscustomers.Feature'),
'name' => '',
'size' => '',
'required' => false,
'desc' => $this->trans('The link is only available after validation and before the parcel gets delivered.', [], 'Admin.Orderscustomers.Help'),
],
],
'submit' => [
'title' => $this->trans('Save', [], 'Admin.Actions'),
],
'buttons' => [
'save-and-stay' => [
'title' => $this->trans('Save and stay', [], 'Admin.Actions'),
'name' => 'submitAdd' . $this->table . 'AndStay',
'type' => 'submit',
'class' => 'btn btn-default pull-right',
'icon' => 'process-icon-save',
],
],
];
$order = new Order($this->object->id_order);
$quantity_displayed = [];
// Customized products */
if ($returned_customizations = OrderReturn::getReturnedCustomizedProducts((int) $this->object->id_order)) {
foreach ($returned_customizations as $returned_customization) {
$quantity_displayed[(int) $returned_customization['id_order_detail']] = isset($quantity_displayed[(int) $returned_customization['id_order_detail']]) ? $quantity_displayed[(int) $returned_customization['id_order_detail']] + (int) $returned_customization['product_quantity'] : (int) $returned_customization['product_quantity'];
}
}
// Classic products
$products = OrderReturn::getOrdersReturnProducts($this->object->id, $order);
// Prepare customer explanation for display
$this->object->question = '<span class="normal-text">' . nl2br($this->object->question) . '</span>';
$parameters = ['vieworder' => 1, 'id_order' => (int) $order->id];
$orderUrl = $this->context->link->getAdminLink('AdminOrders', true, [], $parameters);
$this->tpl_form_vars = [
'customer' => new Customer($this->object->id_customer),
'url_customer' => $this->context->link->getAdminLink('AdminCustomers', true, [], [
'id_customer' => $this->object->id_customer,
'viewcustomer' => 1,
]),
'text_order' => $this->trans(
'Order #%id% from %date%',
[
'%id%' => $order->id,
'%date%' => Tools::displayDate($order->date_upd),
],
'Admin.Orderscustomers.Feature'
),
'url_order' => $orderUrl,
'picture_folder' => _THEME_PROD_PIC_DIR_,
'returnedCustomizations' => $returned_customizations,
'customizedDatas' => Product::getAllCustomizedDatas((int) $order->id_cart),
'products' => $products,
'quantityDisplayed' => $quantity_displayed,
'id_order_return' => $this->object->id,
'state_order_return' => $this->object->state,
];
return parent::renderForm();
}
public function initToolbar()
{
// If display list, we don't want the "add" button
if (!$this->display || $this->display == 'list') {
return;
} elseif ($this->display != 'options') {
$this->toolbar_btn['save-and-stay'] = [
'short' => 'SaveAndStay',
'href' => '#',
'desc' => $this->trans('Save and stay', [], 'Admin.Actions'),
'force_desc' => true,
];
}
parent::initToolbar();
}
public function postProcess()
{
$this->context = Context::getContext();
if (Tools::isSubmit('deleteorder_return_detail')) {
if ($this->access('delete')) {
if (($id_order_detail = (int) Tools::getValue('id_order_detail')) && Validate::isUnsignedId($id_order_detail)) {
if (($id_order_return = (int) Tools::getValue('id_order_return')) && Validate::isUnsignedId($id_order_return)) {
$orderReturn = new OrderReturn($id_order_return);
if (!Validate::isLoadedObject($orderReturn)) {
throw new PrestaShopException(sprintf('Order return with ID "%s" could not be loaded.', $id_order_return));
}
if ((int) $orderReturn->countProduct() > 1) {
if (OrderReturn::deleteOrderReturnDetail($id_order_return, $id_order_detail, (int) Tools::getValue('id_customization', 0))) {
Tools::redirectAdmin(self::$currentIndex . '&updateorder_return&id_order_return=' . $id_order_return . '&conf=4&token=' . $this->token);
} else {
$this->errors[] = $this->trans('An error occurred while deleting the details of your order return.', [], 'Admin.Orderscustomers.Notification');
}
} else {
$this->errors[] = $this->trans('You need at least one product.', [], 'Admin.Orderscustomers.Notification');
}
} else {
$this->errors[] = $this->trans('The order return is invalid.', [], 'Admin.Orderscustomers.Notification');
}
} else {
$this->errors[] = $this->trans('The order return content is invalid.', [], 'Admin.Orderscustomers.Notification');
}
} else {
$this->errors[] = $this->trans('You do not have permission to delete this.', [], 'Admin.Notifications.Error');
}
} elseif (Tools::isSubmit('submitAddorder_return') || Tools::isSubmit('submitAddorder_returnAndStay')) {
if ($this->access('edit')) {
if (($id_order_return = (int) Tools::getValue('id_order_return')) && Validate::isUnsignedId($id_order_return)) {
$orderReturn = new OrderReturn($id_order_return);
$order = new Order($orderReturn->id_order);
$customer = new Customer($orderReturn->id_customer);
$orderLanguage = new Language((int) $order->id_lang);
$orderReturn->state = (int) Tools::getValue('state');
if ($orderReturn->save()) {
$orderReturnState = new OrderReturnState($orderReturn->state);
$vars = [
'{lastname}' => $customer->lastname,
'{firstname}' => $customer->firstname,
'{id_order_return}' => $id_order_return,
'{state_order_return}' => (isset($orderReturnState->name[(int) $order->id_lang]) ? $orderReturnState->name[(int) $order->id_lang] : $orderReturnState->name[(int) Configuration::get('PS_LANG_DEFAULT')]),
];
Mail::Send(
(int) $order->id_lang,
'order_return_state',
$this->trans(
'Your order return status has changed',
[],
'Emails.Subject',
$orderLanguage->locale
),
$vars,
$customer->email,
$customer->firstname . ' ' . $customer->lastname,
null,
null,
null,
null,
_PS_MAIL_DIR_,
true,
(int) $order->id_shop
);
if (Tools::isSubmit('submitAddorder_returnAndStay')) {
Tools::redirectAdmin(self::$currentIndex . '&conf=4&token=' . $this->token . '&updateorder_return&id_order_return=' . (int) $id_order_return);
} else {
Tools::redirectAdmin(self::$currentIndex . '&conf=4&token=' . $this->token);
}
}
} else {
$this->errors[] = $this->trans('No order return ID has been specified.', [], 'Admin.Orderscustomers.Notification');
}
} else {
$this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error');
}
}
parent::postProcess();
}
}

View File

@@ -0,0 +1,479 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property Alias $object
*/
class AdminSearchConfControllerCore extends AdminController
{
/** @var bool */
protected $toolbar_scroll = false;
public function __construct()
{
$this->bootstrap = true;
$this->table = 'alias';
$this->className = 'Alias';
$this->lang = false;
parent::__construct();
$params = [
'action' => 'searchCron',
'ajax' => 1,
'full' => 1,
'token' => $this->getTokenForCron(),
];
if (Shop::getContext() == Shop::CONTEXT_SHOP) {
$params['id_shop'] = (int) Context::getContext()->shop->id;
}
// Search options
$cron_url = Context::getContext()->link->getAdminLink(
'AdminSearch',
false,
[],
$params
);
list($total, $indexed) = Db::getInstance()->getRow('SELECT COUNT(*) as "0", SUM(product_shop.indexed) as "1" FROM ' . _DB_PREFIX_ . 'product p ' . Shop::addSqlAssociation('product', 'p') . ' WHERE product_shop.`visibility` IN ("both", "search") AND product_shop.`active` = 1');
$this->fields_options = [
'indexation' => [
'title' => $this->trans('Indexing', [], 'Admin.Shopparameters.Feature'),
'icon' => 'icon-cogs',
'info' => '<p>
' . $this->trans('The "indexed" products have been analyzed by PrestaShop and will appear in the results of a front office search.', [], 'Admin.Shopparameters.Feature') . '<br />
' . $this->trans('Indexed products', [], 'Admin.Shopparameters.Feature') . ' <strong>' . (int) $indexed . ' / ' . (int) $total . '</strong>.
</p>
<p>
' . $this->trans('Building the product index may take a few minutes.', [], 'Admin.Shopparameters.Feature') . '
' . $this->trans('If your server stops before the process ends, you can resume the indexing by clicking "%add_missing_products_label%".', ['%add_missing_products_label%' => $this->trans('Add missing products to the index', [], 'Admin.Shopparameters.Feature')], 'Admin.Shopparameters.Feature') . '
</p>
<a href="' . Context::getContext()->link->getAdminLink('AdminSearch', false) . '&action=searchCron&ajax=1&token=' . $this->getTokenForCron() . '&amp;redirect=1' . (Shop::getContext() == Shop::CONTEXT_SHOP ? '&id_shop=' . (int) Context::getContext()->shop->id : '') . '" class="btn-link">
<i class="icon-external-link-sign"></i>
' . $this->trans('Add missing products to the index', [], 'Admin.Shopparameters.Feature') . '
</a><br />
<a href="' . Context::getContext()->link->getAdminLink('AdminSearch', false) . '&action=searchCron&ajax=1&full=1&amp;token=' . $this->getTokenForCron() . '&amp;redirect=1' . (Shop::getContext() == Shop::CONTEXT_SHOP ? '&id_shop=' . (int) Context::getContext()->shop->id : '') . '" class="btn-link">
<i class="icon-external-link-sign"></i>
' . $this->trans('Re-build the entire index', [], 'Admin.Shopparameters.Feature') . '
</a><br /><br />
<p>
' . $this->trans('You can set a cron job that will rebuild your index using the following URL:', [], 'Admin.Shopparameters.Feature') . '<br />
<a href="' . Tools::safeOutput($cron_url) . '">
<i class="icon-external-link-sign"></i>
' . Tools::safeOutput($cron_url) . '
</a>
</p><br />',
'fields' => [
'PS_SEARCH_INDEXATION' => [
'title' => $this->trans('Indexing', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isBool',
'type' => 'bool',
'cast' => 'intval',
'desc' => $this->trans('Enable the automatic indexing of products. If you enable this feature, the products will be indexed in the search automatically when they are saved. If the feature is disabled, you will have to index products manually by using the links provided in the field set.', [], 'Admin.Shopparameters.Help'),
],
],
'submit' => ['title' => $this->trans('Save', [], 'Admin.Actions')],
],
'search' => [
'title' => $this->trans('Search', [], 'Admin.Shopparameters.Feature'),
'icon' => 'icon-search',
'fields' => [
'PS_SEARCH_START' => [
'title' => $this->trans('Search within word', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isBool',
'cast' => 'intval',
'type' => 'bool',
'desc' => $this->trans(
'By default, to search for “blouse”, you have to enter “blous”, “blo”, etc (beginning of the word) but not “lous” (within the word).',
[],
'Admin.Shopparameters.Help'
) . '<br/>' .
$this->trans(
'With this option enabled, it also gives the good result if you search for “lous”, “ouse”, or anything contained in the word.',
[],
'Admin.Shopparameters.Help'
),
'hint' => [
$this->trans(
'Enable search within a whole word, rather than from its beginning only.',
[],
'Admin.Shopparameters.Help'
),
$this->trans(
'It checks if the searched term is contained in the indexed word. This may be resource-consuming.',
[],
'Admin.Shopparameters.Help'
),
],
],
'PS_SEARCH_END' => [
'title' => $this->trans('Search exact end match', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isBool',
'cast' => 'intval',
'type' => 'bool',
'desc' => $this->trans(
'By default, if you search "book", you will have "book", "bookcase" and "bookend".',
[],
'Admin.Shopparameters.Help'
) . '<br/>' .
$this->trans(
'With this option enabled, it only gives one result “book”, as exact end of the indexed word is matching.',
[],
'Admin.Shopparameters.Help'
),
'hint' => [
$this->trans(
'Enable more precise search with the end of the word.',
[],
'Admin.Shopparameters.Help'
),
$this->trans(
'It checks if the searched term is the exact end of the indexed word.',
[],
'Admin.Shopparameters.Help'
),
],
],
'PS_SEARCH_FUZZY' => [
'title' => $this->trans('Fuzzy search', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isBool',
'cast' => 'intval',
'type' => 'bool',
'desc' => $this->trans(
'By default, the fuzzy search is enabled. It means spelling errors are allowed, e.g. you can search for "bird" with words like "burd", "bard" or "beerd".',
[],
'Admin.Shopparameters.Help'
) . '<br/>' .
$this->trans(
'Disabling this option will require exact spelling for the search to match results.',
[],
'Admin.Shopparameters.Help'
),
'hint' => [
$this->trans(
'Enable approximate string matching.',
[],
'Admin.Shopparameters.Help'
),
],
],
'PS_SEARCH_FUZZY_MAX_LOOP' => [
'title' => $this->trans(
'Maximum approximate words allowed by fuzzy search',
[],
'Admin.Shopparameters.Feature'
),
'hint' => $this->trans(
'Note that this option is resource-consuming: the more you search, the longer it takes.',
[],
'Admin.Shopparameters.Help'
),
'validation' => 'isUnsignedInt',
'type' => 'text',
'cast' => 'intval',
],
'PS_SEARCH_FUZZY_MAX_DIFFERENCE' => [
'title' => $this->trans(
'Maximum acceptable word difference',
[],
'Admin.Shopparameters.Feature'
),
'desc' => $this->trans(
'This option defines how much different can the alternative words found by fuzzy search be. Or, how many characters can be different/missing/added. The default value is 5.',
[],
'Admin.Shopparameters.Help'
),
'validation' => 'isUnsignedInt',
'type' => 'text',
'cast' => 'intval',
],
'PS_SEARCH_MAX_WORD_LENGTH' => [
'title' => $this->trans(
'Maximum word length (in characters)',
[],
'Admin.Shopparameters.Feature'
),
'hint' => $this->trans(
'Only words fewer or equal to this maximum length will be searched.',
[],
'Admin.Shopparameters.Help'
),
'desc' => $this->trans(
'This parameter will only be used if the fuzzy search is activated: the lower the value, the more tolerant your search will be.',
[],
'Admin.Shopparameters.Help'
),
'validation' => 'isUnsignedInt',
'type' => 'text',
'cast' => 'intval',
'required' => true,
],
'PS_SEARCH_MINWORDLEN' => [
'title' => $this->trans(
'Minimum word length (in characters)',
[],
'Admin.Shopparameters.Feature'
),
'hint' => $this->trans(
'Only words this size or larger will be indexed.',
[],
'Admin.Shopparameters.Help'
),
'validation' => 'isUnsignedInt',
'type' => 'text',
'cast' => 'intval',
],
'PS_SEARCH_BLACKLIST' => [
'title' => $this->trans('Blacklisted words', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isGenericName',
'hint' => $this->trans(
'Please enter the index words separated by a "|".',
[],
'Admin.Shopparameters.Help'
),
'type' => 'textareaLang',
],
],
'submit' => ['title' => $this->trans('Save', [], 'Admin.Actions')],
],
'relevance' => [
'title' => $this->trans('Weight', [], 'Admin.Shopparameters.Feature'),
'icon' => 'icon-cogs',
'info' => $this->trans(
'The "weight" represents its importance and relevance for the ranking of the products when completing a new search.',
[],
'Admin.Shopparameters.Feature'
) . '<br />
' . $this->trans(
'A word with a weight of eight will have four times more value than a word with a weight of two.',
[],
'Admin.Shopparameters.Feature'
) . '<br /><br />
' . $this->trans(
'We advise you to set a greater weight for words which appear in the name or reference of a product. This will allow the search results to be as precise and relevant as possible.',
[],
'Admin.Shopparameters.Feature'
) . '<br /><br />
' . $this->trans(
'Setting a weight to 0 will exclude that field from search index. Re-build of the entire index is required when changing to or from 0',
[],
'Admin.Shopparameters.Feature'
),
'fields' => [
'PS_SEARCH_WEIGHT_PNAME' => [
'title' => $this->trans('Product name weight', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isUnsignedInt',
'type' => 'text',
'cast' => 'intval',
],
'PS_SEARCH_WEIGHT_REF' => [
'title' => $this->trans('Reference weight', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isUnsignedInt',
'type' => 'text',
'cast' => 'intval',
],
'PS_SEARCH_WEIGHT_SHORTDESC' => [
'title' => $this->trans(
'Short description weight',
[],
'Admin.Shopparameters.Feature'
),
'validation' => 'isUnsignedInt',
'type' => 'text',
'cast' => 'intval',
],
'PS_SEARCH_WEIGHT_DESC' => [
'title' => $this->trans('Description weight', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isUnsignedInt',
'type' => 'text',
'cast' => 'intval',
],
'PS_SEARCH_WEIGHT_CNAME' => [
'title' => $this->trans('Category weight', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isUnsignedInt',
'type' => 'text',
'cast' => 'intval',
],
'PS_SEARCH_WEIGHT_MNAME' => [
'title' => $this->trans('Brand weight', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isUnsignedInt',
'type' => 'text',
'cast' => 'intval',
],
'PS_SEARCH_WEIGHT_TAG' => [
'title' => $this->trans('Tags weight', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isUnsignedInt',
'type' => 'text',
'cast' => 'intval',
],
'PS_SEARCH_WEIGHT_ATTRIBUTE' => [
'title' => $this->trans('Attributes weight', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isUnsignedInt',
'type' => 'text',
'cast' => 'intval',
],
'PS_SEARCH_WEIGHT_FEATURE' => [
'title' => $this->trans('Features weight', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isUnsignedInt',
'type' => 'text',
'cast' => 'intval',
],
],
'submit' => ['title' => $this->trans('Save', [], 'Admin.Actions')],
],
];
}
public function initPageHeaderToolbar()
{
if (empty($this->display) || $this->display == 'list') {
$this->page_header_toolbar_btn['new_alias'] = [
'href' => self::$currentIndex . '&addalias&token=' . $this->token,
'desc' => $this->trans('Add new alias', [], 'Admin.Shopparameters.Feature'),
'icon' => 'process-icon-new',
];
}
$this->identifier_name = 'alias';
parent::initPageHeaderToolbar();
if ($this->can_import) {
$this->toolbar_btn['import'] = [
'href' => $this->context->link->getAdminLink('AdminImport', true) . '&import_type=alias',
'desc' => $this->trans('Import', [], 'Admin.Actions'),
];
}
}
public function initProcess()
{
parent::initProcess();
// This is a composite page, we don't want the "options" display mode
if ($this->display == 'options') {
$this->display = '';
}
}
/**
* Function used to render the options for this controller.
*
* @return string|void
*/
public function renderOptions()
{
if ($this->fields_options && is_array($this->fields_options)) {
$helper = new HelperOptions();
$this->setHelperDisplay($helper);
$helper->toolbar_scroll = true;
$helper->toolbar_btn = ['save' => [
'href' => '#',
'desc' => $this->trans('Save', [], 'Admin.Actions'),
]];
$helper->id = $this->id;
$helper->tpl_vars = $this->tpl_option_vars;
$options = $helper->generateOptions($this->fields_options);
return $options;
}
}
public function renderForm()
{
$this->fields_form = [
'legend' => [
'title' => $this->trans('Aliases', [], 'Admin.Shopparameters.Feature'),
'icon' => 'icon-search',
],
'input' => [
[
'type' => 'text',
'label' => $this->trans('Alias', [], 'Admin.Shopparameters.Feature'),
'name' => 'alias',
'required' => true,
'hint' => [
$this->trans('Enter each alias separated by a comma (e.g. \'prestshop,preztashop,prestasohp\').', [], 'Admin.Shopparameters.Help'),
$this->trans('Forbidden characters: &lt;&gt;{}', [], 'Admin.Shopparameters.Help'),
],
],
[
'type' => 'text',
'label' => $this->trans('Result', [], 'Admin.Shopparameters.Feature'),
'name' => 'search',
'required' => true,
'hint' => $this->trans('Search this word instead.', [], 'Admin.Shopparameters.Help'),
],
],
'submit' => [
'title' => $this->trans('Save', [], 'Admin.Actions'),
],
];
$this->fields_value = ['alias' => $this->object->getAliases()];
return parent::renderForm();
}
public function processSave()
{
$search = (string) Tools::getValue('search');
$string = (string) Tools::getValue('alias');
$aliases = explode(',', $string);
if (empty($search) || empty($string)) {
$this->errors[] = $this->trans('Aliases and results are both required.', [], 'Admin.Shopparameters.Notification');
}
if (!Validate::isValidSearch($search)) {
$this->errors[] = Tools::safeOutput($search) . ' ' . $this->trans('Is not a valid result', [], 'Admin.Shopparameters.Notification');
}
foreach ($aliases as $alias) {
if (!Validate::isValidSearch($alias)) {
$this->errors[] = Tools::safeOutput($alias) . ' ' . $this->trans('Is not a valid alias', [], 'Admin.Shopparameters.Notification');
}
}
if (!count($this->errors)) {
// Search existing aliases
$alias = new Alias();
$alias->search = trim($search);
$existingAliases = explode(',', $alias->getAliases());
// New alias
$newAliases = array_diff($aliases, $existingAliases);
foreach ($newAliases as $alias) {
$obj = new Alias(null, trim($alias), trim($search));
$obj->save();
}
// Removed alias
$removedAliases = array_diff($existingAliases, $aliases);
foreach ($removedAliases as $alias) {
$obj = new Alias(null, trim($alias), trim($search));
$obj->delete();
}
}
if (empty($this->errors)) {
if (Tools::getValue('id_alias')) {
$this->confirmations[] = $this->trans('Update successful', [], 'Admin.Notifications.Success');
} else {
$this->confirmations[] = $this->trans('Successful creation', [], 'Admin.Notifications.Success');
}
}
}
/**
* Retrieve a part of the cookie key for token check. (needs to be static).
*
* @return string Token
*/
private function getTokenForCron()
{
return substr(
_COOKIE_KEY_,
AdminSearchController::TOKEN_CHECK_START_POS,
AdminSearchController::TOKEN_CHECK_LENGTH
);
}
}

View File

@@ -0,0 +1,570 @@
<?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\Search\SearchPanel;
use PrestaShop\PrestaShop\Core\Search\SearchPanelInterface;
use PrestaShop\PrestaShop\Core\Security\Permission;
class AdminSearchControllerCore extends AdminController
{
public const TOKEN_CHECK_START_POS = 34;
public const TOKEN_CHECK_LENGTH = 8;
/**
* @var string
*/
public $query;
public function __construct()
{
$this->bootstrap = true;
parent::__construct();
}
/**
* {@inheritdoc}
*/
public function init()
{
if ($this->isCronTask()
&& substr(
_COOKIE_KEY_,
static::TOKEN_CHECK_START_POS,
static::TOKEN_CHECK_LENGTH
) === Tools::getValue('token')
) {
$this->setAllowAnonymous(true);
}
parent::init();
}
public function getTabSlug()
{
return 'ROLE_MOD_TAB_ADMINSEARCHCONF_';
}
public function postProcess()
{
$this->context = Context::getContext();
$this->query = trim(Tools::getValue('bo_query'));
$searchType = (int) Tools::getValue('bo_search_type');
/* 1.6 code compatibility, as we use HelperList, we need to handle click to go to product */
$action = Tools::getValue('action');
if ($action == 'redirectToProduct') {
$id_product = (int) Tools::getValue('id_product');
if (Tools::getIsset('statusproduct')) {
$product = new Product($id_product);
}
if (isset($product) && Validate::isLoadedObject($product)) {
if ($product->toggleStatus()) {
$this->confirmations[] = $this->trans('The status has been updated successfully.', [], 'Admin.Notifications.Success');
} else {
$this->errors[] = $this->trans('An error occurred while updating the status.', [], 'Admin.Notifications.Error');
}
} else {
$link = $this->context->link->getAdminLink('AdminProducts', false, ['id_product' => $id_product]);
Tools::redirectAdmin($link);
}
}
/* Handle empty search field */
if (!empty($this->query)) {
if (!$searchType && strlen($this->query) > 1) {
$this->searchFeatures();
}
/* Product research */
if (!$searchType || $searchType == 1) {
/* Handle product ID */
if ($searchType == 1 && Validate::isUnsignedInt((int) $this->query)) {
$product = new Product((int) $this->query);
if (Validate::isLoadedObject($product)) {
Tools::redirectAdmin($this->context->link->getAdminLink('AdminProducts', true, ['id_product' => (int) $product->id, 'updateproduct' => '1']));
}
}
/* Normal catalog search */
$this->searchCatalog();
}
/* Customer */
if (!$searchType || $searchType == 2 || $searchType == 6) {
if (!$searchType || $searchType == 2) {
/* Handle customer ID */
if ($searchType && Validate::isUnsignedInt((int) $this->query)) {
$customer = new Customer((int) $this->query);
if (Validate::isLoadedObject($customer)) {
Tools::redirectAdmin($this->context->link->getAdminLink(
'AdminCustomers',
true,
[],
[
'id_customer' => $customer->id,
'viewcustomer' => 1,
]
));
}
}
/* Normal customer search */
$this->searchCustomer();
}
if ($searchType == 6) {
$this->searchIP();
}
}
/* Order */
if (!$searchType || $searchType == 3) {
if (Validate::isUnsignedInt(trim($this->query)) && Validate::isLoadedObject($order = new Order((int) $this->query))) {
if ($searchType == 3) {
Tools::redirectAdmin($this->context->link->getAdminLink('AdminOrders', true, [], ['id_order' => (int) $order->id, 'vieworder' => '1']));
} else {
$row = get_object_vars($order);
$row['id_order'] = $row['id'];
$customer = $order->getCustomer();
$row['customer'] = $customer->firstname . ' ' . $customer->lastname;
$order_state = $order->getCurrentOrderState();
$row['osname'] = $order_state->name[$this->context->language->id];
$this->_list['orders'] = [$row];
}
} else {
$orders = Order::getByReference($this->query);
$nb_orders = count($orders);
if ($nb_orders == 1 && $searchType == 3) {
Tools::redirectAdmin($this->context->link->getAdminLink('AdminOrders', true, [], ['id_order' => (int) $orders[0]->id, 'vieworder' => '1']));
} elseif ($nb_orders) {
$this->_list['orders'] = [];
foreach ($orders as $order) {
/** @var Order $order */
$row = get_object_vars($order);
$row['id_order'] = $row['id'];
$customer = $order->getCustomer();
$row['customer'] = $customer->firstname . ' ' . $customer->lastname;
$order_state = $order->getCurrentOrderState();
$row['osname'] = $order_state->name[$this->context->language->id];
$this->_list['orders'][] = $row;
}
} elseif ($searchType == 3) {
$this->errors[] = $this->trans('No order was found with this ID:', [], 'Admin.Orderscustomers.Notification') . ' ' . Tools::htmlentitiesUTF8($this->query);
}
}
}
/* Invoices */
if ($searchType == 4) {
if ($invoice = OrderInvoice::getInvoiceByNumber($this->query)) {
Tools::redirectAdmin(
$this->context->link->getAdminLink(
'AdminPdf',
true,
[
'route' => 'admin_orders_generate_invoice_pdf',
'orderId' => (int) $invoice->id_order,
]
)
);
}
$this->errors[] = $this->trans('No invoice was found with this ID:', [], 'Admin.Orderscustomers.Notification') . ' ' . Tools::htmlentitiesUTF8($this->query);
}
/* Cart */
if ($searchType == 5) {
if (Validate::isUnsignedInt((int) $this->query) && Validate::isLoadedObject($cart = new Cart((int) $this->query))) {
Tools::redirectAdmin($this->context->link->getAdminLink('AdminOrders', true, [], ['id_cart' => (int) $cart->id, 'viewcart' => 1]));
}
$this->errors[] = $this->trans('No cart was found with this ID:', [], 'Admin.Orderscustomers.Notification') . ' ' . Tools::htmlentitiesUTF8($this->query);
}
/* IP */
// 6 - but it is included in the customer block
/* Module search */
if (!$searchType || $searchType == 7) {
/* Handle module name */
if ($searchType == 7 && Validate::isModuleName($this->query) && ($module = Module::getInstanceByName($this->query)) && Validate::isLoadedObject($module)) {
// @todo redirect directly to module manager with search prefilled, because this won't work anymore
Tools::redirectAdmin($this->context->link->getAdminLink('AdminModules', true, [], ['tab_module' => $module->tab, 'module_name' => $module->name, 'anchor' => ucfirst($module->name)]));
}
/* Normal catalog search */
$this->searchModule();
}
}
$this->display = 'view';
}
public function searchIP()
{
if (!ip2long(trim($this->query))) {
$this->errors[] = $this->trans('This is not a valid IP address:', [], 'Admin.Shopparameters.Notification') . ' ' . Tools::htmlentitiesUTF8($this->query);
return;
}
$this->_list['customers'] = Customer::searchByIp($this->query);
}
/**
* Search a specific string in the products and categories.
*/
public function searchCatalog()
{
$this->context = Context::getContext();
$this->_list['products'] = Product::searchByName($this->context->language->id, $this->query);
$this->_list['categories'] = Category::searchByName($this->context->language->id, $this->query);
}
/**
* Search a specific name in the customers.
*/
public function searchCustomer()
{
$this->_list['customers'] = Customer::searchByName($this->query);
}
public function searchModule()
{
$this->_list['modules'] = [];
$all_modules = Module::getModulesOnDisk(true, Context::getContext()->employee->id);
foreach ($all_modules as $module) {
if (
(isset($module->name) && stripos($module->name, $this->query) !== false)
|| (isset($module->displayName) && stripos($module->displayName, $this->query) !== false)
|| (isset($module->description) && stripos($module->description, $this->query) !== false)
) {
$module->linkto = Context::getContext()->link->getAdminLink('ADMINMODULESSF') . '&find=' . $module->name;
$this->_list['modules'][] = $module;
}
}
}
/**
* Search a feature in all store.
*/
public function searchFeatures()
{
$this->_list['features'] = [];
$sql = sprintf(
'SELECT class_name, name, route_name FROM %stab t INNER JOIN %stab_lang tl ON (t.id_tab = tl.id_tab AND tl.id_lang = %d) WHERE active = 1',
_DB_PREFIX_,
_DB_PREFIX_,
(int) $this->context->employee->id_lang
);
$result = Db::getInstance()->executeS($sql);
$mainControllers = Dispatcher::getControllers([
_PS_ADMIN_CONTROLLER_DIR_,
_PS_OVERRIDE_DIR_ . 'controllers/admin/',
]);
foreach ($result as $row) {
// Search pages with the query need
if (stripos($row['name'], $this->query) === false) {
continue;
}
// Remove pages without access
if (!Access::isGranted(Permission::PREFIX_TAB . strtoupper($row['class_name']) . '_READ', $this->context->employee->id_profile)) {
continue;
}
$tab = Tab::getInstanceFromClassName($row['class_name']);
if (!Validate::isLoadedObject($tab)) {
continue;
}
// Check if it's not a parent tab
if (!isset($mainControllers[strtolower($row['class_name'])])) {
$tabs = Tab::getTabs(Context::getContext()->language->id, $tab->id);
if (isset($tabs[0])) {
continue;
}
}
$sfRouteParams = (!empty($row['route_name'])) ? ['route' => $row['route_name']] : [];
$params = ['bo_query' => $this->query];
$this->_list['features'][$row['name']][] = [
'link' => Context::getContext()->link->getAdminLink((string) $row['class_name'], true, $sfRouteParams, $params),
];
}
}
protected function initOrderList()
{
$this->fields_list['orders'] = [
'reference' => ['title' => $this->trans('Reference', [], 'Admin.Global'), 'align' => 'center', 'width' => 65],
'id_order' => ['title' => $this->trans('ID', [], 'Admin.Global'), 'align' => 'center', 'width' => 25],
'customer' => ['title' => $this->trans('Customer', [], 'Admin.Global')],
'total_paid_tax_incl' => ['title' => $this->trans('Total', [], 'Admin.Global'), 'width' => 70, 'align' => 'right', 'type' => 'price', 'currency' => true],
'payment' => ['title' => $this->trans('Payment', [], 'Admin.Global'), 'width' => 100],
'osname' => ['title' => $this->trans('Status', [], 'Admin.Global'), 'width' => 280],
'date_add' => ['title' => $this->trans('Date', [], 'Admin.Global'), 'width' => 130, 'align' => 'right', 'type' => 'datetime'],
];
}
protected function initCustomerList()
{
$genders_icon = ['default' => 'unknown.gif'];
$genders = [0 => $this->trans('?', [], 'Admin.Global')];
foreach (Gender::getGenders() as $gender) {
/* @var Gender $gender */
$genders_icon[$gender->id] = '../genders/' . (int) $gender->id . '.jpg';
$genders[$gender->id] = $gender->name;
}
$this->fields_list['customers'] = [
'id_customer' => ['title' => $this->trans('ID', [], 'Admin.Global'), 'align' => 'center', 'width' => 25],
'id_gender' => ['title' => $this->trans('Social title', [], 'Admin.Global'), 'align' => 'center', 'icon' => $genders_icon, 'list' => $genders, 'width' => 25],
'firstname' => ['title' => $this->trans('First name', [], 'Admin.Global'), 'align' => 'left', 'width' => 150],
'lastname' => ['title' => $this->trans('Name', [], 'Admin.Global'), 'align' => 'left', 'width' => 'auto'],
'email' => ['title' => $this->trans('Email address', [], 'Admin.Global'), 'align' => 'left', 'width' => 250],
'company' => ['title' => $this->trans('Company', [], 'Admin.Global'), 'align' => 'left', 'width' => 150],
'birthday' => ['title' => $this->trans('Birth date', [], 'Admin.Global'), 'align' => 'center', 'type' => 'date', 'width' => 75],
'date_add' => ['title' => $this->trans('Registration date', [], 'Admin.Shopparameters.Feature'), 'align' => 'center', 'type' => 'date', 'width' => 75],
'orders' => ['title' => $this->trans('Orders', [], 'Admin.Global'), 'align' => 'center', 'width' => 50],
'active' => ['title' => $this->trans('Enabled', [], 'Admin.Global'), 'align' => 'center', 'active' => 'status', 'type' => 'bool', 'width' => 25],
];
}
protected function initProductList()
{
$this->show_toolbar = false;
$this->fields_list['products'] = [
'id_product' => ['title' => $this->trans('ID', [], 'Admin.Global'), 'width' => 25],
'manufacturer_name' => ['title' => $this->trans('Brand', [], 'Admin.Global'), 'align' => 'center', 'width' => 200],
'reference' => ['title' => $this->trans('Reference', [], 'Admin.Global'), 'align' => 'center', 'width' => 150],
'name' => ['title' => $this->trans('Name', [], 'Admin.Global'), 'width' => 'auto'],
'price_tax_excl' => ['title' => $this->trans('Price (tax excl.)', [], 'Admin.Catalog.Feature'), 'align' => 'right', 'type' => 'price', 'width' => 60],
'price_tax_incl' => ['title' => $this->trans('Price (tax incl.)', [], 'Admin.Catalog.Feature'), 'align' => 'right', 'type' => 'price', 'width' => 60],
'active' => ['title' => $this->trans('Active', [], 'Admin.Global'), 'width' => 70, 'active' => 'status', 'align' => 'center', 'type' => 'bool'],
];
}
public function setMedia($isNewTheme = false)
{
parent::setMedia($isNewTheme);
$this->addJqueryPlugin('highlight');
}
/* Override because we don't want any buttons */
public function initToolbar()
{
}
public function initToolbarTitle()
{
$this->toolbar_title = $this->trans('Search results', [], 'Admin.Global');
}
public function renderView()
{
$searchedExpression = Tools::safeOutput($this->query);
$this->tpl_view_vars['query'] = $searchedExpression;
$this->tpl_view_vars['show_toolbar'] = true;
if (!count($this->errors)) {
$nb_results = 0;
foreach ($this->_list as $list) {
if ($list != false) {
$nb_results += count($list);
}
}
$this->tpl_view_vars['nb_results'] = $nb_results;
if ($this->isCountableAndNotEmpty($this->_list, 'features')) {
$this->tpl_view_vars['features'] = $this->_list['features'];
}
if ($this->isCountableAndNotEmpty($this->_list, 'categories')) {
$categories = [];
foreach ($this->_list['categories'] as $category) {
$categories[] = Tools::getPath(
$this->context->link->getAdminLink('AdminCategories', false),
$category['id_category']
);
}
$this->tpl_view_vars['categories'] = $categories;
}
if ($this->isCountableAndNotEmpty($this->_list, 'products')) {
$view = '';
$this->initProductList();
$helper = new HelperList();
$helper->shopLinkType = '';
$helper->simple_header = true;
$helper->identifier = 'id_product';
$helper->actions = ['edit'];
$helper->show_toolbar = false;
$helper->table = 'product';
/* 1.6 code compatibility, as we use HelperList, we need to handle click to go to product, a better way need to be find */
$helper->currentIndex = $this->context->link->getAdminLink('AdminSearch', false);
$helper->currentIndex .= '&action=redirectToProduct';
$query = trim(Tools::getValue('bo_query'));
$searchType = (int) Tools::getValue('bo_search_type');
if ($query) {
$helper->currentIndex .= '&bo_query=' . $query . '&bo_search_type=' . $searchType;
}
$helper->token = Tools::getAdminTokenLite('AdminSearch');
if ($this->_list['products']) {
$view = $helper->generateList($this->_list['products'], $this->fields_list['products']);
}
$this->tpl_view_vars['products'] = $view;
$this->tpl_view_vars['productsCount'] = count($this->_list['products']);
}
if ($this->isCountableAndNotEmpty($this->_list, 'customers')) {
$view = '';
$this->initCustomerList();
$helper = new HelperList();
$helper->shopLinkType = '';
$helper->simple_header = true;
$helper->identifier = 'id_customer';
$helper->actions = ['edit', 'view'];
$helper->show_toolbar = false;
$helper->table = 'customer';
$helper->currentIndex = $this->context->link->getAdminLink('AdminCustomers', false);
$helper->token = Tools::getAdminTokenLite('AdminCustomers');
foreach ($this->_list['customers'] as $key => $val) {
$this->_list['customers'][$key]['orders'] = Order::getCustomerNbOrders((int) $val['id_customer']);
}
$view = $helper->generateList($this->_list['customers'], $this->fields_list['customers']);
$this->tpl_view_vars['customers'] = $view;
$this->tpl_view_vars['customerCount'] = count($this->_list['customers']);
}
if ($this->isCountableAndNotEmpty($this->_list, 'orders')) {
$this->initOrderList();
$helper = new HelperList();
$helper->shopLinkType = '';
$helper->simple_header = true;
$helper->identifier = 'id_order';
$helper->actions = ['view'];
$helper->show_toolbar = false;
$helper->table = 'order';
$helper->currentIndex = $this->context->link->getAdminLink('AdminOrders', false);
$helper->token = Tools::getAdminTokenLite('AdminOrders');
$this->tpl_view_vars['orders'] = $helper->generateList($this->_list['orders'], $this->fields_list['orders']);
$this->tpl_view_vars['orderCount'] = count($this->_list['orders']);
}
if ($this->isCountableAndNotEmpty($this->_list, 'modules')) {
$this->tpl_view_vars['modules'] = $this->_list['modules'];
}
}
$this->getSearchPanels($searchedExpression);
return parent::renderView();
}
protected function getSearchPanels(string $searchedExpression): void
{
// Build native search panels
$searchPanels = [];
$searchPanels[] = new SearchPanel(
$this->trans('Search docs.prestashop-project.org', [], 'Admin.Navigation.Search'),
$this->trans('Go to the documentation', [], 'Admin.Navigation.Search'),
'https://docs.prestashop-project.org/welcome/',
[
'q' => $searchedExpression,
]
);
// Get additional search panels from hooks
// An array [module_name => module_output] will be returned
$alternativeSearchPanelsFromModules = Hook::exec(
'actionGetAlternativeSearchPanels',
[
'previous_search_panels' => $searchPanels,
'bo_query' => $searchedExpression,
],
null,
true
);
foreach ($alternativeSearchPanelsFromModules as $alternativeSearchPanelsFromModule) {
foreach ($alternativeSearchPanelsFromModule as $alternativeSearchPanel) {
if ($alternativeSearchPanel instanceof SearchPanelInterface) {
$searchPanels[] = $alternativeSearchPanel;
}
}
}
// Transform the search panels and inject them to the view
$this->tpl_view_vars['searchPanels'] = [];
foreach ($searchPanels as $searchPanel) {
$this->tpl_view_vars['searchPanels'][] = [
'title' => $searchPanel->getTitle(),
'button_label' => $searchPanel->getButtonLabel(),
'link' => $searchPanel->getLink(),
'is_external_link' => $searchPanel->isExternalLink(),
];
}
}
/**
* Check if key is present in array, is countable and has data.
*
* @param array $array Array
* @param string $key Key
*
* @return bool
*/
protected function isCountableAndNotEmpty(array $array, string $key)
{
return isset($array[$key])
&& is_countable($array[$key])
&& count($array[$key]);
}
/**
* Request triggering the search indexation.
*
* Kept as GET request for backward compatibility purpose, but should be modified as POST when migrated.
* NOTE the token is different for that method, check the method checkToken() for more details.
*/
public function displayAjaxSearchCron()
{
if (!Tools::getValue('id_shop')) {
Context::getContext()->shop->setContext(Shop::CONTEXT_ALL);
} else {
Context::getContext()->shop->setContext(Shop::CONTEXT_SHOP, (int) Tools::getValue('id_shop'));
}
// Considering the indexing task can be really long, we ask the PHP process to not stop before 2 hours.
ini_set('max_execution_time', '7200');
Search::indexation(Tools::getValue('full'));
/*
* If redirect parameter was specified in the URL and we have the URL the user came from,
* we will redirect him back. This is used in backoffice so the user lands back to the config form.
*
* In CRON or CLI (referer is missing), it will die here. Even if redirect is in the URL.
*/
if (Tools::getValue('redirect') && !empty($_SERVER['HTTP_REFERER'])) {
Tools::redirectAdmin($_SERVER['HTTP_REFERER'] . '&conf=4');
}
}
/**
* Check if a task is a cron task
*
* @return bool
*/
protected function isCronTask()
{
return Tools::isSubmit('action') && 'searchCron' === Tools::getValue('action');
}
}

View File

@@ -0,0 +1,919 @@
<?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\Addon\Theme\ThemeManagerBuilder;
use PrestaShop\PrestaShop\Core\Domain\CmsPageCategory\ValueObject\CmsPageCategoryId;
use PrestaShopBundle\Utils\Tree;
/**
* @property Shop|null $object
*/
class AdminShopControllerCore extends AdminController
{
/** @var int */
public $id_shop;
/** @var int|null */
public $id_shop_group;
public function __construct()
{
$this->bootstrap = true;
$this->table = 'shop';
$this->className = 'Shop';
$this->multishop_context = Shop::CONTEXT_ALL;
parent::__construct();
$this->id_shop_group = (int) Tools::getValue('id_shop_group');
/* if $_GET['id_shop'] is transmitted, virtual url can be loaded in config.php, so we wether transmit shop_id in herfs */
if ($this->id_shop = (int) Tools::getValue('shop_id')) {
$_GET['id_shop'] = $this->id_shop;
}
$this->list_skip_actions['delete'] = [(int) Configuration::get('PS_SHOP_DEFAULT')];
$this->fields_list = [
'id_shop' => [
'title' => $this->trans('Shop ID', [], 'Admin.Shopparameters.Feature'),
'align' => 'center',
'class' => 'fixed-width-xs',
],
'name' => [
'title' => $this->trans('Store name', [], 'Admin.Shopparameters.Feature'),
'filter_key' => 'a!name',
'width' => 200,
],
'shop_group_name' => [
'title' => $this->trans('Shop group', [], 'Admin.Shopparameters.Feature'),
'width' => 150,
'filter_key' => 'gs!name',
],
'category_name' => [
'title' => $this->trans('Root category', [], 'Admin.Shopparameters.Feature'),
'width' => 150,
'filter_key' => 'cl!name',
],
'url' => [
'title' => $this->trans('Main URL for this shop', [], 'Admin.Shopparameters.Feature'),
'havingFilter' => 'url',
],
];
}
public function getTabSlug()
{
return 'ROLE_MOD_TAB_ADMINSHOPGROUP_';
}
public function viewAccess($disable = false)
{
return Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE');
}
public function initPageHeaderToolbar()
{
parent::initPageHeaderToolbar();
if (!$this->display) {
if ($this->id_object) {
$this->loadObject();
}
if (!$this->id_shop_group && is_object($this->object) && $this->object->id_shop_group) {
$this->id_shop_group = $this->object->id_shop_group;
}
$this->page_header_toolbar_btn['edit'] = [
'desc' => $this->trans('Edit this shop group', [], 'Admin.Shopparameters.Feature'),
'href' => $this->context->link->getAdminLink('AdminShopGroup') . '&updateshop_group&id_shop_group='
. $this->id_shop_group,
];
$this->page_header_toolbar_btn['new'] = [
'desc' => $this->trans('Add new shop', [], 'Admin.Shopparameters.Feature'),
'href' => $this->context->link->getAdminLink('AdminShop') . '&add' . $this->table . '&id_shop_group='
. $this->id_shop_group,
];
}
}
public function initToolbar()
{
parent::initToolbar();
if ($this->display != 'edit' && $this->display != 'add') {
if ($this->id_object) {
$this->loadObject();
}
if (!$this->id_shop_group && $this->object && $this->object->id_shop_group) {
$this->id_shop_group = $this->object->id_shop_group;
}
$this->toolbar_btn['new'] = [
'desc' => $this->trans('Add new shop', [], 'Admin.Shopparameters.Feature'),
'href' => $this->context->link->getAdminLink('AdminShop') . '&add' . $this->table . '&id_shop_group='
. $this->id_shop_group,
];
}
}
/**
* AdminController::initContent() override.
*
* @see AdminController::initContent()
*/
public function initContent()
{
parent::initContent();
$this->addJqueryPlugin('cooki-plugin');
$data = Shop::getTree();
foreach ($data as $group_key => $group) {
foreach ($group['shops'] as $shop_key => $shop) {
$current_shop = new Shop($shop['id_shop']);
$urls = $current_shop->getUrls();
foreach ($urls as $url) {
$title = $url['domain'] . $url['physical_uri'] . $url['virtual_uri'];
if (strlen($title) > 23) {
$title = substr($title, 0, 23) . '...';
}
$url['name'] = $title;
$data[$group_key][$shop_key]['urls'][$url['id_shop_url']] = $url;
}
}
}
$shops_tree = new HelperTreeShops('shops-tree', $this->trans('Multistore tree', [], 'Admin.Shopparameters.Feature'));
$shops_tree->setNodeFolderTemplate('shop_tree_node_folder.tpl')->setNodeItemTemplate('shop_tree_node_item.tpl')
->setHeaderTemplate('shop_tree_header.tpl')->setActions([
new TreeToolbarLink(
'Collapse all',
'#',
'$(\'#' . $shops_tree->getId() . '\').tree(\'collapseAll\'); return false;',
'icon-collapse-alt'
),
new TreeToolbarLink(
'Expand all',
'#',
'$(\'#' . $shops_tree->getId() . '\').tree(\'expandAll\'); return false;',
'icon-expand-alt'
),
])
->setAttribute('url_shop_group', $this->context->link->getAdminLink('AdminShopGroup'))
->setAttribute('url_shop', $this->context->link->getAdminLink('AdminShop'))
->setAttribute('url_shop_url', $this->context->link->getAdminLink('AdminShopUrl'))
->setData($data);
$shops_tree = $shops_tree->render(null, false, false);
if ($this->display == 'edit') {
$this->toolbar_title[] = $this->object->name;
} elseif (!$this->display && $this->id_shop_group) {
$group = new ShopGroup($this->id_shop_group);
$this->toolbar_title[] = $group->name;
}
$this->context->smarty->assign([
'toolbar_scroll' => 1,
'toolbar_btn' => $this->toolbar_btn,
'title' => $this->toolbar_title,
'shops_tree' => $shops_tree,
]);
}
public function renderList()
{
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->_select = 'gs.name shop_group_name, cl.name category_name, CONCAT(\'http://\', su.domain, su.physical_uri, su.virtual_uri) AS url';
$this->_join = '
LEFT JOIN `' . _DB_PREFIX_ . 'shop_group` gs
ON (a.id_shop_group = gs.id_shop_group)
LEFT JOIN `' . _DB_PREFIX_ . 'category_lang` cl
ON (a.id_category = cl.id_category AND cl.id_lang=' . (int) $this->context->language->id . ')
LEFT JOIN ' . _DB_PREFIX_ . 'shop_url su
ON a.id_shop = su.id_shop AND su.main = 1
';
$this->_group = 'GROUP BY a.id_shop';
if ($id_shop_group = (int) Tools::getValue('id_shop_group')) {
$this->_where = 'AND a.id_shop_group = ' . $id_shop_group;
}
return parent::renderList();
}
public function displayAjaxGetCategoriesFromRootCategory()
{
if (Tools::isSubmit('id_category')) {
$getId = function ($category) {
return (int) $category['id_category'];
};
$languageId = $this->context->language->id;
$getChildren = function (array $category) use ($languageId, $getId) {
return Category::getChildren($getId($category), $languageId);
};
// selected categories ids.
$selectedCategories = Tree::extractChildrenId(
[
['id_category' => (int) Tools::getValue('id_category')],
],
$getChildren,
$getId
);
$helper = new HelperTreeCategories('categories-tree', null, (int) Tools::getValue('id_category'), null, false);
$this->content = $helper->setSelectedCategories($selectedCategories)->setUseSearch(true)->setUseCheckBox(true)
->render();
}
parent::displayAjax();
}
public function postProcess()
{
if (Tools::isSubmit('id_category_default')) {
$_POST['id_category'] = Tools::getValue('id_category_default');
}
if (Tools::isSubmit('submitAddshopAndStay') || Tools::isSubmit('submitAddshop')) {
$shop_group = new ShopGroup((int) Tools::getValue('id_shop_group'));
if ($shop_group->shopNameExists(Tools::getValue('name'), (int) Tools::getValue('id_shop'))) {
$this->errors[] = $this->trans('You cannot have two stores with the same name in the same group.', [], 'Admin.Advparameters.Notification');
}
}
if (count($this->errors)) {
return false;
}
/** @var Shop|bool $result */
$result = parent::postProcess();
if ($result != false && (Tools::isSubmit('submitAddshopAndStay') || Tools::isSubmit('submitAddshop')) && (int) $result->id_category != (int) Configuration::get('PS_HOME_CATEGORY', null, null, (int) $result->id)) {
Configuration::updateValue('PS_HOME_CATEGORY', (int) $result->id_category, false, null, (int) $result->id);
}
if ($this->redirect_after) {
$this->redirect_after .= '&id_shop_group=' . $this->id_shop_group;
}
return $result;
}
/**
* @return bool
*
* @throws PrestaShopException
*/
public function processDelete()
{
if (!Validate::isLoadedObject($object = $this->loadObject())) {
$this->errors[] = $this->trans('Unable to load this store.', [], 'Admin.Advparameters.Notification');
} elseif (!Shop::hasDependency($object->id)) {
$result = Category::deleteCategoriesFromShop($object->id) && parent::processDelete();
Tools::generateHtaccess();
return $result;
} else {
$this->errors[] = $this->trans('You cannot delete this shop (customer and/or order dependency).', [], 'Admin.Shopparameters.Notification');
}
return false;
}
/**
* @param Shop $new_shop
*
* @return ObjectModel|bool
*/
protected function afterAdd($new_shop)
{
$import_data = Tools::getValue('importData', []);
// The root category should be at least imported
$new_shop->copyShopData((int) Tools::getValue('importFromShop'), $import_data);
$useImportData = Tools::getValue('useImportData');
$db = Db::getInstance(_PS_USE_SQL_SLAVE_);
// copy default data
if (!$useImportData || (is_array($import_data) && !isset($import_data['group']))) {
$sql = 'INSERT INTO `' . _DB_PREFIX_ . 'group_shop` (`id_shop`, `id_group`)
VALUES
(' . (int) $new_shop->id . ', ' . (int) Configuration::get('PS_UNIDENTIFIED_GROUP') . '),
(' . (int) $new_shop->id . ', ' . (int) Configuration::get('PS_GUEST_GROUP') . '),
(' . (int) $new_shop->id . ', ' . (int) Configuration::get('PS_CUSTOMER_GROUP') . ')
';
$db->execute($sql);
}
if (!$useImportData || (is_array($import_data) && !isset($import_data['cms']))) {
$allLangs = Language::getLanguages(false);
$db->insert(
'cms_category_shop',
[
'id_cms_category' => CmsPageCategoryId::ROOT_CMS_PAGE_CATEGORY_ID,
'id_shop' => (int) $new_shop->id,
]
);
foreach ($allLangs as $lang) {
$db->insert(
'cms_category_lang',
[
'id_cms_category' => CmsPageCategoryId::ROOT_CMS_PAGE_CATEGORY_ID,
'id_lang' => (int) $lang['id_lang'],
'id_shop' => (int) $new_shop->id,
'name' => 'Home',
'link_rewrite' => 'home',
]
);
}
}
return parent::afterAdd($new_shop);
}
/**
* @param Shop $new_shop
*
* @return bool
*/
protected function afterUpdate($new_shop)
{
$categories = Tools::getValue('categoryBox');
if (!is_array($categories)) {
$this->errors[] = $this->trans('Please create some sub-categories for this root category.', [], 'Admin.Shopparameters.Notification');
return false;
}
array_unshift($categories, Configuration::get('PS_ROOT_CATEGORY'));
if (!Category::updateFromShop($categories, $new_shop->id)) {
$this->errors[] = $this->trans('You need to select at least the root category.', [], 'Admin.Shopparameters.Notification');
}
if (Tools::getValue('useImportData') && ($import_data = Tools::getValue('importData')) && is_array($import_data)) {
$new_shop->copyShopData((int) Tools::getValue('importFromShop'), $import_data);
}
if (Tools::isSubmit('submitAddshopAndStay') || Tools::isSubmit('submitAddshop')) {
$this->redirect_after = self::$currentIndex . '&shop_id=' . (int) $new_shop->id . '&conf=4&token=' . $this->token;
}
return parent::afterUpdate($new_shop);
}
public function getList($id_lang, $order_by = null, $order_way = null, $start = 0, $limit = null, $id_lang_shop = false)
{
if (Shop::getContext() == Shop::CONTEXT_GROUP) {
$this->_where .= ' AND a.id_shop_group = ' . (int) Shop::getContextShopGroupID();
}
parent::getList($id_lang, $order_by, $order_way, $start, $limit, $id_lang_shop);
$shop_delete_list = [];
// don't allow to remove shop which have dependencies (customers / orders / ... )
foreach ($this->_list as $shop) {
if (Shop::hasDependency($shop['id_shop'])) {
$shop_delete_list[] = $shop['id_shop'];
}
}
$this->context->smarty->assign('shops_having_dependencies', $shop_delete_list);
}
/**
* @return string|void
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
* @throws SmartyException
*/
public function renderForm()
{
if (!($obj = $this->loadObject(true))) {
return;
}
/* @var Shop $obj */
$this->fields_form = [
'legend' => [
'title' => $this->trans('Store', [], 'Admin.Global'),
'icon' => 'icon-shopping-cart',
],
'identifier' => 'shop_id',
'input' => [
[
'type' => 'text',
'label' => $this->trans('Store name', [], 'Admin.Shopparameters.Feature'),
'desc' => [
$this->trans('This field does not refer to the shop name visible in the front office.', [], 'Admin.Shopparameters.Help'),
$this->trans('Follow [1]this link[/1] to edit the shop name used on the front office.', [
'[1]' => '<a href="' . $this->context->link->getAdminLink('AdminStores') . '#store_fieldset_general">',
'[/1]' => '</a>',
], 'Admin.Shopparameters.Help'), ],
'name' => 'name',
'required' => true,
],
],
];
$display_group_list = true;
if ($this->display == 'edit') {
$group = new ShopGroup($obj->id_shop_group);
if ($group->share_customer || $group->share_order || $group->share_stock) {
$display_group_list = false;
}
}
$this->fields_form['input'][] = [
'type' => 'color',
'label' => $this->trans('Color', [], 'Admin.Catalog.Feature'),
'name' => 'color',
'desc' => [
$this->trans('It will only be applied to the multistore header to highlight your shop context.', [], 'Admin.Shopparameters.Feature'),
],
'hint' => $this->trans('Choose a color with the color picker, or enter an HTML color (e.g. "lightblue", "#CC6600").', [], 'Admin.Catalog.Help'),
];
if ($display_group_list) {
$options = [];
foreach (ShopGroup::getShopGroups() as $group) {
/** @var ShopGroup $group */
if ($this->display == 'edit' && ($group->share_customer || $group->share_order || $group->share_stock) && ShopGroup::hasDependency($group->id)) {
continue;
}
$options[] = [
'id_shop_group' => $group->id,
'name' => $group->name,
];
}
if ($this->display == 'add') {
$group_desc = $this->trans('Warning: You won\'t be able to change the group of this shop if this shop belongs to a group with one of these options activated: Share Customers, Share Quantities or Share Orders.', [], 'Admin.Shopparameters.Notification');
} else {
$group_desc = $this->trans('You can only move your shop to a shop group with all "share" options disabled -- or to a shop group with no customers/orders.', [], 'Admin.Shopparameters.Notification');
}
$this->fields_form['input'][] = [
'type' => 'select',
'label' => $this->trans('Shop group', [], 'Admin.Shopparameters.Feature'),
'desc' => $group_desc,
'name' => 'id_shop_group',
'options' => [
'query' => $options,
'id' => 'id_shop_group',
'name' => 'name',
],
];
} else {
$this->fields_form['input'][] = [
'type' => 'hidden',
'name' => 'id_shop_group',
'default' => isset($group) ? $group->name : '',
];
$this->fields_form['input'][] = [
'type' => 'textShopGroup',
'label' => $this->trans('Shop group', [], 'Admin.Shopparameters.Feature'),
'desc' => $this->trans('You can\'t edit the shop group because the current shop belongs to a group with the "share" option enabled.', [], 'Admin.Shopparameters.Help'),
'name' => 'id_shop_group',
'value' => isset($group) ? $group->name : '',
];
}
$categories = Category::getRootCategories($this->context->language->id);
$this->fields_form['input'][] = [
'type' => 'select',
'label' => $this->trans('Category root', [], 'Admin.Catalog.Feature'),
'desc' => $this->trans('This is the root category of the store that you\'ve created. To define a new root category for your store, [1]please click here[/1].', [
'_raw' => true,
'[1]' => '<a href="' . $this->context->link->getAdminLink('AdminCategories') . '&addcategoryroot" target="_blank">',
'[/1]' => '</a>',
], 'Admin.Shopparameters.Help'),
'name' => 'id_category',
'options' => [
'query' => $categories,
'id' => 'id_category',
'name' => 'name',
],
];
if (Tools::isSubmit('id_shop')) {
$shop = new Shop((int) Tools::getValue('id_shop'));
$id_root = $shop->id_category;
} else {
$id_root = $categories[0]['id_category'];
}
$id_shop = (int) Tools::getValue('id_shop');
self::$currentIndex = self::$currentIndex . '&id_shop_group=' . (int) (Tools::getValue('id_shop_group') ?
Tools::getValue('id_shop_group') : (isset($obj->id_shop_group) ? $obj->id_shop_group : Shop::getContextShopGroupID()));
$shop = new Shop($id_shop);
$selected_cat = Shop::getCategories($id_shop);
if (empty($selected_cat)) {
// get first category root and preselect all these children
$root_categories = Category::getRootCategories();
$root_category = new Category($root_categories[0]['id_category']);
$children = $root_category->getAllChildren($this->context->language->id);
$selected_cat[] = $root_categories[0]['id_category'];
foreach ($children as $child) {
$selected_cat[] = $child->id;
}
}
if (Shop::getContext() == Shop::CONTEXT_SHOP && Tools::isSubmit('id_shop')) {
$root_category = new Category($shop->id_category);
} else {
$root_category = new Category($id_root);
}
$this->fields_form['input'][] = [
'type' => 'categories',
'name' => 'categoryBox',
'label' => $this->trans('Associated categories', [], 'Admin.Catalog.Feature'),
'tree' => [
'id' => 'categories-tree',
'selected_categories' => $selected_cat,
'root_category' => $root_category->id,
'use_search' => true,
'use_checkbox' => true,
],
'desc' => $this->trans('By selecting associated categories, you are choosing to share the categories between shops. Once associated between shops, any alteration of this category will impact every shop.', [], 'Admin.Shopparameters.Help'),
];
$themes = (new ThemeManagerBuilder($this->context, Db::getInstance()))
->buildRepository()
->getList();
$this->fields_form['input'][] = [
'type' => 'theme',
'label' => $this->trans('Theme', [], 'Admin.Design.Feature'),
'name' => 'theme',
'values' => $themes,
];
$this->fields_form['submit'] = [
'title' => $this->trans('Save', [], 'Admin.Actions'),
];
if (Shop::getTotalShops() > 1 && $obj->id) {
$disabled = ['active' => false];
} else {
$disabled = false;
}
$import_data = [
'carrier' => $this->trans('Carriers', [], 'Admin.Shipping.Feature'),
'cms' => $this->trans('Pages', [], 'Admin.Design.Feature'),
'contact' => $this->trans('Contact information', [], 'Admin.Advparameters.Feature'),
'country' => $this->trans('Countries', [], 'Admin.Global'),
'currency' => $this->trans('Currencies', [], 'Admin.Global'),
'discount' => $this->trans('Discount prices', [], 'Admin.Advparameters.Feature'),
'employee' => $this->trans('Employees', [], 'Admin.Advparameters.Feature'),
'image' => $this->trans('Images', [], 'Admin.Global'),
'lang' => $this->trans('Languages', [], 'Admin.Global'),
'manufacturer' => $this->trans('Brands', [], 'Admin.Global'),
'module' => $this->trans('Modules', [], 'Admin.Global'),
'hook_module' => $this->trans('Module hooks', [], 'Admin.Advparameters.Feature'),
'meta_lang' => $this->trans('Meta information', [], 'Admin.Advparameters.Feature'),
'product' => $this->trans('Products', [], 'Admin.Global'),
'product_attribute' => $this->trans('Product combinations', [], 'Admin.Advparameters.Feature'),
'stock_available' => $this->trans('Available quantities for sale', [], 'Admin.Advparameters.Feature'),
'store' => $this->trans('Stores', [], 'Admin.Global'),
'webservice_account' => $this->trans('Webservice accounts', [], 'Admin.Advparameters.Feature'),
'attribute_group' => $this->trans('Attribute groups', [], 'Admin.Advparameters.Feature'),
'feature' => $this->trans('Features', [], 'Admin.Global'),
'group' => $this->trans('Customer groups', [], 'Admin.Advparameters.Feature'),
'tax_rules_group' => $this->trans('Tax rules groups', [], 'Admin.Advparameters.Feature'),
'supplier' => $this->trans('Suppliers', [], 'Admin.Global'),
'zone' => $this->trans('Zones', [], 'Admin.International.Feature'),
'cart_rule' => $this->trans('Cart rules', [], 'Admin.Advparameters.Feature'),
];
// Hook for duplication of shop data
$modules_list = Hook::getHookModuleExecList('actionShopDataDuplication');
if (is_array($modules_list) && count($modules_list) > 0) {
foreach ($modules_list as $m) {
$import_data['Module' . ucfirst($m['module'])] = Module::getModuleName($m['module']);
}
}
asort($import_data);
if (!$this->object->id) {
$fields_import_form = [
'radio' => [
'type' => 'radio',
'label' => $this->trans('Import data', [], 'Admin.Advparameters.Feature'),
'name' => 'useImportData',
'value' => 1,
],
'select' => [
'type' => 'select',
'name' => 'importFromShop',
'label' => $this->trans('Choose the source store', [], 'Admin.Advparameters.Feature'),
'options' => [
'query' => Shop::getShops(false),
'name' => 'name',
],
],
'allcheckbox' => [
'type' => 'checkbox',
'label' => $this->trans('Choose data to import', [], 'Admin.Advparameters.Feature'),
'values' => $import_data,
],
'desc' => $this->trans('Use this option to associate data (products, modules, etc.) the same way for each selected shop.', [], 'Admin.Advparameters.Help'),
];
}
if (!$obj->theme_name) {
$themes = (new ThemeManagerBuilder($this->context, Db::getInstance()))
->buildRepository()
->getList();
$theme = array_pop($themes);
$theme_name = $theme->getName();
} else {
$theme_name = $obj->theme_name;
}
$this->fields_value = [
'id_shop_group' => (
Tools::getValue('id_shop_group')
? Tools::getValue('id_shop_group')
: (
isset($obj->id_shop_group)
? $obj->id_shop_group
: Shop::getContextShopGroupID()
)
),
'id_category' => (
Tools::getValue('id_category')
? Tools::getValue('id_category')
: (
isset($obj->id_category)
? $obj->id_category
: (int) Configuration::get('PS_HOME_CATEGORY')
)
),
'theme_name' => $theme_name,
];
$ids_category = [];
$shops = Shop::getShops(false);
foreach ($shops as $shop) {
$ids_category[$shop['id_shop']] = $shop['id_category'];
}
$this->tpl_form_vars = [
'disabled' => $disabled,
'checked' => (Tools::getValue('addshop') !== false) ? true : false,
'defaultShop' => (int) Configuration::get('PS_SHOP_DEFAULT'),
'ids_category' => $ids_category,
];
if (isset($fields_import_form)) {
$this->tpl_form_vars = array_merge($this->tpl_form_vars, ['form_import' => $fields_import_form]);
}
return parent::renderForm();
}
/**
* Object creation
*
* @return Shop|void
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function processAdd()
{
if (!Tools::getValue('categoryBox') || !in_array(Tools::getValue('id_category'), Tools::getValue('categoryBox'))) {
$this->errors[] = $this->trans('You need to select at least the root category.', [], 'Admin.Advparameters.Notification');
}
if (Tools::isSubmit('id_category_default')) {
$_POST['id_category'] = (int) Tools::getValue('id_category_default');
}
/* Checking fields validity */
$this->validateRules();
$this->errors = array_unique($this->errors);
if (!count($this->errors)) {
/** @var Shop $object */
$object = new $this->className();
$this->copyFromPost($object, $this->table);
$this->beforeAdd($object);
if (!$object->add()) {
$this->errors[] = $this->trans('An error occurred while creating an object.', [], 'Admin.Notifications.Error') .
' <b>' . $this->table . ' (' . Db::getInstance()->getMsgError() . ')</b>';
} elseif (($_POST[$this->identifier] = $object->id) && $this->postImage($object->id) && empty($this->errors) && $this->_redirect) {
// voluntary do affectation here
$parent_id = (int) Tools::getValue('id_parent', 1);
$this->afterAdd($object);
$this->updateAssoShop($object->id);
// Save and stay on same form
if (Tools::isSubmit('submitAdd' . $this->table . 'AndStay')) {
$this->redirect_after = self::$currentIndex . '&shop_id=' . (int) $object->id . '&conf=3&update' . $this->table . '&token=' . $this->token;
}
// Save and back to parent
if (Tools::isSubmit('submitAdd' . $this->table . 'AndBackToParent')) {
$this->redirect_after = self::$currentIndex . '&shop_id=' . (int) $parent_id . '&conf=3&token=' . $this->token;
}
// Default behavior (save and back)
if (empty($this->redirect_after)) {
$this->redirect_after = self::$currentIndex . ($parent_id ? '&shop_id=' . $object->id : '') . '&conf=3&token=' . $this->token;
}
}
} else {
$this->display = 'add';
return;
}
$object->associateSuperAdmins();
$categories = Tools::getValue('categoryBox');
array_unshift($categories, Configuration::get('PS_ROOT_CATEGORY'));
Category::updateFromShop($categories, $object->id);
if (Tools::getValue('useImportData') && ($import_data = Tools::getValue('importData')) && is_array($import_data) && isset($import_data['product'])) {
ini_set('max_execution_time', '7200');
Search::indexation(true);
}
return $object;
}
public function displayEditLink($token, $id, $name = null)
{
if ($this->access('edit')) {
$tpl = $this->createTemplate('helpers/list/list_action_edit.tpl');
if (!array_key_exists('Edit', self::$cache_lang)) {
self::$cache_lang['Edit'] = $this->trans('Edit', [], 'Admin.Actions');
}
$tpl->assign([
'href' => $this->context->link->getAdminLink('AdminShop') . '&shop_id=' . (int) $id . '&update' . $this->table,
'action' => self::$cache_lang['Edit'],
'id' => $id,
]);
return $tpl->fetch();
} else {
return;
}
}
public function initCategoriesAssociation($id_root = null)
{
if (null === $id_root) {
$id_root = Configuration::get('PS_ROOT_CATEGORY');
}
$id_shop = (int) Tools::getValue('id_shop');
$shop = new Shop($id_shop);
$selected_cat = Shop::getCategories($id_shop);
if (empty($selected_cat)) {
// get first category root and preselect all these children
$root_categories = Category::getRootCategories();
$root_category = new Category($root_categories[0]['id_category']);
$children = $root_category->getAllChildren($this->context->language->id);
$selected_cat[] = $root_categories[0]['id_category'];
foreach ($children as $child) {
$selected_cat[] = $child->id;
}
}
if (Shop::getContext() == Shop::CONTEXT_SHOP && Tools::isSubmit('id_shop')) {
$root_category = new Category($shop->id_category);
} else {
$root_category = new Category($id_root);
}
$root_category = ['id_category' => $root_category->id, 'name' => $root_category->name[$this->context->language->id]];
$helper = new Helper();
return $helper->renderCategoryTree($root_category, $selected_cat, 'categoryBox', false, true);
}
public function ajaxProcessTree()
{
$tree = [];
$sql = 'SELECT g.id_shop_group, g.name as group_name, s.id_shop, s.name as shop_name, u.id_shop_url, u.domain, u.physical_uri, u.virtual_uri
FROM ' . _DB_PREFIX_ . 'shop_group g
LEFT JOIN ' . _DB_PREFIX_ . 'shop s ON g.id_shop_group = s.id_shop_group
LEFT JOIN ' . _DB_PREFIX_ . 'shop_url u ON u.id_shop = s.id_shop
ORDER BY g.name, s.name, u.domain';
$results = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
foreach ($results as $row) {
$id_shop_group = $row['id_shop_group'];
$id_shop = $row['id_shop'];
$id_shop_url = $row['id_shop_url'];
// Group list
if (!isset($tree[$id_shop_group])) {
$tree[$id_shop_group] = [
'data' => [
'title' => '<b>' . $this->trans('Group', [], 'Admin.Global') . '</b> ' . $row['group_name'],
'icon' => 'themes/' . $this->context->employee->bo_theme . '/img/tree-multishop-groups.png',
'attr' => [
'href' => $this->context->link->getAdminLink('AdminShop') . '&id_shop_group=' . $id_shop_group,
'title' => $this->trans('Click here to display the shops in the %name% shop group', ['%name%' => $row['group_name']], 'Admin.Advparameters.Help'),
],
],
'attr' => [
'id' => 'tree-group-' . $id_shop_group,
],
'children' => [],
];
}
// Shop list
if (!$id_shop) {
continue;
}
if (!isset($tree[$id_shop_group]['children'][$id_shop])) {
$tree[$id_shop_group]['children'][$id_shop] = [
'data' => [
'title' => $row['shop_name'],
'icon' => 'themes/' . $this->context->employee->bo_theme . '/img/tree-multishop-shop.png',
'attr' => [
'href' => $this->context->link->getAdminLink('AdminShopUrl') . '&shop_id=' . (int) $id_shop,
'title' => $this->trans('Click here to display the URLs of the %name% shop', ['%name%' => $row['shop_name']], 'Admin.Advparameters.Help'),
],
],
'attr' => [
'id' => 'tree-shop-' . $id_shop,
],
'children' => [],
];
}
// Url list
if (!$id_shop_url) {
continue;
}
if (!isset($tree[$id_shop_group]['children'][$id_shop]['children'][$id_shop_url])) {
$url = $row['domain'] . $row['physical_uri'] . $row['virtual_uri'];
if (strlen($url) > 23) {
$url = substr($url, 0, 23) . '...';
}
$tree[$id_shop_group]['children'][$id_shop]['children'][$id_shop_url] = [
'data' => [
'title' => $url,
'icon' => 'themes/' . $this->context->employee->bo_theme . '/img/tree-multishop-url.png',
'attr' => [
'href' => $this->context->link->getAdminLink('AdminShopUrl') . '&updateshop_url&id_shop_url=' . $id_shop_url,
'title' => $row['domain'] . $row['physical_uri'] . $row['virtual_uri'],
],
],
'attr' => [
'id' => 'tree-url-' . $id_shop_url,
],
];
}
}
// jstree need to have children as array and not object, so we use sort to get clean keys
// DO NOT REMOVE this code, even if it seems really strange ;)
sort($tree);
foreach ($tree as &$groups) {
sort($groups['children']);
foreach ($groups['children'] as &$shops) {
sort($shops['children']);
}
}
$tree = [[
'data' => [
'title' => '<b>' . $this->trans('Store groups list', [], 'Admin.Advparameters.Feature') . '</b>',
'icon' => 'themes/' . $this->context->employee->bo_theme . '/img/tree-multishop-root.png',
'attr' => [
'href' => $this->context->link->getAdminLink('AdminShopGroup'),
'title' => $this->trans('Click here to display the list of shop groups', [], 'Admin.Advparameters.Help'),
],
],
'attr' => [
'id' => 'tree-root',
],
'state' => 'open',
'children' => $tree,
]];
die(json_encode($tree));
}
}

View File

@@ -0,0 +1,383 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property ShopGroup $object
*/
class AdminShopGroupControllerCore extends AdminController
{
public function __construct()
{
$this->bootstrap = true;
$this->table = 'shop_group';
$this->className = 'ShopGroup';
$this->lang = false;
$this->multishop_context = Shop::CONTEXT_ALL;
$this->addRowAction('edit');
$this->addRowAction('delete');
parent::__construct();
if (!Tools::getValue('realedit')) {
$this->deleted = false;
}
$this->show_toolbar = false;
$this->fields_list = [
'id_shop_group' => [
'title' => $this->trans('ID', [], 'Admin.Global'),
'align' => 'center',
'class' => 'fixed-width-xs',
],
'name' => [
'title' => $this->trans('Store group', [], 'Admin.Advparameters.Feature'),
'width' => 'auto',
'filter_key' => 'a!name',
],
];
$this->fields_options = [
'general' => [
'title' => $this->trans('Multistore options', [], 'Admin.Advparameters.Feature'),
'fields' => [
'PS_SHOP_DEFAULT' => [
'title' => $this->trans('Default store', [], 'Admin.Advparameters.Feature'),
'cast' => 'intval',
'type' => 'select',
'identifier' => 'id_shop',
'list' => Shop::getShops(),
'visibility' => Shop::CONTEXT_ALL,
],
],
'submit' => ['title' => $this->trans('Save', [], 'Admin.Actions')],
],
];
}
public function viewAccess($disable = false)
{
return Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE');
}
/**
* AdminController::initContent() override.
*
* @see AdminController::initContent()
*/
public function initContent()
{
parent::initContent();
$this->addJqueryPlugin('cooki-plugin');
$data = Shop::getTree();
foreach ($data as $key_group => &$group) {
foreach ($group['shops'] as $key_shop => &$shop) {
$current_shop = new Shop($shop['id_shop']);
$urls = $current_shop->getUrls();
foreach ($urls as $key_url => &$url) {
$title = $url['domain'] . $url['physical_uri'] . $url['virtual_uri'];
if (strlen($title) > 23) {
$title = substr($title, 0, 23) . '...';
}
$url['name'] = $title;
$shop['urls'][$url['id_shop_url']] = $url;
}
}
}
$shops_tree = new HelperTreeShops('shops-tree', $this->trans('Multistore tree', [], 'Admin.Advparameters.Feature'));
$shops_tree->setNodeFolderTemplate('shop_tree_node_folder.tpl')->setNodeItemTemplate('shop_tree_node_item.tpl')
->setHeaderTemplate('shop_tree_header.tpl')->setActions([
new TreeToolbarLink(
'Collapse all',
'#',
'$(\'#' . $shops_tree->getId() . '\').tree(\'collapseAll\'); return false;',
'icon-collapse-alt'
),
new TreeToolbarLink(
'Expand all',
'#',
'$(\'#' . $shops_tree->getId() . '\').tree(\'expandAll\'); return false;',
'icon-expand-alt'
),
])
->setAttribute('url_shop_group', $this->context->link->getAdminLink('AdminShopGroup'))
->setAttribute('url_shop', $this->context->link->getAdminLink('AdminShop'))
->setAttribute('url_shop_url', $this->context->link->getAdminLink('AdminShopUrl'))
->setData($data);
$shops_tree = $shops_tree->render(null, false, false);
if ($this->display == 'edit') {
$this->toolbar_title[] = $this->object->name;
}
$this->context->smarty->assign([
'toolbar_scroll' => 1,
'toolbar_btn' => $this->toolbar_btn,
'title' => $this->toolbar_title,
'shops_tree' => $shops_tree,
]);
}
public function initPageHeaderToolbar()
{
parent::initPageHeaderToolbar();
if ($this->display != 'add' && $this->display != 'edit') {
$this->page_header_toolbar_btn['new'] = [
'desc' => $this->trans('Add a new group of stores', [], 'Admin.Advparameters.Feature'),
'href' => self::$currentIndex . '&add' . $this->table . '&token=' . $this->token,
];
$this->page_header_toolbar_btn['new_2'] = [
'desc' => $this->trans('Add a new store', [], 'Admin.Advparameters.Feature'),
'href' => $this->context->link->getAdminLink('AdminShop') . '&addshop',
'imgclass' => 'new_2',
'icon' => 'process-icon-new',
];
}
}
public function initToolbar()
{
parent::initToolbar();
if ($this->display != 'add' && $this->display != 'edit') {
$this->toolbar_btn['new'] = [
'desc' => $this->trans('Add a new group of stores', [], 'Admin.Advparameters.Feature'),
'href' => self::$currentIndex . '&add' . $this->table . '&token=' . $this->token,
];
}
}
/**
* @return string|void
*
* @throws SmartyException
*/
public function renderForm()
{
$this->fields_form = [
'legend' => [
'title' => $this->trans('Store group', [], 'Admin.Advparameters.Feature'),
'icon' => 'icon-shopping-cart',
],
'description' => $this->trans('Warning: Enabling the "share customers" and "share orders" options is not recommended. Once activated and orders are created, you will not be able to disable these options. If you need these options, we recommend using several categories rather than several shops.', [], 'Admin.Advparameters.Help'),
'input' => [
[
'type' => 'text',
'label' => $this->trans('Name of the store group', [], 'Admin.Advparameters.Feature'),
'name' => 'name',
'required' => true,
],
[
'type' => 'color',
'label' => $this->trans('Color', [], 'Admin.Catalog.Feature'),
'name' => 'color',
'desc' => [
$this->trans('It will only be applied to this group of shops, each store will keep its individual color.', [], 'Admin.Shopparameters.Feature'),
],
'hint' => $this->trans('Choose a color with the color picker, or enter an HTML color (e.g. "lightblue", "#CC6600").', [], 'Admin.Catalog.Help'),
],
[
'type' => 'switch',
'label' => $this->trans('Share customers', [], 'Admin.Advparameters.Feature'),
'name' => 'share_customer',
'required' => true,
'class' => 't',
'is_bool' => true,
'disabled' => ($this->id_object && $this->display == 'edit' && ShopGroup::hasDependency($this->id_object, 'customer')) ? true : false,
'values' => [
[
'id' => 'share_customer_on',
'value' => 1,
],
[
'id' => 'share_customer_off',
'value' => 0,
],
],
'desc' => $this->trans('Once this option is enabled, the shops in this group will share customers. If a customer registers in any one of these shops, the account will automatically be available in the others shops of this group.', [], 'Admin.Advparameters.Help') . '<br/>' . $this->trans('Warning: you will not be able to disable this option once you have registered customers.', [], 'Admin.Advparameters.Help'),
],
[
'type' => 'switch',
'label' => $this->trans('Share available quantities for sale', [], 'Admin.Advparameters.Feature'),
'name' => 'share_stock',
'required' => true,
'class' => 't',
'is_bool' => true,
'values' => [
[
'id' => 'share_stock_on',
'value' => 1,
],
[
'id' => 'share_stock_off',
'value' => 0,
],
],
'desc' => $this->trans('Share available quantities between shops of this group. When changing this option, all available products quantities will be reset to 0.', [], 'Admin.Advparameters.Feature'),
],
[
'type' => 'switch',
'label' => $this->trans('Share orders', [], 'Admin.Advparameters.Feature'),
'name' => 'share_order',
'required' => true,
'class' => 't',
'is_bool' => true,
'disabled' => ($this->id_object && $this->display == 'edit' && ShopGroup::hasDependency($this->id_object, 'order')) ? true : false,
'values' => [
[
'id' => 'share_order_on',
'value' => 1,
],
[
'id' => 'share_order_off',
'value' => 0,
],
],
'desc' => $this->trans('Once this option is enabled (which is only possible if customers and available quantities are shared among shops), the customer\'s cart will be shared by all shops in this group. This way, any purchase started in one shop will be able to be completed in another shop from the same group.', [], 'Admin.Advparameters.Help') . '<br/>' . $this->trans('Warning: You will not be able to disable this option once you\'ve started to accept orders.', [], 'Admin.Advparameters.Help'),
],
[
'type' => 'switch',
'label' => $this->trans('Status', [], 'Admin.Global'),
'name' => 'active',
'required' => true,
'class' => 't',
'is_bool' => true,
'values' => [
[
'id' => 'active_on',
'value' => 1,
],
[
'id' => 'active_off',
'value' => 0,
],
],
'desc' => $this->trans('Enable or disable this shop group?', [], 'Admin.Advparameters.Help'),
],
],
'submit' => [
'title' => $this->trans('Save', [], 'Admin.Actions'),
],
];
if (!($obj = $this->loadObject(true))) {
return;
}
if (Shop::getTotalShops() > 1 && $obj->id) {
$disabled = [
'share_customer' => true,
'share_stock' => true,
'share_order' => true,
'active' => false,
];
} else {
$disabled = false;
}
$default_shop = new Shop((int) Configuration::get('PS_SHOP_DEFAULT'));
$this->tpl_form_vars = [
'disabled' => $disabled,
'checked' => (Tools::getValue('addshop_group') !== false) ? true : false,
'defaultGroup' => $default_shop->id_shop_group,
];
$this->fields_value = [
'active' => $obj->id ? (bool) $obj->active : true,
];
return parent::renderForm();
}
public function getList($id_lang, $order_by = null, $order_way = null, $start = 0, $limit = null, $id_lang_shop = false)
{
parent::getList($id_lang, $order_by, $order_way, $start, $limit, $id_lang_shop);
$shop_group_delete_list = [];
// test store authorized to remove
foreach ($this->_list as $shop_group) {
$shops = Shop::getShops(true, $shop_group['id_shop_group']);
if (!empty($shops)) {
$shop_group_delete_list[] = $shop_group['id_shop_group'];
}
}
$this->addRowActionSkipList('delete', $shop_group_delete_list);
}
public function postProcess()
{
if (Tools::isSubmit('delete' . $this->table) || Tools::isSubmit('status') || Tools::isSubmit('status' . $this->table)) {
/** @var ShopGroup $object */
$object = $this->loadObject();
if (ShopGroup::getTotalShopGroup() == 1) {
$this->errors[] = $this->trans('You cannot delete or disable the last shop group.', [], 'Admin.Notifications.Error');
} elseif ($object->haveShops()) {
$this->errors[] = $this->trans('You cannot delete or disable a shop group in use.', [], 'Admin.Notifications.Error');
}
if (count($this->errors)) {
return false;
}
}
return parent::postProcess();
}
public function beforeUpdateOptions()
{
if (!(new Shop((int) Tools::getValue('PS_SHOP_DEFAULT')))->getBaseURL()) {
$this->errors[] = $this->trans('You must configure this store\'s URL before setting it as default.', [], 'Admin.Advparameters.Notification');
}
}
/**
* @param ShopGroup $new_shop_group
*
* @return bool|void
*/
protected function afterAdd($new_shop_group)
{
// Reset available quantitites
StockAvailable::resetProductFromStockAvailableByShopGroup($new_shop_group);
}
/**
* @param ShopGroup $new_shop_group
*
* @return bool|void
*/
protected function afterUpdate($new_shop_group)
{
// Reset available quantitites
StockAvailable::resetProductFromStockAvailableByShopGroup($new_shop_group);
}
/**
* @return string|void
*/
public function renderOptions()
{
if ($this->fields_options && is_array($this->fields_options)) {
$this->display = 'options';
$this->show_toolbar = false;
$helper = new HelperOptions();
$this->setHelperDisplay($helper);
$helper->id = $this->id;
$helper->tpl_vars = $this->tpl_option_vars;
$options = $helper->generateOptions($this->fields_options);
return $options;
}
}
}

View File

@@ -0,0 +1,576 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property ShopUrl|null $object
*/
class AdminShopUrlControllerCore extends AdminController
{
/**
* @var int
*/
public $id_shop;
/**
* @var bool
*/
public $redirect_shop_url;
public function __construct()
{
$this->bootstrap = true;
$this->table = 'shop_url';
$this->className = 'ShopUrl';
$this->lang = false;
$this->multishop_context = Shop::CONTEXT_ALL;
$this->bulk_actions = [];
parent::__construct();
/* if $_GET['id_shop'] is transmitted, virtual url can be loaded in config.php, so we wether transmit shop_id in herfs */
if ($this->id_shop = (int) Tools::getValue('shop_id')) {
$_GET['id_shop'] = $this->id_shop;
} else {
$this->id_shop = (int) Tools::getValue('id_shop');
}
if (!Tools::getValue('realedit')) {
$this->deleted = false;
}
$this->fields_list = [
'id_shop_url' => [
'title' => $this->trans('Store URL ID', [], 'Admin.Advparameters.Feature'),
'align' => 'center',
'class' => 'fixed-width-xs',
],
'shop_name' => [
'title' => $this->trans('Store name', [], 'Admin.Advparameters.Feature'),
'filter_key' => 's!name',
],
'url' => [
'title' => $this->trans('URL', [], 'Admin.Global'),
'filter_key' => 'url',
'havingFilter' => true,
'remove_onclick' => true,
],
'main' => [
'title' => $this->trans('Is it the main URL?', [], 'Admin.Advparameters.Feature'),
'align' => 'center',
'activeVisu' => 'main',
'active' => 'main',
'type' => 'bool',
'orderby' => false,
'filter_key' => 'main',
'class' => 'fixed-width-md',
],
'active' => [
'title' => $this->trans('Enabled', [], 'Admin.Global'),
'align' => 'center',
'active' => 'status',
'type' => 'bool',
'orderby' => false,
'filter_key' => 'active',
'class' => 'fixed-width-md',
],
];
}
public function viewAccess($disable = false)
{
return Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE');
}
public function renderList()
{
// We will hide "delete" action for all URLs that are set as main ones for the store
$this->addRowActionSkipList('delete', $this->getUnremovableUrls());
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->_select = 's.name AS shop_name, CONCAT(\'http://\', a.domain, a.physical_uri, a.virtual_uri) AS url';
$this->_join = 'LEFT JOIN `' . _DB_PREFIX_ . 'shop` s ON (s.id_shop = a.id_shop)';
if ($id_shop = (int) Tools::getValue('id_shop')) {
$this->_where = 'AND a.id_shop = ' . $id_shop;
}
$this->_use_found_rows = false;
return parent::renderList();
}
/**
* Returns a list of URLs that are selected as main ones for some store.
*
* @return array of URLs that are selected as main
*/
protected function getUnremovableUrls()
{
return array_column(
Db::getInstance()->executeS('SELECT id_shop_url FROM ' . _DB_PREFIX_ . 'shop_url WHERE main = 1'),
'id_shop_url'
);
}
/**
* @return string|void
*
* @throws PrestaShopException
* @throws SmartyException
*/
public function renderForm()
{
$update_htaccess = Tools::modRewriteActive() && ((file_exists('.htaccess') && is_writable('.htaccess')) || is_writable(dirname('.htaccess')));
$this->multiple_fieldsets = true;
if (!$update_htaccess) {
$desc_virtual_uri = [
'<span class="warning_mod_rewrite">' . $this->trans('If you want to add a virtual URL, you need to activate URL rewriting on your web server and enable Friendly URL option.', [], 'Admin.Advparameters.Help') . '</span>',
];
} else {
$desc_virtual_uri = [
$this->trans('You can use this option if you want to create a store with a URL that doesn\'t exist on your server (e.g. if you want your store to be available with the URL www.example.com/my-store/shoes/, you have to set shoes/ in this field, assuming that my-store/ is your Physical URL).', [], 'Admin.Advparameters.Help'),
'<strong>' . $this->trans('URL rewriting must be activated on your server to use this feature.', [], 'Admin.Advparameters.Help') . '</strong>',
];
}
$this->fields_form = [
[
'form' => [
'legend' => [
'title' => $this->trans('URL options', [], 'Admin.Advparameters.Feature'),
'icon' => 'icon-cogs',
],
'input' => [
[
'type' => 'select',
'label' => $this->trans('Store', [], 'Admin.Global'),
'name' => 'id_shop',
'onchange' => 'checkMainUrlInfo(this.value);',
'options' => [
'optiongroup' => [
'query' => Shop::getTree(),
'label' => 'name',
],
'options' => [
'query' => 'shops',
'id' => 'id_shop',
'name' => 'name',
],
],
],
[
'type' => 'switch',
'label' => $this->trans('Is it the main URL for this store?', [], 'Admin.Advparameters.Feature'),
'name' => 'main',
'is_bool' => true,
'class' => 't',
'values' => [
[
'id' => 'main_on',
'value' => 1,
],
[
'id' => 'main_off',
'value' => 0,
],
],
'desc' => [
$this->trans('If you set this URL as the Main URL for the selected shop, all URLs set to this shop will be redirected to this URL (you can only have one Main URL per shop).', [], 'Admin.Advparameters.Help'),
[
'text' => $this->trans('Since the selected shop has no main URL, you have to set this URL as the Main URL.', [], 'Admin.Advparameters.Help'),
'id' => 'mainUrlInfo',
],
[
'text' => $this->trans('The selected shop already has a Main URL. Therefore, if you set this one as the Main URL, the older Main URL will be set as a regular URL.', [], 'Admin.Advparameters.Help'),
'id' => 'mainUrlInfoExplain',
],
],
],
[
'type' => 'switch',
'label' => $this->trans('Enabled', [], 'Admin.Global'),
'name' => 'active',
'required' => false,
'is_bool' => true,
'class' => 't',
'values' => [
[
'id' => 'active_on',
'value' => 1,
],
[
'id' => 'active_off',
'value' => 0,
],
],
],
],
'submit' => [
'title' => $this->trans('Save', [], 'Admin.Actions'),
],
],
],
[
'form' => [
'legend' => [
'title' => $this->trans('Store URL', [], 'Admin.Advparameters.Feature'),
'icon' => 'icon-shopping-cart',
],
'input' => [
[
'type' => 'text',
'label' => $this->trans('Domain', [], 'Admin.Advparameters.Feature'),
'name' => 'domain',
'size' => 50,
],
[
'type' => 'text',
'label' => $this->trans('SSL Domain', [], 'Admin.Advparameters.Feature'),
'name' => 'domain_ssl',
'size' => 50,
],
[
'type' => 'text',
'label' => $this->trans('Physical URL', [], 'Admin.Advparameters.Feature'),
'name' => 'physical_uri',
'desc' => $this->trans('This is the physical folder for your store on the web server. Leave this field empty if your store is installed on the root path. For instance, if your store is available at www.example.com/my-store/, you must input my-store/ in this field.', [], 'Admin.Advparameters.Help'),
'size' => 50,
],
[
'type' => 'text',
'label' => $this->trans('Virtual URL', [], 'Admin.Advparameters.Feature'),
'name' => 'virtual_uri',
'desc' => $desc_virtual_uri,
'size' => 50,
'hint' => (!$update_htaccess) ? $this->trans('Warning: URL rewriting (e.g. mod_rewrite for Apache) seems to be disabled. If your Virtual URL doesn\'t work, please check with your hosting provider on how to activate URL rewriting.', [], 'Admin.Advparameters.Help') : null,
],
[
'type' => 'text',
'label' => $this->trans('Final URL', [], 'Admin.Advparameters.Feature'),
'name' => 'final_url',
'size' => 76,
'readonly' => true,
],
],
'submit' => [
'title' => $this->trans('Save', [], 'Admin.Actions'),
],
],
],
];
if (!($obj = $this->loadObject(true))) {
return;
}
self::$currentIndex = self::$currentIndex . ($obj->id ? '&shop_id=' . (int) $obj->id_shop : '');
$current_shop = Shop::initialize();
$list_shop_with_url = [];
foreach (Shop::getShops(false, null, true) as $id) {
$list_shop_with_url[$id] = (bool) count(ShopUrl::getShopUrls($id));
}
$this->tpl_form_vars = [
'js_shop_url' => json_encode($list_shop_with_url),
];
$this->fields_value = [
'domain' => trim(Validate::isLoadedObject($obj) ? $this->getFieldValue($obj, 'domain') : $current_shop->domain),
'domain_ssl' => trim(Validate::isLoadedObject($obj) ? $this->getFieldValue($obj, 'domain_ssl') : $current_shop->domain_ssl),
'physical_uri' => trim(Validate::isLoadedObject($obj) ? $this->getFieldValue($obj, 'physical_uri') : $current_shop->physical_uri),
'active' => trim(Validate::isLoadedObject($obj) ? $this->getFieldValue($obj, 'active') : true),
];
return parent::renderForm();
}
public function initPageHeaderToolbar()
{
parent::initPageHeaderToolbar();
if ($this->display != 'add' && $this->display != 'edit') {
if ($this->id_object) {
$this->loadObject();
}
if (!$this->id_shop && $this->object && $this->object->id_shop) {
$this->id_shop = $this->object->id_shop;
}
$this->page_header_toolbar_btn['edit'] = [
'desc' => $this->trans('Edit this store', [], 'Admin.Advparameters.Feature'),
'href' => $this->context->link->getAdminLink('AdminShop') . '&updateshop&shop_id=' . (int) $this->id_shop,
];
$this->page_header_toolbar_btn['new'] = [
'desc' => $this->trans('Add a new URL', [], 'Admin.Advparameters.Feature'),
'href' => $this->context->link->getAdminLink('AdminShopUrl') . '&add' . $this->table . '&shop_id=' . (int) $this->id_shop,
];
}
}
public function initToolbar()
{
parent::initToolbar();
if ($this->display != 'add' && $this->display != 'edit') {
if ($this->id_object) {
$this->loadObject();
}
if (!$this->id_shop && $this->object && $this->object->id_shop) {
$this->id_shop = $this->object->id_shop;
}
$this->toolbar_btn['new'] = [
'desc' => $this->trans('Add a new URL', [], 'Admin.Advparameters.Feature'),
'href' => $this->context->link->getAdminLink('AdminShopUrl') . '&add' . $this->table . '&shop_id=' . (int) $this->id_shop,
];
}
}
/**
* AdminController::initContent() override.
*
* @see AdminController::initContent()
*/
public function initContent()
{
parent::initContent();
$this->addJqueryPlugin('cooki-plugin');
$data = Shop::getTree();
foreach ($data as $group_key => $group) {
foreach ($group['shops'] as $shop_key => $shop) {
$current_shop = new Shop($shop['id_shop']);
$urls = $current_shop->getUrls();
foreach ($urls as $url) {
$title = $url['domain'] . $url['physical_uri'] . $url['virtual_uri'];
if (strlen($title) > 23) {
$title = substr($title, 0, 23) . '...';
}
$url['name'] = $title;
$data[$group_key][$shop_key]['urls'][$url['id_shop_url']] = $url;
}
}
}
$shops_tree = new HelperTreeShops('shops-tree', $this->trans('Multistore tree', [], 'Admin.Advparameters.Feature'));
$shops_tree->setNodeFolderTemplate('shop_tree_node_folder.tpl')->setNodeItemTemplate('shop_tree_node_item.tpl')
->setHeaderTemplate('shop_tree_header.tpl')->setActions([
new TreeToolbarLink(
'Collapse all',
'#',
'$(\'#' . $shops_tree->getId() . '\').tree(\'collapseAll\'); return false;',
'icon-collapse-alt'
),
new TreeToolbarLink(
'Expand all',
'#',
'$(\'#' . $shops_tree->getId() . '\').tree(\'expandAll\'); return false;',
'icon-expand-alt'
),
])
->setAttribute('url_shop_group', $this->context->link->getAdminLink('AdminShopGroup'))
->setAttribute('url_shop', $this->context->link->getAdminLink('AdminShop'))
->setAttribute('url_shop_url', $this->context->link->getAdminLink('AdminShopUrl'))
->setData($data);
$shops_tree = $shops_tree->render(null, false, false);
if (!$this->display && $this->id_shop) {
$shop = new Shop($this->id_shop);
$this->toolbar_title[] = $shop->name;
}
$this->context->smarty->assign([
'toolbar_scroll' => 1,
'toolbar_btn' => $this->toolbar_btn,
'title' => $this->toolbar_title,
'shops_tree' => $shops_tree,
]);
}
public function postProcess()
{
$token = Tools::getValue('token') ? Tools::getValue('token') : $this->token;
$result = true;
if ((Tools::isSubmit('status' . $this->table) || Tools::isSubmit('status')) && Tools::getValue($this->identifier)) {
if ($this->access('edit')) {
if (Validate::isLoadedObject($object = $this->loadObject())) {
/** @var ShopUrl $object */
if ($object->main) {
$this->errors[] = $this->trans('You cannot disable the Main URL.', [], 'Admin.Notifications.Error');
} elseif ($object->toggleStatus()) {
Tools::redirectAdmin(self::$currentIndex . '&conf=5&token=' . $token);
} else {
$this->errors[] = $this->trans('An error occurred while updating the status.', [], 'Admin.Notifications.Error');
}
} else {
$this->errors[] = $this->trans('An error occurred while updating the status for an object.', [], 'Admin.Notifications.Error') . ' <b>' . $this->table . '</b> ' . $this->trans('(cannot load object)', [], 'Admin.Notifications.Error');
}
} else {
$this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error');
}
} elseif (Tools::isSubmit('main' . $this->table) && Tools::getValue($this->identifier)) {
if ($this->access('edit')) {
if (Validate::isLoadedObject($object = $this->loadObject())) {
/** @var ShopUrl $object */
if (!$object->main) {
$result = $object->setMain();
Tools::redirectAdmin(self::$currentIndex . '&conf=4&token=' . $token);
} else {
$this->errors[] = $this->trans('You cannot change a main URL to a non-main URL. You have to set another URL as your Main URL for the selected shop.', [], 'Admin.Notifications.Error');
}
} else {
$this->errors[] = $this->trans('An error occurred while updating the status for an object.', [], 'Admin.Notifications.Error') . ' <b>' . $this->table . '</b> ' . $this->trans('(cannot load object)', [], 'Admin.Notifications.Error');
}
} else {
$this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error');
}
} else {
$result = parent::postProcess();
}
if ($this->redirect_after) {
$this->redirect_after .= '&shop_id=' . (int) $this->id_shop;
}
return $result;
}
public function processSave()
{
/** @var ShopUrl $object */
$object = $this->loadObject(true);
if ($object->canAddThisUrl(Tools::getValue('domain'), Tools::getValue('domain_ssl'), Tools::getValue('physical_uri'), Tools::getValue('virtual_uri'))) {
$this->errors[] = $this->trans('A shop URL that uses this domain already exists.', [], 'Admin.Notifications.Error');
}
$unallowed = str_replace('/', '', Tools::getValue('virtual_uri'));
if ($unallowed == 'c' || $unallowed == 'img' || is_numeric($unallowed) || !preg_match('/^[a-z\d\-_]*$/i', $unallowed)) {
$this->errors[] = $this->trans(
'A shop virtual URL cannot be "%URL%"',
[
'%URL%' => $unallowed,
],
'Admin.Notifications.Error'
);
}
$return = parent::processSave();
if (!$this->errors) {
Tools::generateHtaccess();
Tools::generateRobotsFile();
Tools::clearSmartyCache();
Media::clearCache();
}
return $return;
}
public function processAdd()
{
/** @var ShopUrl $object */
$object = $this->loadObject(true);
if ($object->canAddThisUrl(Tools::getValue('domain'), Tools::getValue('domain_ssl'), Tools::getValue('physical_uri'), Tools::getValue('virtual_uri'))) {
$this->errors[] = $this->trans('A shop URL that uses this domain already exists.', [], 'Admin.Notifications.Error');
}
if (Tools::getValue('main') && !Tools::getValue('active')) {
$this->errors[] = $this->trans('You cannot disable the Main URL.', [], 'Admin.Notifications.Error');
}
return parent::processAdd();
}
public function processUpdate()
{
$this->redirect_shop_url = false;
$current_url = parse_url($_SERVER['REQUEST_URI']);
if (trim(dirname(dirname($current_url['path'])), '/') == trim($this->object->getBaseURI(), '/')) {
$this->redirect_shop_url = true;
}
/** @var ShopUrl $object */
$object = $this->loadObject(true);
if ($object->main && !Tools::getValue('main')) {
$this->errors[] = $this->trans('You cannot change a main URL to a non-main URL. You have to set another URL as your Main URL for the selected shop.', [], 'Admin.Notifications.Error');
}
if (($object->main || Tools::getValue('main')) && !Tools::getValue('active')) {
$this->errors[] = $this->trans('You cannot disable the Main URL.', [], 'Admin.Notifications.Error');
}
return parent::processUpdate();
}
/**
* @param ShopUrl $object
*
* @return void|bool
*/
protected function afterUpdate($object)
{
if ($object->id && Tools::getValue('main')) {
$object->setMain();
}
if ($this->redirect_shop_url) {
$this->redirect_after = $this->context->link->getAdminLink('AdminShopUrl');
}
}
/**
* @param string $token
* @param int $id
* @param string $name
*
* @return mixed
*/
public function displayDeleteLink($token, $id, $name = null)
{
$tpl = $this->createTemplate('helpers/list/list_action_delete.tpl');
if (!array_key_exists('Delete', self::$cache_lang)) {
self::$cache_lang['Delete'] = $this->trans('Delete', [], 'Admin.Actions');
}
if (!array_key_exists('DeleteItem', self::$cache_lang)) {
self::$cache_lang['DeleteItem'] = $this->trans('Delete selected item?', [], 'Admin.Notifications.Warning');
}
if (!array_key_exists('Name', self::$cache_lang)) {
self::$cache_lang['Name'] = $this->trans('Name:', [], 'Admin.Global');
}
if (null !== $name) {
$name = '\n\n' . self::$cache_lang['Name'] . ' ' . $name;
}
$data = [
$this->identifier => $id,
'href' => self::$currentIndex . '&' . $this->identifier . '=' . $id . '&delete' . $this->table . '&shop_id=' . (int) $this->id_shop . '&token=' . ($token != null ? $token : $this->token),
'action' => self::$cache_lang['Delete'],
];
if ($this->specificConfirmDelete !== false) {
$data['confirm'] = null !== $this->specificConfirmDelete ? '\r' . $this->specificConfirmDelete : self::$cache_lang['DeleteItem'] . $name;
}
$tpl->assign(array_merge($this->tpl_delete_link_vars, $data));
return $tpl->fetch();
}
}

View File

@@ -0,0 +1,381 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property SpecificPriceRule $object
*/
class AdminSpecificPriceRuleControllerCore extends AdminController
{
public $list_reduction_type;
/**
* @var int
*/
public $id_shop;
public function __construct()
{
$this->bootstrap = true;
$this->table = 'specific_price_rule';
$this->className = 'SpecificPriceRule';
$this->lang = false;
$this->multishop_context = Shop::CONTEXT_ALL;
parent::__construct();
/* if $_GET['id_shop'] is transmitted, virtual url can be loaded in config.php, so we wether transmit shop_id in hrefs */
if ($this->id_shop = (int) Tools::getValue('shop_id')) {
$_GET['id_shop'] = $this->id_shop;
$_POST['id_shop'] = $this->id_shop;
}
$this->list_reduction_type = [
'percentage' => $this->trans('Percentage', [], 'Admin.Global'),
'amount' => $this->trans('Amount', [], 'Admin.Global'),
];
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->_select = 's.name shop_name, cul.name as currency_name, cl.name country_name, gl.name group_name';
$this->_join = 'LEFT JOIN ' . _DB_PREFIX_ . 'shop s ON (s.id_shop = a.id_shop)
LEFT JOIN ' . _DB_PREFIX_ . 'currency_lang cul ON (cul.id_currency = a.id_currency AND cul.id_lang=' . (int) $this->context->language->id . ')
LEFT JOIN ' . _DB_PREFIX_ . 'country_lang cl ON (cl.id_country = a.id_country AND cl.id_lang=' . (int) $this->context->language->id . ')
LEFT JOIN ' . _DB_PREFIX_ . 'group_lang gl ON (gl.id_group = a.id_group AND gl.id_lang=' . (int) $this->context->language->id . ')';
$this->_use_found_rows = false;
$this->bulk_actions = [
'delete' => [
'text' => $this->trans('Delete selected', [], 'Admin.Actions'),
'confirm' => $this->trans('Delete selected items?', [], 'Admin.Notifications.Warning'),
'icon' => 'icon-trash',
],
];
$this->fields_list = [
'id_specific_price_rule' => [
'title' => $this->trans('ID', [], 'Admin.Global'),
'align' => 'center',
'class' => 'fixed-width-xs',
],
'name' => [
'title' => $this->trans('Name', [], 'Admin.Global'),
'filter_key' => 'a!name',
'width' => 'auto',
],
'id_currency' => [
'title' => $this->trans('Currency', [], 'Admin.Global'),
'align' => 'center',
'filter_key' => 'cul!name',
],
'country_name' => [
'title' => $this->trans('Country', [], 'Admin.Global'),
'align' => 'center',
'filter_key' => 'cl!name',
],
'group_name' => [
'title' => $this->trans('Group', [], 'Admin.Global'),
'align' => 'center',
'filter_key' => 'gl!name',
],
'from_quantity' => [
'title' => $this->trans('From quantity', [], 'Admin.Catalog.Feature'),
'align' => 'center',
'class' => 'fixed-width-xs',
],
'reduction_type' => [
'title' => $this->trans('Reduction type', [], 'Admin.Catalog.Feature'),
'align' => 'center',
'type' => 'select',
'filter_key' => 'a!reduction_type',
'list' => $this->list_reduction_type,
],
'reduction' => [
'title' => $this->trans('Reduction', [], 'Admin.Catalog.Feature'),
'align' => 'center',
'type' => 'decimal',
'class' => 'fixed-width-xs',
],
'from' => [
'title' => $this->trans('Beginning', [], 'Admin.Catalog.Feature'),
'align' => 'right',
'type' => 'datetime',
'filter_key' => 'a!from',
'order_key' => 'a!from',
],
'to' => [
'title' => $this->trans('End', [], 'Admin.Catalog.Feature'),
'align' => 'right',
'type' => 'datetime',
'filter_key' => 'a!to',
'order_key' => 'a!to',
],
];
if (Shop::isFeatureActive()) {
$this->fields_list = Tools::arrayInsertElementAfterKey(
$this->fields_list,
'name',
'shop_name',
[
'title' => $this->trans('Store', [], 'Admin.Global'),
'filter_key' => 's!name',
]
);
}
}
public function initPageHeaderToolbar()
{
if (empty($this->display)) {
$this->page_header_toolbar_btn['new_specific_price_rule'] = [
'href' => self::$currentIndex . '&addspecific_price_rule&token=' . $this->token,
'desc' => $this->trans('Add new catalog price rule', [], 'Admin.Catalog.Feature'),
'icon' => 'process-icon-new',
];
}
parent::initPageHeaderToolbar();
}
public function getList($id_lang, $order_by = null, $order_way = null, $start = 0, $limit = null, $id_lang_shop = false)
{
parent::getList($id_lang, $order_by, $order_way, $start, $limit, $id_lang_shop);
foreach ($this->_list as $k => $list) {
if (null !== $this->_list[$k]['id_currency']) {
$currency = new Currency(
(int) $this->_list[$k]['id_currency'],
(int) $this->context->language->id,
(int) $this->context->shop->id
);
$this->_list[$k]['id_currency'] = Validate::isLoadedObject($currency) ? $currency->getName() : null;
}
if ($list['reduction_type'] == 'amount') {
$this->_list[$k]['reduction_type'] = $this->list_reduction_type['amount'];
} elseif ($list['reduction_type'] == 'percentage') {
$this->_list[$k]['reduction_type'] = $this->list_reduction_type['percentage'];
}
}
}
public function renderForm()
{
if (!$this->object->id) {
$this->object->price = -1;
}
$this->fields_form = [
'legend' => [
'title' => $this->trans('Catalog price rules', [], 'Admin.Catalog.Feature'),
'icon' => 'icon-dollar',
],
'input' => [
[
'type' => 'text',
'label' => $this->trans('Name', [], 'Admin.Global'),
'name' => 'name',
'maxlength' => 255,
'required' => true,
],
[
'type' => 'select',
'label' => $this->trans('Store', [], 'Admin.Global'),
'name' => 'shop_id',
'options' => [
'query' => Shop::getShops(),
'id' => 'id_shop',
'name' => 'name',
],
'condition' => Shop::isFeatureActive(),
'default_value' => Shop::getContextShopID(),
],
[
'type' => 'select',
'label' => $this->trans('Currency', [], 'Admin.Global'),
'name' => 'id_currency',
'options' => [
'query' => array_merge([0 => ['id_currency' => 0, 'name' => $this->trans('All currencies', [], 'Admin.Global')]], Currency::getCurrencies(false, true, true)),
'id' => 'id_currency',
'name' => 'name',
],
],
[
'type' => 'select',
'label' => $this->trans('Country', [], 'Admin.Global'),
'name' => 'id_country',
'options' => [
'query' => array_merge([0 => ['id_country' => 0, 'name' => $this->trans('All countries', [], 'Admin.Global')]], Country::getCountries((int) $this->context->language->id)),
'id' => 'id_country',
'name' => 'name',
],
],
[
'type' => 'select',
'label' => $this->trans('Group', [], 'Admin.Global'),
'name' => 'id_group',
'options' => [
'query' => array_merge([0 => ['id_group' => 0, 'name' => $this->trans('All groups', [], 'Admin.Global')]], Group::getGroups((int) $this->context->language->id)),
'id' => 'id_group',
'name' => 'name',
],
],
[
'type' => 'text',
'label' => $this->trans('From quantity', [], 'Admin.Catalog.Feature'),
'name' => 'from_quantity',
'maxlength' => 10,
'required' => true,
],
[
'type' => 'text',
'label' => $this->trans('Price (tax excl.)', [], 'Admin.Catalog.Feature'),
'name' => 'price',
'disabled' => ($this->object->price == -1 ? 1 : 0),
'maxlength' => 10,
'suffix' => $this->context->currency->getSign('right'),
],
[
'type' => 'checkbox',
'name' => 'leave_bprice',
'values' => [
'query' => [
[
'id' => 'on',
'name' => $this->trans('Leave initial price', [], 'Admin.Catalog.Feature'),
'val' => '1',
'checked' => '1',
],
],
'id' => 'id',
'name' => 'name',
],
],
[
'type' => 'datetime',
'label' => $this->trans('From', [], 'Admin.Global'),
'name' => 'from',
],
[
'type' => 'datetime',
'label' => $this->trans('To', [], 'Admin.Global'),
'name' => 'to',
],
[
'type' => 'select',
'label' => $this->trans('Reduction type', [], 'Admin.Catalog.Feature'),
'name' => 'reduction_type',
'options' => [
'query' => [['reduction_type' => 'amount', 'name' => $this->trans('Amount', [], 'Admin.Global')], ['reduction_type' => 'percentage', 'name' => $this->trans('Percentage', [], 'Admin.Global')]],
'id' => 'reduction_type',
'name' => 'name',
],
],
[
'type' => 'select',
'label' => $this->trans('Reduction with or without taxes', [], 'Admin.Catalog.Feature'),
'name' => 'reduction_tax',
'align' => 'center',
'options' => [
'query' => [
['lab' => $this->trans('Tax included', [], 'Admin.Global'), 'val' => 1],
['lab' => $this->trans('Tax excluded', [], 'Admin.Global'), 'val' => 0],
],
'id' => 'val',
'name' => 'lab',
],
],
[
'type' => 'text',
'label' => $this->trans('Reduction', [], 'Admin.Catalog.Feature'),
'name' => 'reduction',
'required' => true,
],
],
'submit' => [
'title' => $this->trans('Save', [], 'Admin.Actions'),
],
];
$value = $this->getFieldValue($this->object, 'price');
if ($value !== '' && $value != -1) {
$price = number_format($value, 6);
} else {
$price = '';
}
$this->fields_value = [
'price' => $price,
'from_quantity' => (($value = $this->getFieldValue($this->object, 'from_quantity')) ? $value : 1),
'reduction' => number_format(($value = $this->getFieldValue($this->object, 'reduction')) ? $value : 0, 6),
'leave_bprice_on' => $price ? 0 : 1,
'shop_id' => (($value = $this->getFieldValue($this->object, 'id_shop')) ? $value : 1),
];
$attribute_groups = [];
$attributes = ProductAttribute::getAttributes((int) $this->context->language->id);
foreach ($attributes as $attribute) {
if (!isset($attribute_groups[$attribute['id_attribute_group']])) {
$attribute_groups[$attribute['id_attribute_group']] = [
'id_attribute_group' => $attribute['id_attribute_group'],
'name' => $attribute['attribute_group'],
];
}
$attribute_groups[$attribute['id_attribute_group']]['attributes'][] = [
'id_attribute' => $attribute['id_attribute'],
'name' => $attribute['name'],
];
}
$features = Feature::getFeatures((int) $this->context->language->id);
foreach ($features as &$feature) {
$feature['values'] = FeatureValue::getFeatureValuesWithLang((int) $this->context->language->id, $feature['id_feature'], true);
}
$this->tpl_form_vars = [
'manufacturers' => Manufacturer::getManufacturers(false, (int) $this->context->language->id, true, false, false, false, true),
'suppliers' => Supplier::getSuppliers(),
'attributes_group' => $attribute_groups,
'features' => $features,
'categories' => Category::getSimpleCategories((int) $this->context->language->id),
'conditions' => $this->object->getConditions(),
'is_multishop' => Shop::isFeatureActive(),
];
return parent::renderForm();
}
public function processSave()
{
$_POST['price'] = Tools::getValue('leave_bprice_on') ? '-1' : Tools::getValue('price');
/** @var SpecificPriceRule $object */
$object = parent::processSave();
if (Validate::isLoadedObject($object)) {
$object->deleteConditions();
foreach ($_POST as $key => $values) {
if (preg_match('/^condition_group_([0-9]+)$/Ui', $key, $condition_group)) {
$conditions = [];
foreach ($values as $value) {
$condition = explode('_', $value);
$conditions[] = ['type' => $condition[0], 'value' => $condition[1]];
}
$object->addConditions($conditions);
}
}
$object->apply();
return $object;
}
}
public function postProcess()
{
Tools::clearSmartyCache();
return parent::postProcess();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,299 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
abstract class AdminStatsTabControllerCore extends AdminController
{
public function init()
{
parent::init();
$this->bootstrap = true;
$this->action = 'view';
$this->display = 'view';
}
/**
* AdminController::initContent() override.
*
* @see AdminController::initContent()
*/
public function initContent()
{
if ($this->ajax) {
return;
}
$this->toolbar_title = $this->trans('Stats', [], 'Admin.Stats.Feature');
if ($this->display == 'view') {
// Some controllers use the view action without an object
if ($this->className) {
$this->loadObject(true);
}
$this->content .= $this->renderView();
}
$this->content .= $this->displayMenu();
$this->content .= $this->displayCalendar();
$this->content .= $this->displayStats();
$this->context->smarty->assign([
'content' => $this->content,
]);
}
public function initPageHeaderToolbar()
{
parent::initPageHeaderToolbar();
unset($this->page_header_toolbar_btn['back']);
}
public function displayCalendar()
{
return AdminStatsTabController::displayCalendarForm([
'Calendar' => $this->trans('Calendar', [], 'Admin.Global'),
'Day' => $this->trans('Day', [], 'Admin.Global'),
'Month' => $this->trans('Month', [], 'Admin.Global'),
'Year' => $this->trans('Year', [], 'Admin.Global'),
'From' => $this->trans('From:', [], 'Admin.Global'),
'To' => $this->trans('To:', [], 'Admin.Global'),
'Save' => $this->trans('Save', [], 'Admin.Global'),
], $this->token);
}
public static function displayCalendarForm($translations, $token, $action = null, $table = null, $identifier = null, $id = null)
{
$context = Context::getContext();
$tpl = $context->controller->createTemplate('calendar.tpl');
$context->controller->addJqueryUI('ui.datepicker');
if ($identifier === null && Tools::getValue('module')) {
$identifier = 'module';
$id = Tools::getValue('module');
}
$action = Context::getContext()->link->getAdminLink('AdminStats');
$action .= ($action && $table ? '&' . Tools::safeOutput($action) : '');
$action .= ($identifier && $id ? '&' . Tools::safeOutput($identifier) . '=' . (int) $id : '');
$module = Tools::getValue('module');
$action .= ($module ? '&module=' . Tools::safeOutput($module) : '');
$action .= (($id_product = Tools::getValue('id_product')) ? '&id_product=' . Tools::safeOutput($id_product) : '');
$tpl->assign([
'current' => self::$currentIndex,
'token' => $token,
'action' => $action,
'table' => $table,
'identifier' => $identifier,
'id' => $id,
'translations' => $translations,
'datepickerFrom' => Tools::getValue('datepickerFrom', $context->employee->stats_date_from),
'datepickerTo' => Tools::getValue('datepickerTo', $context->employee->stats_date_to),
]);
return $tpl->fetch();
}
/* Not used anymore, but still work */
protected function displayEngines()
{
$tpl = $this->createTemplate('engines.tpl');
$autoclean_period = [
'never' => $this->trans('Never', [], 'Admin.Global'),
'week' => $this->trans('Week', [], 'Admin.Global'),
'month' => $this->trans('Month', [], 'Admin.Global'),
'year' => $this->trans('Year', [], 'Admin.Global'),
];
$tpl->assign([
'current' => self::$currentIndex,
'token' => $this->token,
'graph_engine' => Configuration::get('PS_STATS_RENDER'),
'grid_engine' => Configuration::get('PS_STATS_GRID_RENDER'),
'auto_clean' => Configuration::get('PS_STATS_OLD_CONNECT_AUTO_CLEAN'),
'array_graph_engines' => ModuleGraphEngine::getGraphEngines(),
'array_grid_engines' => ModuleGridEngine::getGridEngines(),
'array_auto_clean' => $autoclean_period,
]);
return $tpl->fetch();
}
public function displayMenu()
{
$tpl = $this->createTemplate('menu.tpl');
$modules = $this->getModules();
$module_instance = [];
foreach ($modules as $m => $module) {
if ($module_instance[$module['name']] = Module::getInstanceByName($module['name'])) {
$modules[$m]['displayName'] = $module_instance[$module['name']]->displayName;
} else {
unset(
$module_instance[$module['name']],
$modules[$m]
);
}
}
uasort($modules, [$this, 'checkModulesNames']);
$tpl->assign([
'current' => self::$currentIndex,
'current_module_name' => Tools::getValue('module', 'statsforecast'),
'token' => $this->token,
'modules' => $modules,
'module_instance' => $module_instance,
]);
return $tpl->fetch();
}
public function checkModulesNames($a, $b): int
{
return strcasecmp($a['displayName'], $b['displayName']);
}
protected function getModules()
{
$moduleList = Hook::getHookModuleExecList('displayAdminStatsModules');
if (true === is_array($moduleList)) {
return array_map(
function ($moduleArray) {
return ['name' => $moduleArray['module']];
},
$moduleList
);
}
return [];
}
public function displayStats()
{
$tpl = $this->createTemplate('stats.tpl');
if ((!($module_name = Tools::getValue('module')) || !Validate::isModuleName($module_name)) && ($module_instance = Module::getInstanceByName('statsforecast')) && $module_instance->active) {
$module_name = 'statsforecast';
}
if ($module_name) {
$_GET['module'] = $module_name;
if (!isset($module_instance)) {
$module_instance = Module::getInstanceByName($module_name);
}
if ($module_instance && $module_instance->active) {
// Hook called only for the module concerned
$hook = Hook::exec('displayAdminStatsModules', [], $module_instance->id);
}
}
$tpl->assign([
'module_name' => $module_name,
'module_instance' => isset($module_instance) ? $module_instance : null,
'hook' => isset($hook) ? $hook : null,
]);
return $tpl->fetch();
}
public function postProcess()
{
$this->context = Context::getContext();
$this->processDateRange();
if (Tools::getValue('submitSettings')) {
if ($this->access('edit')) {
self::$currentIndex .= '&module=' . Tools::getValue('module');
Configuration::updateValue('PS_STATS_RENDER', Tools::getValue('PS_STATS_RENDER', Configuration::get('PS_STATS_RENDER')));
Configuration::updateValue('PS_STATS_GRID_RENDER', Tools::getValue('PS_STATS_GRID_RENDER', Configuration::get('PS_STATS_GRID_RENDER')));
Configuration::updateValue('PS_STATS_OLD_CONNECT_AUTO_CLEAN', Tools::getValue('PS_STATS_OLD_CONNECT_AUTO_CLEAN', Configuration::get('PS_STATS_OLD_CONNECT_AUTO_CLEAN')));
} else {
$this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error');
}
}
}
public function processDateRange()
{
if (Tools::isSubmit('submitDatePicker')) {
if ((!Validate::isDate($from = Tools::getValue('datepickerFrom')) || !Validate::isDate($to = Tools::getValue('datepickerTo'))) || (strtotime($from) > strtotime($to))) {
$this->errors[] = $this->trans('The specified date is invalid.', [], 'Admin.Stats.Notification');
}
}
if (Tools::isSubmit('submitDateDay')) {
$from = date('Y-m-d');
$to = date('Y-m-d');
}
if (Tools::isSubmit('submitDateDayPrev')) {
$yesterday = time() - 60 * 60 * 24;
$from = date('Y-m-d', $yesterday);
$to = date('Y-m-d', $yesterday);
}
if (Tools::isSubmit('submitDateMonth')) {
$from = date('Y-m-01');
$to = date('Y-m-t');
}
if (Tools::isSubmit('submitDateMonthPrev')) {
$m = (date('m') == 1 ? 12 : date('m') - 1);
$y = ($m == 12 ? date('Y') - 1 : date('Y'));
$from = $y . '-' . $m . '-01';
$to = $y . '-' . $m . date('-t', mktime(12, 0, 0, $m, 15, $y));
}
if (Tools::isSubmit('submitDateYear')) {
$from = date('Y-01-01');
$to = date('Y-12-31');
}
if (Tools::isSubmit('submitDateYearPrev')) {
$from = (date('Y') - 1) . date('-01-01');
$to = (date('Y') - 1) . date('-12-31');
}
if (isset($from, $to) && !count($this->errors)) {
$this->context->employee->stats_date_from = $from;
$this->context->employee->stats_date_to = $to;
$this->context->employee->update();
if (!$this->isXmlHttpRequest()) {
Tools::redirectAdmin($_SERVER['REQUEST_URI']);
}
}
}
public function ajaxProcessSetDashboardDateRange()
{
$this->processDateRange();
if ($this->isXmlHttpRequest()) {
if (is_array($this->errors) && count($this->errors)) {
die(json_encode(
[
'has_errors' => true,
'errors' => [$this->errors],
'date_from' => $this->context->employee->stats_date_from,
'date_to' => $this->context->employee->stats_date_to, ]
));
} else {
die(json_encode(
[
'has_errors' => false,
'date_from' => $this->context->employee->stats_date_from,
'date_to' => $this->context->employee->stats_date_to, ]
));
}
}
}
protected function getDate()
{
$year = isset($this->context->cookie->stats_year) ? $this->context->cookie->stats_year : date('Y');
$month = isset($this->context->cookie->stats_month) ? sprintf('%02d', $this->context->cookie->stats_month) : '%';
$day = isset($this->context->cookie->stats_day) ? sprintf('%02d', $this->context->cookie->stats_day) : '%';
return $year . '-' . $month . '-' . $day;
}
}

View File

@@ -0,0 +1,598 @@
<?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\Image\ImageFormatConfiguration;
/**
* @property Store $object
*/
class AdminStoresControllerCore extends AdminController
{
public function __construct()
{
$this->bootstrap = true;
$this->table = 'store';
$this->className = 'Store';
$this->lang = false;
$this->toolbar_scroll = false;
parent::__construct();
if (!Tools::getValue('realedit')) {
$this->deleted = false;
}
$this->fieldImageSettings = [
'name' => 'image',
'dir' => 'st',
];
$this->fields_list = [
'id_store' => ['title' => $this->trans('ID', [], 'Admin.Global'), 'align' => 'center', 'class' => 'fixed-width-xs'],
'name' => ['title' => $this->trans('Name', [], 'Admin.Global'), 'filter_key' => 'sl!name'],
'address1' => ['title' => $this->trans('Address', [], 'Admin.Global'), 'filter_key' => 'sl!address1'],
'city' => ['title' => $this->trans('City', [], 'Admin.Global')],
'postcode' => ['title' => $this->trans('Zip/Postal code', [], 'Admin.Global')],
'state' => ['title' => $this->trans('State', [], 'Admin.Global'), 'filter_key' => 'st!name'],
'country' => ['title' => $this->trans('Country', [], 'Admin.Global'), 'filter_key' => 'cl!name'],
'phone' => ['title' => $this->trans('Phone', [], 'Admin.Global')],
'fax' => ['title' => $this->trans('Fax', [], 'Admin.Global')],
'active' => ['title' => $this->trans('Enabled', [], 'Admin.Global'), 'align' => 'center', 'active' => 'status', 'type' => 'bool', 'orderby' => false],
];
$this->bulk_actions = [
'delete' => [
'text' => $this->trans('Delete selected', [], 'Admin.Actions'),
'confirm' => $this->trans('Delete selected items?', [], 'Admin.Notifications.Warning'),
'icon' => 'icon-trash',
],
];
$this->_buildOrderedFieldsShop($this->_getDefaultFieldsContent());
}
public function renderOptions()
{
// Set toolbar options
$this->display = 'options';
$this->show_toolbar = true;
$this->toolbar_scroll = true;
$this->initToolbar();
return parent::renderOptions();
}
public function initToolbar()
{
parent::initToolbar();
if ($this->display == 'options') {
unset($this->toolbar_btn['new']);
} elseif ($this->display != 'add' && $this->display != 'edit') {
unset($this->toolbar_btn['save']);
}
}
public function initPageHeaderToolbar()
{
if (empty($this->display)) {
$this->page_header_toolbar_btn['new_store'] = [
'href' => self::$currentIndex . '&addstore&token=' . $this->token,
'desc' => $this->trans('Add new store', [], 'Admin.Shopparameters.Feature'),
'icon' => 'process-icon-new',
];
}
parent::initPageHeaderToolbar();
}
public function renderList()
{
// Set toolbar options
$this->display = null;
$this->initToolbar();
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->_select = 'cl.`name` country, st.`name` state, sl.*';
$this->_join = '
LEFT JOIN `' . _DB_PREFIX_ . 'country_lang` cl
ON (cl.`id_country` = a.`id_country`
AND cl.`id_lang` = ' . (int) $this->context->language->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'state` st
ON (st.`id_state` = a.`id_state`)
LEFT JOIN `' . _DB_PREFIX_ . 'store_lang` sl
ON (sl.`id_store` = a.`id_store`
AND sl.`id_lang` = ' . (int) $this->context->language->id . ') ';
return parent::renderList();
}
/**
* @return string|void
*
* @throws PrestaShopDatabaseException
* @throws SmartyException
*/
public function renderForm()
{
if (!($obj = $this->loadObject(true))) {
return;
}
$image = _PS_STORE_IMG_DIR_ . $obj->id . '.jpg';
$image_url = ImageManager::thumbnail(
$image,
$this->table . '_' . (int) $obj->id . '.' . $this->imageType,
350,
$this->imageType,
true,
true
);
$image_size = file_exists($image) ? filesize($image) / 1000 : false;
$tmp_addr = new Address();
$res = $tmp_addr->getFieldsRequiredDatabase();
$required_fields = [];
foreach ($res as $row) {
$required_fields[(int) $row['id_required_field']] = $row['field_name'];
}
$this->fields_form = [
'legend' => [
'title' => $this->trans('Stores', [], 'Admin.Shopparameters.Feature'),
'icon' => 'icon-home',
],
'input' => [
[
'type' => 'text',
'label' => $this->trans('Name', [], 'Admin.Global'),
'name' => 'name',
'lang' => true,
'required' => false,
'hint' => [
$this->trans('Store name (e.g. City Center Mall Store).', [], 'Admin.Shopparameters.Feature'),
$this->trans('Allowed characters: letters, spaces and %s', [], 'Admin.Shopparameters.Feature'),
],
],
[
'type' => 'text',
'label' => $this->trans('Address', [], 'Admin.Global'),
'name' => 'address1',
'lang' => true,
'required' => true,
],
[
'type' => 'text',
'label' => $this->trans('Address (2)', [], 'Admin.Global'),
'name' => 'address2',
'lang' => true,
],
[
'type' => 'text',
'label' => $this->trans('Zip/Postal code', [], 'Admin.Global'),
'name' => 'postcode',
'required' => in_array('postcode', $required_fields),
],
[
'type' => 'text',
'label' => $this->trans('City', [], 'Admin.Global'),
'name' => 'city',
'required' => true,
],
[
'type' => 'select',
'label' => $this->trans('Country', [], 'Admin.Global'),
'name' => 'id_country',
'required' => true,
'default_value' => (int) $this->context->country->id,
'options' => [
'query' => Country::getCountries($this->context->language->id),
'id' => 'id_country',
'name' => 'name',
],
],
[
'type' => 'select',
'label' => $this->trans('State', [], 'Admin.Global'),
'name' => 'id_state',
'required' => true,
'options' => [
'id' => 'id_state',
'name' => 'name',
'query' => null,
],
],
[
'type' => 'latitude',
'label' => $this->trans('Latitude / Longitude', [], 'Admin.Shopparameters.Feature'),
'name' => 'latitude',
'required' => true,
'maxlength' => 12,
'hint' => $this->trans('Store coordinates (e.g. 45.265469/-47.226478).', [], 'Admin.Shopparameters.Feature'),
],
[
'type' => 'text',
'label' => $this->trans('Phone', [], 'Admin.Global'),
'name' => 'phone',
],
[
'type' => 'text',
'label' => $this->trans('Fax', [], 'Admin.Global'),
'name' => 'fax',
],
[
'type' => 'text',
'label' => $this->trans('Email address', [], 'Admin.Global'),
'name' => 'email',
],
[
'type' => 'textarea',
'label' => $this->trans('Note', [], 'Admin.Global'),
'name' => 'note',
'lang' => true,
'cols' => 42,
'rows' => 4,
],
[
'type' => 'switch',
'label' => $this->trans('Active', [], 'Admin.Global'),
'name' => 'active',
'required' => false,
'is_bool' => true,
'values' => [
[
'id' => 'active_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'active_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
'hint' => $this->trans('Whether or not to display this store.', [], 'Admin.Shopparameters.Help'),
],
[
'type' => 'file',
'label' => $this->trans('Picture', [], 'Admin.Shopparameters.Feature'),
'name' => 'image',
'display_image' => true,
'image' => $image_url ? $image_url : false,
'size' => $image_size,
'hint' => $this->trans('Storefront picture.', [], 'Admin.Shopparameters.Help'),
],
],
'hours' => [
],
'submit' => [
'title' => $this->trans('Save', [], 'Admin.Actions'),
],
];
if (Shop::isFeatureActive()) {
$this->fields_form['input'][] = [
'type' => 'shop',
'label' => $this->trans('Store association', [], 'Admin.Global'),
'name' => 'checkBoxShopAsso',
];
}
$days = [];
$days[1] = $this->trans('Monday', [], 'Admin.Shopparameters.Feature');
$days[2] = $this->trans('Tuesday', [], 'Admin.Shopparameters.Feature');
$days[3] = $this->trans('Wednesday', [], 'Admin.Shopparameters.Feature');
$days[4] = $this->trans('Thursday', [], 'Admin.Shopparameters.Feature');
$days[5] = $this->trans('Friday', [], 'Admin.Shopparameters.Feature');
$days[6] = $this->trans('Saturday', [], 'Admin.Shopparameters.Feature');
$days[7] = $this->trans('Sunday', [], 'Admin.Shopparameters.Feature');
$hours = [];
$hours_temp = $this->getFieldValue($obj, 'hours');
if (is_array($hours_temp) && !empty($hours_temp)) {
$langs = Language::getLanguages(false);
$hours_temp = array_map('json_decode', $hours_temp);
$hours = array_map(
[$this, 'adaptHoursFormat'],
$hours_temp
);
$hours = (count($langs) > 1) ? $hours : $hours[reset($langs)['id_lang']];
}
$this->fields_value = [
'latitude' => $this->getFieldValue($obj, 'latitude') ? $this->getFieldValue($obj, 'latitude') : '',
'longitude' => $this->getFieldValue($obj, 'longitude') ? $this->getFieldValue($obj, 'longitude') : '',
'days' => $days,
'hours' => $hours,
];
$this->tpl_form_vars['states_url'] = $this->getContainer()->get('router')->generate('admin_country_states');
return parent::renderForm();
}
public function postProcess()
{
if (isset($_POST['submitAdd' . $this->table])) {
$langs = Language::getLanguages(false);
/* Cleaning fields */
foreach ($_POST as $kp => $vp) {
if (is_string($vp)) {
$_POST[$kp] = trim($vp);
}
if ('hours' === $kp) {
foreach ($vp as $day => $value) {
$_POST['hours'][$day] = is_array($value) ? array_map('trim', $_POST['hours'][$day]) : trim($value);
}
}
}
/* Rewrite latitude and longitude to 8 digits */
$_POST['latitude'] = number_format((float) $_POST['latitude'], 8);
$_POST['longitude'] = number_format((float) $_POST['longitude'], 8);
/* If the selected country does not contain states */
$id_state = (int) Tools::getValue('id_state');
$id_country = (int) Tools::getValue('id_country');
$country = new Country($id_country);
if ($id_country && !(int) $country->contains_states && $id_state) {
$this->errors[] = $this->trans('You\'ve selected a state for a country that does not contain states.', [], 'Admin.Advparameters.Notification');
}
/* If the selected country contains states, then a state have to be selected */
if ((int) $country->contains_states && !$id_state) {
$this->errors[] = $this->trans('An address located in a country containing states must have a state selected.', [], 'Admin.Shopparameters.Notification');
}
$latitude = (float) Tools::getValue('latitude');
$longitude = (float) Tools::getValue('longitude');
if (empty($latitude) || empty($longitude)) {
$this->errors[] = $this->trans('Latitude and longitude are required.', [], 'Admin.Shopparameters.Notification');
}
$postcode = Tools::getValue('postcode');
/* Check zip code format */
if ($country->zip_code_format && !$country->checkZipCode($postcode)) {
$this->errors[] = $this->trans('Your Zip/Postal code is incorrect.', [], 'Admin.Notifications.Error') . '<br />' . $this->trans('It must be entered as follows:', [], 'Admin.Notifications.Error') . ' ' . str_replace('C', $country->iso_code, str_replace('N', '0', str_replace('L', 'A', $country->zip_code_format)));
} elseif (empty($postcode) && $country->need_zip_code) {
$this->errors[] = $this->trans('A Zip/Postal code is required.', [], 'Admin.Notifications.Error');
} elseif ($postcode && !Validate::isPostCode($postcode)) {
$this->errors[] = $this->trans('The Zip/Postal code is invalid.', [], 'Admin.Notifications.Error');
}
/* Store hours */
$encodedHours = [];
foreach ($langs as $lang) {
$hours = [];
for ($i = 1; $i < 8; ++$i) {
if (1 < count($langs)) {
$hours[] = explode(' | ', $_POST['hours'][$i][$lang['id_lang']]);
unset($_POST['hours'][$i][$lang['id_lang']]);
} else {
$hours[] = explode(' | ', $_POST['hours'][$i]);
unset($_POST['hours'][$i]);
}
}
$encodedHours[$lang['id_lang']] = json_encode($hours);
}
$_POST['hours'] = (1 < count($langs)) ? $encodedHours : json_encode($hours ?? []);
}
if (!count($this->errors)) {
parent::postProcess();
} else {
$this->display = 'add';
}
}
protected function postImage($id)
{
$ret = parent::postImage($id);
/*
* Let's resolve which formats we will use for image generation.
*
* In case of .jpg images, the actual format inside is decided by ImageManager.
*/
$configuredImageFormats = $this->get(ImageFormatConfiguration::class)->getGenerationFormats();
if (($id_store = (int) Tools::getValue('id_store')) && count($_FILES) && file_exists(_PS_STORE_IMG_DIR_ . $id_store . '.jpg')) {
$images_types = ImageType::getImagesTypes('stores');
foreach ($images_types as $image_type) {
foreach ($configuredImageFormats as $imageFormat) {
ImageManager::resize(
_PS_STORE_IMG_DIR_ . $id_store . '.jpg',
_PS_STORE_IMG_DIR_ . $id_store . '-' . stripslashes($image_type['name']) . '.' . $imageFormat,
(int) $image_type['width'],
(int) $image_type['height'],
$imageFormat
);
}
}
}
return $ret;
}
protected function _getDefaultFieldsContent()
{
$this->context = Context::getContext();
$countryList = [];
$countryList[] = ['id' => '0', 'name' => $this->trans('Choose your country', [], 'Admin.Shopparameters.Feature')];
foreach (Country::getCountries($this->context->language->id) as $country) {
$countryList[] = ['id' => $country['id_country'], 'name' => $country['name']];
}
$stateList = [];
$stateList[] = ['id' => '0', 'name' => $this->trans('Choose your state (if applicable)', [], 'Admin.Shopparameters.Feature')];
foreach (State::getStates($this->context->language->id) as $state) {
$stateList[] = ['id' => $state['id_state'], 'name' => $state['name']];
}
$formFields = [
'PS_SHOP_NAME' => [
'title' => $this->trans('Store name', [], 'Admin.Shopparameters.Feature'),
'hint' => $this->trans('Displayed in emails and page titles.', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isGenericName',
'required' => true,
'type' => 'text',
'no_escape' => true,
],
'PS_SHOP_EMAIL' => ['title' => $this->trans('Shop email', [], 'Admin.Shopparameters.Feature'),
'hint' => $this->trans('Displayed in emails sent to customers.', [], 'Admin.Shopparameters.Help'),
'validation' => 'isEmail',
'required' => true,
'type' => 'text',
],
'PS_SHOP_DETAILS' => [
'title' => $this->trans('Registration number', [], 'Admin.Shopparameters.Feature'),
'hint' => $this->trans('Shop registration information (e.g. SIRET or RCS).', [], 'Admin.Shopparameters.Help'),
'validation' => 'isGenericName',
'type' => 'textarea',
'cols' => 30,
'rows' => 5,
],
'PS_SHOP_ADDR1' => [
'title' => $this->trans('Shop address line 1', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isAddress',
'type' => 'text',
],
'PS_SHOP_ADDR2' => [
'title' => $this->trans('Shop address line 2', [], 'Admin.Shopparameters.Feature'),
'validation' => 'isAddress',
'type' => 'text',
],
'PS_SHOP_CODE' => [
'title' => $this->trans('Zip/Postal code', [], 'Admin.Global'),
'validation' => 'isGenericName',
'type' => 'text',
],
'PS_SHOP_CITY' => [
'title' => $this->trans('City', [], 'Admin.Global'),
'validation' => 'isGenericName',
'type' => 'text',
],
'PS_SHOP_COUNTRY_ID' => [
'title' => $this->trans('Country', [], 'Admin.Global'),
'validation' => 'isInt',
'type' => 'select',
'list' => $countryList,
'identifier' => 'id',
'cast' => 'intval',
'defaultValue' => (int) $this->context->country->id,
],
'PS_SHOP_STATE_ID' => [
'title' => $this->trans('State', [], 'Admin.Global'),
'validation' => 'isInt',
'type' => 'select',
'list' => $stateList,
'identifier' => 'id',
'cast' => 'intval',
],
'PS_SHOP_PHONE' => [
'title' => $this->trans('Phone', [], 'Admin.Global'),
'validation' => 'isGenericName',
'type' => 'text',
],
'PS_SHOP_FAX' => [
'title' => $this->trans('Fax', [], 'Admin.Global'),
'validation' => 'isGenericName',
'type' => 'text',
],
];
return $formFields;
}
protected function _buildOrderedFieldsShop(array $formFields)
{
// You cannot do that, because the fields must be sorted for the country you've selected.
// Simple example: the current country is France, where we don't display the state. You choose "US" as a country in the form. The state is not dsplayed at the right place...
// $associatedOrderKey = array(
// 'PS_SHOP_NAME' => 'company',
// 'PS_SHOP_ADDR1' => 'address1',
// 'PS_SHOP_ADDR2' => 'address2',
// 'PS_SHOP_CITY' => 'city',
// 'PS_SHOP_STATE_ID' => 'State:name',
// 'PS_SHOP_CODE' => 'postcode',
// 'PS_SHOP_COUNTRY_ID' => 'Country:name',
// 'PS_SHOP_PHONE' => 'phone');
// $fields = array();
// $orderedFields = AddressFormat::getOrderedAddressFields(Configuration::get('PS_SHOP_COUNTRY_ID'), false, true);
// foreach ($orderedFields as $lineFields)
// if (($patterns = explode(' ', $lineFields)))
// foreach ($patterns as $pattern)
// if (($key = array_search($pattern, $associatedOrderKey)))
// $fields[$key] = $formFields[$key];
// foreach ($formFields as $key => $value)
// if (!isset($fields[$key]))
// $fields[$key] = $formFields[$key];
$fields = $formFields;
$this->fields_options['contact'] = [
'title' => $this->trans('Contact details', [], 'Admin.Shopparameters.Feature'),
'icon' => 'icon-user',
'fields' => $fields,
'submit' => ['title' => $this->trans('Save', [], 'Admin.Actions')],
];
}
public function beforeUpdateOptions()
{
if (isset($_POST['PS_SHOP_STATE_ID']) && $_POST['PS_SHOP_STATE_ID'] != '0') {
$sql = 'SELECT `active` FROM `' . _DB_PREFIX_ . 'state`
WHERE `id_country` = ' . (int) Tools::getValue('PS_SHOP_COUNTRY_ID') . '
AND `id_state` = ' . (int) Tools::getValue('PS_SHOP_STATE_ID');
$isStateOk = Db::getInstance()->getValue($sql);
if ($isStateOk != 1) {
$this->errors[] = $this->trans('The specified state is not located in this country.', [], 'Admin.Shopparameters.Notification');
}
}
}
public function updateOptionPsShopCountryId($value)
{
if (!$this->errors && $value) {
$country = new Country($value, $this->context->language->id);
if ($country->id) {
Configuration::updateValue('PS_SHOP_COUNTRY_ID', $value);
Configuration::updateValue('PS_SHOP_COUNTRY', pSQL($country->name));
}
}
}
public function updateOptionPsShopStateId($value)
{
if (!$this->errors && $value) {
$state = new State($value);
if ($state->id) {
Configuration::updateValue('PS_SHOP_STATE_ID', $value);
Configuration::updateValue('PS_SHOP_STATE', pSQL($state->name));
}
}
}
/**
* Adapt the format of hours.
*
* @param array $value
*
* @return array
*/
protected function adaptHoursFormat(?array $value)
{
if (null === $value) {
return [];
}
$separator = array_fill(0, count($value), ' | ');
return array_map('implode', $separator, $value);
}
}

View File

@@ -0,0 +1,379 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property Tab $object
*/
class AdminTabsControllerCore extends AdminController
{
/** @var string */
protected $position_identifier = 'id_tab';
public function __construct()
{
$this->bootstrap = true;
$this->multishop_context = Shop::CONTEXT_ALL;
$this->table = 'tab';
$this->list_id = 'tab';
$this->className = 'Tab';
$this->lang = true;
parent::__construct();
$this->fieldImageSettings = [
'name' => 'icon',
'dir' => 't',
];
$this->imageType = 'gif';
$this->bulk_actions = [
'delete' => [
'text' => $this->trans('Delete selected', [], 'Admin.Actions'),
'confirm' => $this->trans('Delete selected items?', [], 'Admin.Actions'),
'icon' => 'icon-trash',
],
];
$this->fields_list = [
'id_tab' => [
'title' => $this->trans('ID', [], 'Admin.Global'),
'align' => 'center',
'class' => 'fixed-width-xs',
],
'name' => [
'title' => $this->trans('Name', [], 'Admin.Global'),
],
'class_name' => [
'title' => $this->trans('Class', [], 'Admin.Global'),
],
'module' => [
'title' => $this->trans('Module', [], 'Admin.Global'),
],
'active' => [
'title' => $this->trans('Enabled', [], 'Admin.Global'),
'align' => 'center',
'active' => 'status',
'type' => 'bool',
'orderby' => false,
],
'position' => [
'title' => $this->trans('Position', [], 'Admin.Global'),
'filter_key' => 'a!position',
'position' => 'position',
'align' => 'center',
'class' => 'fixed-width-md',
],
];
}
public function initPageHeaderToolbar()
{
$this->page_header_toolbar_title = $this->trans('Menus', [], 'Admin.Global');
if ($this->display == 'details') {
$this->page_header_toolbar_btn['back_to_list'] = [
'href' => Context::getContext()->link->getAdminLink('AdminTabs'),
'desc' => $this->trans('Back to list', [], 'Admin.Actions'),
'icon' => 'process-icon-back',
];
} elseif (empty($this->display)) {
$this->page_header_toolbar_btn['new_menu'] = [
'href' => self::$currentIndex . '&addtab&token=' . $this->token,
'desc' => $this->trans('Add new menu', [], 'Admin.Actions'),
'icon' => 'process-icon-new',
];
}
parent::initPageHeaderToolbar();
}
/**
* AdminController::renderForm() override.
*
* @see AdminController::renderForm()
*
* @return string
*
* @throws SmartyException
*/
public function renderForm()
{
$tabs = Tab::getTabs($this->context->language->id, 0);
// If editing, we clean itself
if (Tools::isSubmit('id_tab')) {
foreach ($tabs as $key => $tab) {
if ($tab['id_tab'] == Tools::getValue('id_tab')) {
unset($tabs[$key]);
}
}
}
// added category "Home" in var $tabs
$tab_zero = [
'id_tab' => 0,
'name' => $this->trans('Home', [], 'Admin.Global'),
];
array_unshift($tabs, $tab_zero);
$this->fields_form = [
'legend' => [
'title' => $this->trans('Menus', [], 'Admin.Global'),
'icon' => 'icon-list-ul',
],
'input' => [
[
'type' => 'hidden',
'name' => 'position',
'required' => false,
],
[
'type' => 'text',
'label' => $this->trans('Name', [], 'Admin.Global'),
'name' => 'name',
'lang' => true,
'required' => true,
'hint' => $this->trans('Invalid characters:', [], 'Admin.Notifications.Info') . ' &lt;&gt;{}',
],
[
'type' => 'text',
'label' => $this->trans('Class', [], 'Admin.Global'),
'name' => 'class_name',
'required' => true,
],
[
'type' => 'text',
'label' => $this->trans('Module', [], 'Admin.Global'),
'name' => 'module',
],
[
'type' => 'switch',
'label' => $this->trans('Status', [], 'Admin.Global'),
'name' => 'active',
'required' => false,
'is_bool' => true,
'values' => [
[
'id' => 'active_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'active_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
'hint' => $this->trans('Show or hide menu.', [], 'Admin.Actions'),
],
],
'submit' => [
'title' => $this->trans('Save', [], 'Admin.Actions'),
],
];
$display_parent = true;
if (Validate::isLoadedObject($this->object) && !class_exists($this->object->class_name . 'Controller')) {
$display_parent = false;
}
if ($display_parent) {
$this->fields_form['input'][] = [
'type' => 'select',
'label' => $this->trans('Parent', [], 'Admin.Global'),
'name' => 'id_parent',
'options' => [
'query' => $tabs,
'id' => 'id_tab',
'name' => 'name',
],
];
}
return parent::renderForm();
}
/**
* AdminController::renderList() override.
*
* @see AdminController::renderList()
*/
public function renderList()
{
$this->addRowAction('edit');
$this->addRowAction('details');
$this->addRowAction('delete');
$this->_where = 'AND a.`id_parent` = 0';
$this->_orderBy = 'position';
return parent::renderList();
}
public function initProcess()
{
if (Tools::getIsset('details' . $this->table)) {
$this->list_id = 'details';
if (isset($_POST['submitReset' . $this->list_id])) {
$this->processResetFilters();
}
} else {
$this->list_id = 'tab';
}
return parent::initProcess();
}
/**
* @return false|string|void
*
* @throws PrestaShopException
*/
public function renderDetails()
{
if ($id = Tools::getValue('id_tab')) {
$this->lang = false;
$this->list_id = 'details';
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->toolbar_btn = [];
/** @var Tab $tab */
$tab = $this->loadObject($id);
$this->toolbar_title = $tab->name[$this->context->employee->id_lang];
$this->_select = 'b.*';
$this->_join = 'LEFT JOIN `' . _DB_PREFIX_ . 'tab_lang` b ON (b.`id_tab` = a.`id_tab` AND b.`id_lang` = ' .
(int) $this->context->language->id . ')';
$this->_where = 'AND a.`id_parent` = ' . (int) $id;
$this->_orderBy = 'position';
$this->_use_found_rows = false;
self::$currentIndex = self::$currentIndex . '&details' . $this->table;
$this->processFilter();
return parent::renderList();
}
}
public function postProcess()
{
/* PrestaShop demo mode */
if (_PS_MODE_DEMO_) {
$this->errors[] = $this->trans('This functionality has been disabled.', [], 'Admin.Notifications.Error');
return;
}
/* PrestaShop demo mode */
if (($id_tab = (int) Tools::getValue('id_tab')) && ($direction = Tools::getValue('move')) && Validate::isLoadedObject($tab = new Tab($id_tab))) {
if ($tab->move($direction)) {
Tools::redirectAdmin(self::$currentIndex . '&token=' . $this->token);
}
} elseif (Tools::getValue('position') && !Tools::isSubmit('submitAdd' . $this->table)) {
$object = new Tab((int) Tools::getValue($this->identifier));
if ($this->access('edit') !== '1') {
$this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error');
} elseif (!Validate::isLoadedObject($object)) {
$this->errors[] = $this->trans('An error occurred while updating the status for an object.', [], 'Admin.Notifications.Error') .
' <b>' . $this->table . '</b> ' . $this->trans('(cannot load object)', [], 'Admin.Notifications.Error');
}
if (!$object->updatePosition((bool) Tools::getValue('way'), (int) Tools::getValue('position'))) {
$this->errors[] = $this->trans('Failed to update the position.', [], 'Admin.Notifications.Error');
} else {
Tools::redirectAdmin(self::$currentIndex . '&conf=5&token=' . Tools::getAdminTokenLite('AdminTabs'));
}
} elseif (Tools::isSubmit('submitAdd' . $this->table) && Tools::getValue('id_tab') === Tools::getValue('id_parent')) {
$this->errors[] = $this->trans('You can\'t put this menu inside itself. ', [], 'Admin.Advparameters.Notification');
} elseif (Tools::isSubmit('submitAdd' . $this->table) && $id_parent = (int) Tools::getValue('id_parent')) {
$this->redirect_after = self::$currentIndex . '&id_' . $this->table . '=' . $id_parent . '&details' . $this->table . '&conf=4&token=' . $this->token;
} elseif (isset($_GET['details' . $this->table]) && is_array($this->bulk_actions)) {
$submit_bulk_actions = array_merge([
'enableSelection' => [
'text' => $this->trans('Enable selection', [], 'Admin.Actions'),
'icon' => 'icon-power-off text-success',
],
'disableSelection' => [
'text' => $this->trans('Disable selection', [], 'Admin.Actions'),
'icon' => 'icon-power-off text-danger',
],
], $this->bulk_actions);
foreach ($submit_bulk_actions as $bulk_action => $params) {
if (Tools::isSubmit('submitBulk' . $bulk_action . $this->table) || Tools::isSubmit('submitBulk' . $bulk_action)) {
if ($this->access('edit')) {
$this->action = 'bulk' . $bulk_action;
$this->boxes = Tools::getValue($this->list_id . 'Box');
} else {
$this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error');
}
break;
} elseif (Tools::isSubmit('submitBulk')) {
if ($this->access('edit')) {
$this->action = 'bulk' . Tools::getValue('select_submitBulk');
$this->boxes = Tools::getValue($this->list_id . 'Box');
} else {
$this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error');
}
break;
}
}
} else {
// Temporary add the position depend of the selection of the parent category
if (!Tools::isSubmit('id_tab')) { // @todo Review
$_POST['position'] = Tab::getNbTabs(Tools::getValue('id_parent'));
}
}
if (!count($this->errors)) {
parent::postProcess();
}
}
/**
* @return bool|void
*/
protected function afterImageUpload()
{
if (!($obj = $this->loadObject(true))) {
return;
}
/* @var Tab $obj */
@rename(_PS_IMG_DIR_ . 't/' . $obj->id . '.gif', _PS_IMG_DIR_ . 't/' . $obj->class_name . '.gif');
}
public function ajaxProcessUpdatePositions()
{
$way = (bool) Tools::getValue('way');
$id_tab = (int) Tools::getValue('id');
$positions = Tools::getValue('tab');
// when changing positions in a tab sub-list, the first array value is empty and needs to be removed
if (!$positions[0]) {
unset($positions[0]);
// reset indexation from 0
$positions = array_merge($positions);
}
foreach ($positions as $position => $value) {
$pos = explode('_', $value);
if (isset($pos[2]) && (int) $pos[2] === $id_tab) {
$tab = new Tab((int) $pos[2]);
if (Validate::isLoadedObject($tab)) {
if (isset($position) && $tab->updatePosition($way, $position)) {
echo 'ok position ' . (int) $position . ' for tab ' . (int) $pos[1] . '\r\n';
} else {
echo '{"hasError" : true, "errors" : "Can not update tab ' . (int) $id_tab . ' to position ' . (int) $position . ' "}';
}
} else {
echo '{"hasError" : true, "errors" : "This tab (' . (int) $id_tab . ') can t be loaded"}';
}
break;
}
}
}
}

View File

@@ -0,0 +1,157 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property Tag $object
*/
class AdminTagsControllerCore extends AdminController
{
/** @var bool */
public $bootstrap = true;
public function __construct()
{
$this->table = 'tag';
$this->className = 'Tag';
parent::__construct();
$this->fields_list = [
'id_tag' => [
'title' => $this->trans('ID', [], 'Admin.Global'),
'align' => 'center',
'class' => 'fixed-width-xs',
],
'lang' => [
'title' => $this->trans('Language', [], 'Admin.Global'),
'filter_key' => 'l!name',
],
'name' => [
'title' => $this->trans('Name', [], 'Admin.Global'),
'filter_key' => 'a!name',
],
'products' => [
'title' => $this->trans('Products', [], 'Admin.Global'),
'align' => 'center',
'class' => 'fixed-width-xs',
'havingFilter' => true,
],
];
$this->bulk_actions = [
'delete' => [
'text' => $this->trans('Delete selected', [], 'Admin.Actions'),
'icon' => 'icon-trash',
'confirm' => $this->trans('Delete selected items?', [], 'Admin.Notifications.Warning'),
],
];
}
public function initPageHeaderToolbar()
{
if (empty($this->display)) {
$this->page_header_toolbar_btn['new_tag'] = [
'href' => self::$currentIndex . '&addtag&token=' . $this->token,
'desc' => $this->trans('Add new tag', [], 'Admin.Shopparameters.Feature'),
'icon' => 'process-icon-new',
];
}
parent::initPageHeaderToolbar();
}
public function renderList()
{
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->_select = 'l.name as lang, COUNT(pt.id_product) as products';
$this->_join = '
LEFT JOIN `' . _DB_PREFIX_ . 'product_tag` pt
ON (a.`id_tag` = pt.`id_tag`)
LEFT JOIN `' . _DB_PREFIX_ . 'lang` l
ON (l.`id_lang` = a.`id_lang`)';
$this->_group = 'GROUP BY a.name, a.id_lang';
return parent::renderList();
}
public function postProcess()
{
if ($this->access('edit') && Tools::getValue('submitAdd' . $this->table)) {
$id = (int) Tools::getValue($this->identifier);
$obj = new $this->className($id);
if (Validate::isLoadedObject($obj)) {
/** @var Tag $obj */
$previous_products = $obj->getProducts();
$removed_products = [];
foreach ($previous_products as $product) {
if (!in_array($product['id_product'], $_POST['products'])) {
$removed_products[] = $product['id_product'];
}
}
if (Configuration::get('PS_SEARCH_INDEXATION')) {
Search::removeProductsSearchIndex($removed_products);
}
$obj->setProducts($_POST['products']);
}
}
return parent::postProcess();
}
/**
* @return string|void
*
* @throws SmartyException
*/
public function renderForm()
{
/** @var Tag|null $obj */
$obj = $this->loadObject(true);
if (!$obj) {
return;
}
$this->fields_form = [
'legend' => [
'title' => $this->trans('Tag', [], 'Admin.Shopparameters.Feature'),
'icon' => 'icon-tag',
],
'input' => [
[
'type' => 'text',
'label' => $this->trans('Name', [], 'Admin.Global'),
'name' => 'name',
'required' => true,
],
[
'type' => 'select',
'label' => $this->trans('Language', [], 'Admin.Global'),
'name' => 'id_lang',
'required' => true,
'options' => [
'query' => Language::getLanguages(false),
'id' => 'id_lang',
'name' => 'name',
],
],
],
'selects' => [
'products' => $obj->getProducts(true),
'products_unselected' => $obj->getProducts(false),
],
'submit' => [
'title' => $this->trans('Save', [], 'Admin.Actions'),
],
];
return parent::renderForm();
}
}

View File

@@ -0,0 +1,561 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
/**
* @property TaxRulesGroup $object
*/
class AdminTaxRulesGroupControllerCore extends AdminController
{
public $tax_rule;
public $selected_countries = [];
public $selected_states = [];
public $errors_tax_rule;
public function __construct()
{
$this->bootstrap = true;
$this->table = 'tax_rules_group';
$this->className = 'TaxRulesGroup';
$this->lang = false;
parent::__construct();
$this->fields_list = [
'id_tax_rules_group' => [
'title' => $this->trans('ID', [], 'Admin.Global'),
'align' => 'center',
'class' => 'fixed-width-xs',
],
'name' => [
'title' => $this->trans('Name', [], 'Admin.Global'),
],
'active' => [
'title' => $this->trans('Enabled', [], 'Admin.Global'),
'active' => 'status',
'type' => 'bool',
'orderby' => false,
'align' => 'center',
'class' => 'fixed-width-sm',
],
];
$this->bulk_actions = [
'delete' => [
'text' => $this->trans('Delete selected', [], 'Admin.Actions'),
'confirm' => $this->trans('Delete selected items?', [], 'Admin.Notifications.Warning'),
'icon' => 'icon-trash',
],
];
$this->_where .= ' AND a.deleted = 0';
}
public function initPageHeaderToolbar()
{
if (empty($this->display)) {
$this->page_header_toolbar_btn['new_tax_rules_group'] = [
'href' => self::$currentIndex . '&addtax_rules_group&token=' . $this->token,
'desc' => $this->trans('Add new tax rules group', [], 'Admin.International.Feature'),
'icon' => 'process-icon-new',
];
}
if ($this->display === 'edit') {
$this->page_header_toolbar_btn['new'] = [
'href' => '#',
'desc' => $this->trans('Add a new tax rule', [], 'Admin.International.Feature'),
];
}
parent::initPageHeaderToolbar();
}
public function renderList()
{
$this->addRowAction('edit');
$this->addRowAction('delete');
return parent::renderList();
}
public function initRulesList($id_group)
{
$this->table = 'tax_rule';
$this->list_id = 'tax_rule';
$this->identifier = 'id_tax_rule';
$this->className = 'TaxRule';
$this->lang = false;
$this->list_simple_header = false;
$this->toolbar_btn = null;
$this->list_no_link = true;
$this->bulk_actions = [
'delete' => ['text' => $this->trans('Delete selected', [], 'Admin.Actions'), 'confirm' => $this->trans('Delete selected items?', [], 'Admin.Notifications.Warning'), 'icon' => 'icon-trash'],
];
$this->fields_list = [
'country_name' => [
'title' => $this->trans('Country', [], 'Admin.Global'),
],
'state_name' => [
'title' => $this->trans('State', [], 'Admin.Global'),
],
'zipcode' => [
'title' => $this->trans('Zip/Postal code', [], 'Admin.Global'),
'class' => 'fixed-width-md',
],
'behavior' => [
'title' => $this->trans('Behavior', [], 'Admin.International.Feature'),
],
'rate' => [
'title' => $this->trans('Tax', [], 'Admin.Global'),
'class' => 'fixed-width-sm',
],
'description' => [
'title' => $this->trans('Description', [], 'Admin.Global'),
],
];
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->_select = '
c.`name` AS country_name,
s.`name` AS state_name,
CONCAT_WS(" - ", a.`zipcode_from`, a.`zipcode_to`) AS zipcode,
t.rate';
$this->_join = '
LEFT JOIN `' . _DB_PREFIX_ . 'country_lang` c
ON (a.`id_country` = c.`id_country` AND id_lang = ' . (int) $this->context->language->id . ')
LEFT JOIN `' . _DB_PREFIX_ . 'state` s
ON (a.`id_state` = s.`id_state`)
LEFT JOIN `' . _DB_PREFIX_ . 'tax` t
ON (a.`id_tax` = t.`id_tax` AND t.active = 1)';
$this->_where = 'AND `id_tax_rules_group` = ' . (int) $id_group;
$this->_use_found_rows = false;
$this->show_toolbar = false;
$this->tpl_list_vars = ['id_tax_rules_group' => (int) $id_group];
$this->_filter = false;
return parent::renderList();
}
/**
* @return string|void
*
* @throws SmartyException
*/
public function renderForm()
{
$this->fields_form = [
'legend' => [
'title' => $this->trans('Tax rules', [], 'Admin.International.Feature'),
'icon' => 'icon-money',
],
'input' => [
[
'type' => 'text',
'label' => $this->trans('Name', [], 'Admin.Global'),
'name' => 'name',
'required' => true,
'hint' => $this->trans('Invalid characters:', [], 'Admin.Notifications.Info') . ' <>{}',
],
[
'type' => 'switch',
'label' => $this->trans('Enable', [], 'Admin.Actions'),
'name' => 'active',
'required' => false,
'is_bool' => true,
'values' => [
[
'id' => 'active_on',
'value' => 1,
'label' => $this->trans('Yes', [], 'Admin.Global'),
],
[
'id' => 'active_off',
'value' => 0,
'label' => $this->trans('No', [], 'Admin.Global'),
],
],
],
],
'submit' => [
'title' => $this->trans('Save and stay', [], 'Admin.Actions'),
'stay' => true,
],
];
if (Shop::isFeatureActive()) {
$this->fields_form['input'][] = [
'type' => 'shop',
'label' => $this->trans('Store association', [], 'Admin.Global'),
'name' => 'checkBoxShopAsso',
];
}
if (!($obj = $this->loadObject(true))) {
return;
}
if (!isset($obj->id)) {
$content = parent::renderForm();
} else {
$this->page_header_toolbar_btn['new'] = [
'href' => '#',
'desc' => $this->trans('Add a new tax rule', [], 'Admin.International.Feature'),
];
$content = parent::renderForm();
$this->tpl_folder = 'tax_rules/';
$content .= $this->initRuleForm();
// We change the variable $ tpl_folder to avoid the overhead calling the file in list_action_edit.tpl in intList ();
$content .= $this->initRulesList((int) $obj->id);
}
return $content;
}
public function initRuleForm()
{
$this->fields_form[0]['form'] = [
'legend' => [
'title' => $this->trans('New tax rule', [], 'Admin.International.Feature'),
'icon' => 'icon-money',
],
'input' => [
[
'type' => 'select',
'label' => $this->trans('Country', [], 'Admin.Global'),
'name' => 'country',
'id' => 'country',
'options' => [
'query' => Country::getCountries($this->context->language->id),
'id' => 'id_country',
'name' => 'name',
'default' => [
'value' => 0,
'label' => $this->trans('All', [], 'Admin.Global'),
],
],
],
[
'type' => 'select',
'label' => $this->trans('State', [], 'Admin.Global'),
'name' => 'states[]',
'id' => 'states',
'multiple' => true,
'options' => [
'query' => [],
'id' => 'id_state',
'name' => 'name',
'default' => [
'value' => 0,
'label' => $this->trans('All', [], 'Admin.Global'),
],
],
],
[
'type' => 'hidden',
'name' => 'action',
],
[
'type' => 'text',
'label' => $this->trans('Zip/Postal code range', [], 'Admin.International.Feature'),
'name' => 'zipcode',
'required' => false,
'hint' => $this->trans('You can define a range of Zip/Postal codes (e.g., 75000-75015) or simply use one Zip/Postal code.', [], 'Admin.International.Help'),
],
[
'type' => 'select',
'label' => $this->trans('Behavior', [], 'Admin.International.Feature'),
'name' => 'behavior',
'required' => false,
'options' => [
'query' => [
[
'id' => 0,
'name' => $this->trans('This tax only', [], 'Admin.International.Feature'),
],
[
'id' => 1,
'name' => $this->trans('Combine', [], 'Admin.International.Feature'),
],
[
'id' => 2,
'name' => $this->trans('One after another', [], 'Admin.International.Feature'),
],
],
'id' => 'id',
'name' => 'name',
],
'hint' => [
$this->trans('You must define the behavior if an address matches multiple rules:', [], 'Admin.International.Help') . '<br>',
$this->trans('- This tax only: Will apply only this tax', [], 'Admin.International.Help') . '<br>',
$this->trans('- Combine: Combine taxes (e.g.: 10% + 5% = 15%)', [], 'Admin.International.Help') . '<br>',
$this->trans('- One after another: Apply taxes one after another (e.g.: 100 + 10% => 110 + 5% = 115.5)', [], 'Admin.International.Help'),
],
],
[
'type' => 'select',
'label' => $this->trans('Tax', [], 'Admin.Global'),
'name' => 'id_tax',
'required' => false,
'options' => [
'query' => Tax::getTaxes((int) $this->context->language->id),
'id' => 'id_tax',
'name' => 'name',
'default' => [
'value' => 0,
'label' => $this->trans('No Tax', [], 'Admin.International.Help'),
],
],
],
[
'type' => 'text',
'label' => $this->trans('Description', [], 'Admin.Global'),
'name' => 'description',
],
],
'submit' => [
'title' => $this->trans('Save and stay', [], 'Admin.Actions'),
'stay' => true,
],
];
if (!($obj = $this->loadObject(true))) {
return;
}
$this->fields_value = [
'action' => 'create_rule',
'id_tax_rules_group' => $obj->id,
'id_tax_rule' => '',
];
$this->getlanguages();
$helper = new HelperForm();
$helper->override_folder = $this->tpl_folder;
$helper->currentIndex = self::$currentIndex;
$helper->token = $this->token;
$helper->table = 'tax_rule';
$helper->identifier = 'id_tax_rule';
$helper->id = $obj->id;
$helper->toolbar_scroll = true;
$helper->show_toolbar = true;
$helper->languages = $this->_languages;
$helper->default_form_language = $this->default_form_language;
$helper->allow_employee_form_lang = $this->allow_employee_form_lang;
$helper->fields_value = $this->getFieldsValue($this->object);
$helper->toolbar_btn['save_new_rule'] = [
'href' => self::$currentIndex . '&amp;id_tax_rules_group=' . $obj->id . '&amp;action=create_rule&amp;token=' . $this->token,
'desc' => 'Save tax rule',
'class' => 'process-icon-save',
];
$helper->submit_action = 'create_rule';
return $helper->generateForm($this->fields_form);
}
public function initProcess()
{
if (Tools::isSubmit('deletetax_rule')) {
if ($this->access('delete')) {
$this->action = 'delete_tax_rule';
} else {
$this->errors[] = $this->trans('You do not have permission to delete this.', [], 'Admin.Notifications.Error');
}
} elseif (Tools::isSubmit('submitBulkdeletetax_rule')) {
if ($this->access('delete')) {
$this->action = 'bulk_delete_tax_rules';
} else {
$this->errors[] = $this->trans('You do not have permission to delete this.', [], 'Admin.Notifications.Error');
}
} elseif (Tools::getValue('action') == 'create_rule') {
if ($this->access('add')) {
$this->action = 'create_rule';
} else {
$this->errors[] = $this->trans('You do not have permission to add this.', [], 'Admin.Notifications.Error');
}
} else {
parent::initProcess();
}
}
protected function processCreateRule()
{
$zip_code = Tools::getValue('zipcode');
$zip_code = ('' === $zip_code) ? 0 : $zip_code;
$id_rule = (int) Tools::getValue('id_tax_rule');
$id_tax = (int) Tools::getValue('id_tax');
$id_tax_rules_group = (int) Tools::getValue('id_tax_rules_group');
$behavior = (int) Tools::getValue('behavior');
$description = pSQL(Tools::getValue('description'));
if ((int) ($id_country = Tools::getValue('country')) == 0) {
$countries = Country::getCountries($this->context->language->id);
$this->selected_countries = [];
foreach ($countries as $country) {
$this->selected_countries[] = (int) $country['id_country'];
}
} else {
$this->selected_countries = [$id_country];
}
$this->selected_states = Tools::getValue('states');
if (empty($this->selected_states) || count($this->selected_states) == 0) {
$this->selected_states = [0];
}
$tax_rules_group = new TaxRulesGroup((int) $id_tax_rules_group);
foreach ($this->selected_countries as $id_country) {
$first = true;
foreach ($this->selected_states as $id_state) {
if ($tax_rules_group->hasUniqueTaxRuleForCountry($id_country, $id_state, $id_rule)) {
$this->errors[] = $this->trans('A tax rule already exists for this country/state with tax only behavior.', [], 'Admin.International.Notification');
continue;
}
$tr = new TaxRule();
// update or creation?
if ($first) {
$tr->id = $id_rule;
$first = false;
}
$tr->id_tax = $id_tax;
$tax_rules_group = new TaxRulesGroup((int) $id_tax_rules_group);
$tr->id_tax_rules_group = (int) $tax_rules_group->id;
$tr->id_country = (int) $id_country;
$tr->id_state = (int) $id_state;
list($tr->zipcode_from, $tr->zipcode_to) = $tr->breakDownZipCode($zip_code);
// Construct Object Country
$country = new Country((int) $id_country, (int) $this->context->language->id);
if ($zip_code && $country->need_zip_code) {
if ($country->zip_code_format) {
foreach ([$tr->zipcode_from, $tr->zipcode_to] as $zip_code) {
if ($zip_code) {
if (!$country->checkZipCode($zip_code)) {
$this->errors[] = $this->trans(
'The Zip/Postal code is invalid. It must be typed as follows: %format% for %country%.',
[
'%format%' => str_replace('C', $country->iso_code, str_replace('N', '0', str_replace('L', 'A', $country->zip_code_format))),
'%country%' => $country->name,
],
'Admin.International.Notification'
);
}
}
}
}
}
$tr->behavior = (int) $behavior;
$tr->description = $description;
$this->tax_rule = $tr;
$_POST['id_state'] = $tr->id_state;
$this->errors = array_merge($this->errors, $this->validateTaxRule($tr));
if (count($this->errors) == 0) {
$tax_rules_group = $this->updateTaxRulesGroup($tax_rules_group);
$tr->id = (int) $tax_rules_group->getIdTaxRuleGroupFromHistorizedId((int) $tr->id);
$tr->id_tax_rules_group = (int) $tax_rules_group->id;
if (!$tr->save()) {
$this->errors[] = $this->trans('An error has occurred: Cannot save the current tax rule.', [], 'Admin.International.Notification');
}
}
}
}
if (count($this->errors) == 0) {
Tools::redirectAdmin(
self::$currentIndex . '&' . $this->identifier . '=' . (int) $tax_rules_group->id . '&conf=4&update' . $this->table . '&token=' . $this->token
);
} else {
$this->display = 'edit';
}
}
protected function processBulkDeleteTaxRules()
{
$this->deleteTaxRule(Tools::getValue('tax_ruleBox'));
}
protected function processDeleteTaxRule()
{
$this->deleteTaxRule([Tools::getValue('id_tax_rule')]);
}
public function displayAjaxUpdateTaxRule()
{
if ($this->access('view')) {
$id_tax_rule = Tools::getValue('id_tax_rule');
$tax_rules = new TaxRule((int) $id_tax_rule);
$output = [];
foreach (get_object_vars($tax_rules) as $key => $result) {
$output[$key] = $result;
}
die(json_encode($output));
}
}
protected function deleteTaxRule(array $id_tax_rule_list)
{
$result = true;
foreach ($id_tax_rule_list as $id_tax_rule) {
$tax_rule = new TaxRule((int) $id_tax_rule);
if (Validate::isLoadedObject($tax_rule)) {
$tax_rules_group = new TaxRulesGroup((int) $tax_rule->id_tax_rules_group);
$tax_rules_group = $this->updateTaxRulesGroup($tax_rules_group);
$tax_rule = new TaxRule($tax_rules_group->getIdTaxRuleGroupFromHistorizedId((int) $id_tax_rule));
if (Validate::isLoadedObject($tax_rule)) {
$result &= $tax_rule->delete();
}
}
}
$idTaxRulesGroup = isset($tax_rules_group) ? (int) $tax_rules_group->id : 0;
Tools::redirectAdmin(
self::$currentIndex . '&' . $this->identifier . '=' . $idTaxRulesGroup . '&conf=4&update' . $this->table . '&token=' . $this->token
);
}
/**
* Check if the tax rule could be added in the database.
*
* @param TaxRule $tr
*
* @return array
*/
protected function validateTaxRule(TaxRule $tr)
{
// @TODO: check if the rule already exists
return $tr->validateController();
}
/**
* @param TaxRulesGroup $object
*
* @return TaxRulesGroup
*/
protected function updateTaxRulesGroup(TaxRulesGroup $object)
{
static $tax_rules_group = null;
if ($tax_rules_group === null) {
$object->update();
$tax_rules_group = $object;
}
return $tax_rules_group;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
<?php
/**
* For the full copyright and license information, please view the
* docs/licenses/LICENSE.txt file that was distributed with this source code.
*/
class BoOrderCore extends PaymentModule
{
/** @var bool */
public $active = true;
/** @var string */
public $name = 'bo_order';
public function __construct()
{
$this->displayName = $this->trans('Back office order', [], 'Admin.Orderscustomers.Feature');
}
}

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.
*/
declare(strict_types=1);
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* This class is used only because some parts of the Back Office require a Controller
* to function (like the NullDispatcher). It is also used in integration tests for override.
*/
class DummyAdminControllerCore extends AdminController
{
public function __construct()
{
parent::__construct();
$this->id = 0;
$this->controller_type = 'dummy';
}
public function checkAccess()
{
return true;
}
public function viewAccess($disable = false)
{
return true;
}
public function postProcess()
{
return true;
}
public function display()
{
return '';
}
public function setMedia($isNewTheme = false)
{
return null;
}
public function initHeader()
{
return '';
}
public function initContent()
{
return '';
}
public function initCursedPage()
{
return '';
}
public function initFooter()
{
return '';
}
protected function redirect()
{
return '';
}
protected function buildContainer(): ContainerInterface
{
return SymfonyContainer::getInstance();
}
}

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;