* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
use PrestaShop\Autoload\PrestashopAutoload;
use PrestaShop\PrestaShop\Adapter\ContainerFinder;
use PrestaShop\PrestaShop\Adapter\LegacyLogger;
use PrestaShop\PrestaShop\Adapter\Module\ModuleDataProvider;
use PrestaShop\PrestaShop\Adapter\Module\Repository\ModuleRepository;
use PrestaShop\PrestaShop\Adapter\ServiceLocator;
use PrestaShop\PrestaShop\Core\Exception\ContainerNotFoundException;
use PrestaShop\PrestaShop\Core\Foundation\Filesystem\FileSystem;
use PrestaShop\PrestaShop\Core\Module\Legacy\ModuleInterface;
use PrestaShop\PrestaShop\Core\Module\WidgetInterface;
use PrestaShop\TranslationToolsBundle\Translation\Helper\DomainHelper;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\Filesystem\Filesystem as SfFileSystem;
use Symfony\Component\Finder\Finder;
abstract class ModuleCore implements ModuleInterface
{
/** @var int|null Module ID */
public $id = null;
/** @var string Version */
public $version;
public $database_version;
/**
* @since 1.5.0.1
*
* @var string Registered Version in database
*/
public $registered_version;
/** @var array filled with known compliant PS versions */
public $ps_versions_compliancy = [];
/** @var array filled with modules needed for install */
public $dependencies = [];
/** @var string|int|null Unique name */
public $name;
/** @var string Human name */
public $displayName;
/** @var string A little description of the module */
public $description;
/**
* @var string Text to display when ask for confirmation on uninstall action
*/
public $confirmUninstall = '';
/** @var string author of the module */
public $author;
/** @var string URI author of the module */
public $author_uri = '';
/** @var string Module key provided by addons.prestashop.com */
public $module_key = '';
/**
* @var bool Set to true to enable bootstrap theme on configuration page
*/
public $bootstrap = false;
public $description_full;
public $additional_description;
public $compatibility;
public $nb_rates;
public $avg_rate;
public $badges;
/** @var string */
public $message = '';
/** @var string */
public $logo = '';
/** @var array */
public $options;
/** @var array|string */
public $optionsHtml;
/** @var int need_instance */
public $need_instance = 1;
/** @var string Admin tab corresponding to the module */
public $tab = null;
/** @var bool Status */
public $active = false;
/** @var string Fill it if the module is installed but not yet set up */
public $warning;
public $enable_device = 7;
/** @var array to store the limited country */
public $limited_countries = [];
/** @var array names of the controllers */
public $controllers = [];
/** @var bool */
public $installed;
/** @var bool */
public $show_quick_view = false;
/** @var array used by AdminTab to determine which lang file to use (admin.php or module lang file) */
public static $classInModule = [];
/** @var array current language translations */
protected $_lang = [];
/** @var string Module web path (eg. '/shop/modules/modulename/') */
protected $_path = null;
/**
* @since 1.5.0.1
*
* @var string Module local path (eg. '/home/prestashop/modules/modulename/')
*/
protected $local_path = null;
/** @var array Array filled with module errors */
protected $_errors = [];
/** @var array Array array filled with module success */
protected $_confirmations = [];
/** @var string Main table used for modules installed */
protected $table = 'module';
/** @var string Identifier of the main table */
protected $identifier = 'id_module';
/** @var array|null Array cache filled with modules informations */
protected static $modules_cache;
/** @var array Array cache filled with modules instances */
protected static $_INSTANCE = [];
/** @var bool Config xml generation mode */
protected static $_generate_config_xml_mode = false;
/** @var array Array filled with cache translations */
protected static $l_cache = [];
/** @var array Array filled with cache permissions (modules / employee profiles) */
protected static $cache_permissions = [];
/** @var array Array filled with cache permissions (modules / employee profiles) */
protected static $cache_lgc_access = [];
/** @var Context */
protected $context;
/** @var Smarty_Data|Smarty_Internal_TemplateBase */
protected $smarty;
/** @var Smarty_Internal_Template|null */
protected $current_subtemplate = null;
protected static $update_translations_after_install = true;
protected static $_batch_mode = false;
protected static $_defered_clearCache = [];
protected static $_defered_func_call = [];
/**
* @var array array of arrays representing tabs added by this module
*
* @see PrestaShop\PrestaShop\Adapter\Module\Tab\RegisterTabs($module)
*/
protected $tabs = [];
/** @var bool Define if we will log modules performances for this session */
public static $_log_modules_perfs = null;
/** @var bool Random session for modules perfs logs */
public static $_log_modules_perfs_session = null;
/** @var \Symfony\Component\DependencyInjection\ContainerInterface */
private $container;
/** @var array|null used to cache module ids */
protected static $cachedModuleNames = null;
/** @var int Defines the multistore compatibility level of the module */
public $multistoreCompatibility = self::MULTISTORE_COMPATIBILITY_UNKNOWN;
public const CACHE_FILE_MODULES_LIST = '/config/xml/modules_list.xml';
public const CACHE_FILE_TAB_MODULES_LIST = '/config/xml/tab_modules_list.xml';
public const CACHE_FILE_ALL_COUNTRY_MODULES_LIST = '/config/xml/modules_native_addons.xml';
public const MULTISTORE_COMPATIBILITY_NO = -20;
public const MULTISTORE_COMPATIBILITY_NOT_CONCERNED = -10;
public const MULTISTORE_COMPATIBILITY_UNKNOWN = 0;
public const MULTISTORE_COMPATIBILITY_PARTIAL = 10;
public const MULTISTORE_COMPATIBILITY_YES = 20;
public static $hosted_modules_blacklist = ['autoupgrade'];
public static function setContextInstanceForTesting(Context $context)
{
/** @var Module $module */
foreach (static::$_INSTANCE as $module) {
$module->context = $context;
}
}
/**
* Set the flag to indicate we are doing an import.
*
* @param bool $value
*/
public static function setBatchMode($value)
{
static::$_batch_mode = (bool) $value;
}
/**
* @return bool
*/
public static function getBatchMode()
{
return static::$_batch_mode;
}
public static function processDeferedFuncCall()
{
static::setBatchMode(false);
foreach (static::$_defered_func_call as $func_call) {
call_user_func_array($func_call[0], $func_call[1]);
}
static::$_defered_func_call = [];
}
/**
* Clear the caches stored in $_defered_clearCache.
*/
public static function processDeferedClearCache()
{
static::setBatchMode(false);
foreach (static::$_defered_clearCache as $clearCache_array) {
static::_deferedClearCache($clearCache_array[0], $clearCache_array[1], $clearCache_array[2]);
}
static::$_defered_clearCache = [];
}
/**
* Constructor.
*
* @param string|null $name (Deprecated parameter)
* @param Context|null $context
*/
public function __construct($name = null, Context $context = null)
{
if ($name !== null) {
Tools::displayParameterAsDeprecated('name');
}
if (!isset($this->ps_versions_compliancy['min'])) {
$this->ps_versions_compliancy['min'] = '1.4.0.0';
}
if (!isset($this->ps_versions_compliancy['max'])) {
$this->ps_versions_compliancy['max'] = _PS_VERSION_;
}
$minParts = explode('.', $this->ps_versions_compliancy['min']);
$maxParts = explode('.', $this->ps_versions_compliancy['max']);
// Since v8, we don't pad versions
if ((int) current($minParts) < 8) {
$this->ps_versions_compliancy['min'] = str_pad($this->ps_versions_compliancy['min'], 7, '.0');
}
if ((int) current($maxParts) < 8) {
$padLength = strlen($this->ps_versions_compliancy['max']) + (4 - count($maxParts)) * 4;
$this->ps_versions_compliancy['max'] = str_pad($this->ps_versions_compliancy['max'], $padLength, '.999');
}
// Load context and smarty
$this->context = $context ? $context : Context::getContext();
if (is_object($this->context->smarty)) {
$this->smarty = $this->context->smarty->createData($this->context->smarty);
}
// If the module has no name we gave him its id as name
if ($this->name === null) {
$this->name = $this->id;
}
// If the module has the name we load the corresponding data from the cache
if ($this->name != null) {
// If cache is not generated, we generate it
if (static::$modules_cache == null && !is_array(static::$modules_cache)) {
$id_shop = (Validate::isLoadedObject($this->context->shop) ? $this->context->shop->id : Configuration::get('PS_SHOP_DEFAULT'));
static::$modules_cache = [];
// Join clause is done to check if the module is activated in current shop context
$result = Db::getInstance()->executeS('
SELECT m.`id_module`, m.`name`, ms.`id_module`as `mshop`
FROM `' . _DB_PREFIX_ . 'module` m
LEFT JOIN `' . _DB_PREFIX_ . 'module_shop` ms
ON m.`id_module` = ms.`id_module`
AND ms.`id_shop` = ' . (int) $id_shop);
foreach ($result as $row) {
static::$modules_cache[$row['name']] = $row;
static::$modules_cache[$row['name']]['active'] = ($row['mshop'] > 0) ? 1 : 0;
}
}
// We load configuration from the cache
if (isset(static::$modules_cache[$this->name])) {
if (isset(static::$modules_cache[$this->name]['id_module'])) {
$this->id = static::$modules_cache[$this->name]['id_module'];
}
foreach (static::$modules_cache[$this->name] as $key => $value) {
if (array_key_exists($key, get_object_vars($this))) {
$this->{$key} = $value;
}
}
$this->_path = __PS_BASE_URI__ . 'modules/' . $this->name . '/';
}
if (!$this->context->controller instanceof Controller) {
static::$modules_cache = null;
}
$this->local_path = _PS_MODULE_DIR_ . $this->name . '/';
}
}
/**
* Insert module into datable.
*/
public function install()
{
Hook::exec('actionModuleInstallBefore', ['object' => $this]);
// Check module name validation
if (!Validate::isModuleName($this->name)) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('Unable to install the module (Module name is not valid).', [], 'Admin.Modules.Notification');
return false;
}
// Check PS version compliancy
if (!$this->checkCompliancy()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('The version of your module is not compliant with your PrestaShop version.', [], 'Admin.Modules.Notification');
return false;
}
// Check module dependencies
if (count($this->dependencies) > 0) {
foreach ($this->dependencies as $dependency) {
if (!Db::getInstance()->getRow('SELECT `id_module` FROM `' . _DB_PREFIX_ . 'module` WHERE LOWER(`name`) = \'' . pSQL(Tools::strtolower($dependency)) . '\'')) {
$error = Context::getContext()->getTranslator()->trans('Before installing this module, you have to install this/these module(s) first:', [], 'Admin.Modules.Notification') . ' ';
foreach ($this->dependencies as $d) {
$error .= '- ' . $d . ' ';
}
$this->_errors[] = $error;
return false;
}
}
}
// Check if module is installed
$result = (new ModuleDataProvider(new LegacyLogger(), $this->getTranslator()))->isInstalled($this->name);
if ($result) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('This module has already been installed.', [], 'Admin.Modules.Notification');
return false;
}
if (!$this->installControllers()) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('Could not install module controllers.', [], 'Admin.Modules.Notification');
$this->uninstallOverrides();
return false;
}
// Install module and retrieve the installation id
$result = Db::getInstance()->insert($this->table, [
'name' => $this->name,
'active' => 1,
'version' => $this->version,
]);
if (!$result) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('Technical error: PrestaShop could not install this module.', [], 'Admin.Modules.Notification');
if (method_exists($this, 'uninstallTabs')) {
$this->uninstallTabs();
}
$this->uninstallOverrides();
return false;
}
$this->id = Db::getInstance()->Insert_ID();
Cache::clean('Module::isInstalled' . $this->name);
Cache::clean('Module::getModuleIdByName_' . pSQL($this->name));
// Enable the module for current shops in context
if (!$this->enable()) {
return false;
}
// Permissions management
foreach (['CREATE', 'READ', 'UPDATE', 'DELETE'] as $action) {
$slug = 'ROLE_MOD_MODULE_' . strtoupper($this->name) . '_' . $action;
Db::getInstance()->execute(
'INSERT INTO `' . _DB_PREFIX_ . 'authorization_role` (`slug`) VALUES ("' . $slug . '")'
);
Db::getInstance()->execute('
INSERT INTO `' . _DB_PREFIX_ . 'module_access` (`id_profile`, `id_authorization_role`) (
SELECT id_profile, "' . Db::getInstance()->Insert_ID() . '"
FROM ' . _DB_PREFIX_ . 'access a
LEFT JOIN `' . _DB_PREFIX_ . 'authorization_role` r
ON r.id_authorization_role = a.id_authorization_role
WHERE r.slug = "ROLE_MOD_TAB_ADMINMODULESSF_' . $action . '"
)');
}
// Adding Restrictions for client groups
Group::addRestrictionsForModule($this->id, Shop::getShops(true, null, true));
Hook::exec('actionModuleInstallAfter', ['object' => $this]);
if (Module::$update_translations_after_install) {
$this->updateModuleTranslations();
}
return true;
}
/**
* Important: Do not type this method for compatibility reason.
* If your module aims to be compatible for older PHP versions, it will
* not be possible if we add strict typing as PHP 5.6 (for example) cannot strict type with bool.
*
* @return bool
*/
public function postInstall()
{
return true;
}
public function checkCompliancy()
{
if (version_compare(_PS_VERSION_, $this->ps_versions_compliancy['min'], '<') || version_compare(_PS_VERSION_, $this->ps_versions_compliancy['max'], '>')) {
return false;
} else {
return true;
}
}
public static function updateTranslationsAfterInstall($update = true)
{
Module::$update_translations_after_install = (bool) $update;
}
public function updateModuleTranslations()
{
return Language::updateModulesTranslations([$this->name]);
}
/**
* Set errors, warning or success message of a module upgrade.
*
* @param array $upgrade_detail
*/
protected function setUpgradeMessage($upgrade_detail)
{
// Store information if a module has been upgraded (memory optimization)
if ($upgrade_detail['available_upgrade']) {
if ($upgrade_detail['success']) {
$this->_confirmations[] = Context::getContext()->getTranslator()->trans('Current version: %s', [$this->version], 'Admin.Modules.Notification');
$this->_confirmations[] = Context::getContext()->getTranslator()->trans('%d file upgrade applied', [$upgrade_detail['number_upgraded']], 'Admin.Modules.Notification');
} else {
if (!$upgrade_detail['number_upgraded']) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('No upgrade has been applied', [], 'Admin.Modules.Notification');
} else {
$this->_errors[] = Context::getContext()->getTranslator()->trans('Upgraded from: %s to %s', [$upgrade_detail['upgraded_from'], $upgrade_detail['upgraded_to']], 'Admin.Modules.Notification');
$this->_errors[] = Context::getContext()->getTranslator()->trans('%d upgrade left', [$upgrade_detail['number_upgrade_left']], 'Admin.Modules.Notification');
}
if (isset($upgrade_detail['duplicate']) && $upgrade_detail['duplicate']) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('Module %s cannot be upgraded this time: please refresh this page to update it.', [$this->name], 'Admin.Modules.Notification');
} else {
$this->_errors[] = Context::getContext()->getTranslator()->trans('To prevent any problem, this module has been turned off', [], 'Admin.Modules.Notification');
}
}
}
}
/**
* Init the upgrade module.
*
* @param Module|stdClass $module
*
* @return bool
*/
public static function initUpgradeModule($module)
{
if (((int) $module->installed == 1) & (empty($module->database_version) === true)) {
Module::upgradeModuleVersion($module->name, $module->version);
$module->database_version = $module->version;
}
// Init cache upgrade details
static::$modules_cache[$module->name]['upgrade'] = [
'success' => false, // bool to know if upgrade succeed or not
'available_upgrade' => 0, // Number of available module before any upgrade
'number_upgraded' => 0, // Number of upgrade done
'number_upgrade_left' => 0,
'upgrade_file_left' => [], // List of the upgrade file left
'version_fail' => 0, // Version of the upgrade failure
'upgraded_from' => 0, // Version number before upgrading anything
'upgraded_to' => 0, // Last upgrade applied
];
// Need Upgrade will check and load upgrade file to the moduleCache upgrade case detail
$ret = $module->installed && Module::needUpgrade($module);
return $ret;
}
/**
* Run the upgrade for a given module name and version.
*
* @return array
*/
public function runUpgradeModule()
{
$upgrade = &static::$modules_cache[$this->name]['upgrade'];
foreach ($upgrade['upgrade_file_left'] as $num => $file_detail) {
foreach ($file_detail['upgrade_function'] as $item) {
if (function_exists($item)) {
$upgrade['success'] = false;
$upgrade['duplicate'] = true;
break 2;
}
}
include $file_detail['file'];
// Call the upgrade function if defined
$upgrade['success'] = false;
foreach ($file_detail['upgrade_function'] as $item) {
if (function_exists($item)) {
$upgrade['success'] = $item($this);
}
}
// Set detail when an upgrade succeed or failed
if ($upgrade['success']) {
++$upgrade['number_upgraded'];
$upgrade['upgraded_to'] = $file_detail['version'];
unset($upgrade['upgrade_file_left'][$num]);
} else {
$upgrade['version_fail'] = $file_detail['version'];
// If any errors, the module is disabled
$this->disable();
break;
}
}
$upgrade['number_upgrade_left'] = count($upgrade['upgrade_file_left']);
// Update module version in DB with the last succeed upgrade
if ($upgrade['upgraded_to']) {
Module::upgradeModuleVersion($this->name, $upgrade['upgraded_to']);
}
$this->setUpgradeMessage($upgrade);
return $upgrade;
}
/**
* Upgrade the registered version to a new one.
*
* @param string $name
* @param string $version
*
* @return bool
*/
public static function upgradeModuleVersion($name, $version)
{
return Db::getInstance()->execute('
UPDATE `' . _DB_PREFIX_ . 'module` m
SET m.version = \'' . pSQL($version) . '\'
WHERE m.name = \'' . pSQL($name) . '\'');
}
/**
* Check if a module need to be upgraded.
* This method modify the module_cache adding an upgrade list file.
*
* @param Module $module
*
* @return bool|null Boolean if Module::$version != Module::$database_version, null instead
*/
public static function needUpgrade($module)
{
static::$modules_cache[$module->name]['upgrade']['upgraded_from'] = $module->database_version;
// Check the version of the module with the registered one and look if any upgrade file exist
if (Tools::version_compare($module->version, $module->database_version, '>')) {
$old_version = $module->database_version;
$module = Module::getInstanceByName($module->name);
if ($module instanceof Module) {
return $module->loadUpgradeVersionList($module->name, $module->version, $old_version);
}
}
return null;
}
/**
* Load the available list of upgrade of a specified module
* with an associated version.
*
* @param string $module_name
* @param string $module_version
* @param string $registered_version
*
* @return bool to know directly if any files have been found
*/
protected static function loadUpgradeVersionList($module_name, $module_version, $registered_version)
{
$list = [];
$upgrade_path = _PS_MODULE_DIR_ . $module_name . '/upgrade/';
// Check if folder exist and it could be read
if (file_exists($upgrade_path) && ($files = scandir($upgrade_path, SCANDIR_SORT_NONE))) {
// Read each file name
foreach ($files as $file) {
if (!in_array($file, ['.', '..', '.svn', 'index.php']) && preg_match('/\.php$/', $file)) {
$tab = explode('-', $file);
if (!isset($tab[1])) {
continue;
}
$file_version = basename($tab[1], '.php');
// Compare version, if minor than actual, we need to upgrade the module
if (count($tab) == 2 &&
(Tools::version_compare($file_version, $module_version, '<=') &&
Tools::version_compare($file_version, $registered_version, '>'))) {
$list[] = [
'file' => $upgrade_path . $file,
'version' => $file_version,
'upgrade_function' => [
'upgrade_module_' . str_replace('.', '_', $file_version),
'upgradeModule' . str_replace('.', '', $file_version), ],
];
}
}
}
}
// No files upgrade, then upgrade succeed
if (count($list) == 0) {
static::$modules_cache[$module_name]['upgrade']['success'] = true;
Module::upgradeModuleVersion($module_name, $module_version);
}
usort($list, 'ps_module_version_sort');
// Set the list to module cache
static::$modules_cache[$module_name]['upgrade']['upgrade_file_left'] = $list;
static::$modules_cache[$module_name]['upgrade']['available_upgrade'] = count($list);
return (bool) count($list);
}
/**
* Return the status of the upgraded module.
*
* @param string $module_name
*
* @return bool
*/
public static function getUpgradeStatus($module_name)
{
return isset(static::$modules_cache[$module_name]) &&
static::$modules_cache[$module_name]['upgrade']['success'];
}
/**
* Uninstalls the module from database.
*
* @return bool result
*/
public function uninstall()
{
Hook::exec('actionModuleUninstallBefore', ['object' => $this]);
// Check if module instance is valid
if (!Validate::isUnsignedId($this->id)) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('The module is not installed.', [], 'Admin.Modules.Notification');
return false;
}
// Uninstall all overrides this module may have used
if (!$this->uninstallOverrides()) {
return false;
}
// Retrieve hooks used by the module
$sql = 'SELECT DISTINCT(`id_hook`) FROM `' . _DB_PREFIX_ . 'hook_module` WHERE `id_module` = ' . (int) $this->id;
$result = Db::getInstance()->executeS($sql);
foreach ($result as $row) {
// Unhook this module from each of the hooks
$this->unregisterHook((int) $row['id_hook']);
// Remove all hook conditions that may have been configured - don't confuse it with error exception. :-)
$this->unregisterExceptions((int) $row['id_hook']);
}
// Remove all configured meta data (titles, URLs etc.) for this module's front controllers
foreach ($this->controllers as $controller) {
$page_name = 'module-' . $this->name . '-' . $controller;
$meta = Db::getInstance()->getValue('SELECT id_meta FROM `' . _DB_PREFIX_ . 'meta` WHERE page="' . pSQL($page_name) . '"');
if ((int) $meta > 0) {
Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'meta_lang` WHERE id_meta=' . (int) $meta);
Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'meta` WHERE id_meta=' . (int) $meta);
}
}
// Disable the module for all shops
$this->disable(true);
// Delete permissions module access
$roles = Db::getInstance()->executeS('SELECT `id_authorization_role` FROM `' . _DB_PREFIX_ . 'authorization_role` WHERE `slug` LIKE "ROLE_MOD_MODULE_' . strtoupper($this->name) . '_%"');
if (!empty($roles)) {
foreach ($roles as $role) {
Db::getInstance()->execute(
'DELETE FROM `' . _DB_PREFIX_ . 'module_access` WHERE `id_authorization_role` = ' . $role['id_authorization_role']
);
Db::getInstance()->execute(
'DELETE FROM `' . _DB_PREFIX_ . 'authorization_role` WHERE `id_authorization_role` = ' . $role['id_authorization_role']
);
}
}
// Remove restrictions for client groups
Group::truncateRestrictionsByModule($this->id);
// Uninstall the module
if (Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'module` WHERE `id_module` = ' . (int) $this->id)) {
Cache::clean('Module::isInstalled' . $this->name);
Cache::clean('Module::getModuleIdByName_' . pSQL($this->name));
Hook::exec('actionModuleUninstallAfter', ['object' => $this]);
return true;
}
return false;
}
/**
* This function enable module $name. If an $name is an array,
* this will enable all of them.
*
* @param array|string $name
*
* @return bool
*
* @since 1.4.1
* @deprecated since 1.7
* @see PrestaShop\PrestaShop\Core\Addon\Module\ModuleManager->enable($name)
*/
public static function enableByName($name)
{
// If $name is not an array, we set it as an array
if (!is_array($name)) {
$name = [$name];
}
$res = true;
// Enable each module
foreach ($name as $n) {
if (Validate::isModuleName($n)) {
$res &= Module::getInstanceByName($n)->enable();
}
}
return $res;
}
/**
* Activate current module.
*
* @param bool $force_all If true, enable module for all shop
*
* @return bool
*/
public function enable($force_all = false)
{
// Retrieve all shops where the module is enabled
$list = Shop::getContextListShopID();
if (!$this->id || !is_array($list)) {
return false;
}
$sql = 'SELECT `id_shop` FROM `' . _DB_PREFIX_ . 'module_shop`
WHERE `id_module` = ' . (int) $this->id .
((!$force_all) ? ' AND `id_shop` IN(' . implode(', ', $list) . ')' : '');
// Store the results in an array
$items = [];
if ($results = Db::getInstance()->executeS($sql)) {
foreach ($results as $row) {
$items[] = $row['id_shop'];
}
}
if ($this->getOverrides() != null) {
// Install overrides
try {
$this->installOverrides();
} catch (Exception $e) {
$this->_errors[] = Context::getContext()->getTranslator()->trans('Unable to install override: %s', [$e->getMessage()], 'Admin.Modules.Notification');
$this->uninstallOverrides();
return false;
}
}
// Enable module in the shop where it is not enabled yet
$moduleActivated = false;
foreach ($list as $id) {
if (!in_array($id, $items)) {
Db::getInstance()->insert('module_shop', [
'id_module' => $this->id,
'id_shop' => $id,
]);
$moduleActivated = true;
}
}
// set active to 1 in the module table
Db::getInstance()->update('module', ['active' => 1], 'id_module = ' . (int) $this->id);
if ($moduleActivated) {
$this->loadBuiltInTranslations();
}
return true;
}
public function enableDevice($device)
{
Db::getInstance()->execute(
'
UPDATE ' . _DB_PREFIX_ . 'module_shop
SET enable_device = enable_device + ' . (int) $device . '
WHERE (enable_device &~ ' . (int) $device . ' OR enable_device = 0) AND id_module=' . (int) $this->id .
Shop::addSqlRestriction()
);
return true;
}
public function disableDevice($device)
{
Db::getInstance()->execute(
'UPDATE ' . _DB_PREFIX_ . 'module_shop
SET enable_device = enable_device - ' . (int) $device . '
WHERE enable_device & ' . (int) $device . ' AND id_module=' . (int) $this->id .
Shop::addSqlRestriction()
);
return true;
}
/**
* This function disable all module $name. If an $name is an array,
* this will disable all of them.
*
* @param array|string $name
*
* @return bool
*
* @since 1.7
*/
public static function disableAllByName($name)
{
// If $name is not an array, we set it as an array
if (!is_array($name)) {
$name = [$name];
}
$res = true;
// Disable each module
foreach ($name as $n) {
$sql = 'DELETE `' . _DB_PREFIX_ . 'module_shop` FROM `' . _DB_PREFIX_ . 'module_shop` JOIN `' . _DB_PREFIX_ . 'module` USING (id_module) WHERE `name` = "' . pSQL($n) . '"';
$res &= Db::getInstance()->execute($sql);
}
return $res;
}
/**
* This function disable module $name. If an $name is an array,
* this will disable all of them.
*
* @param array|string $name
*
* @return bool
*
* @since 1.4.1
* @deprecated since 1.7
* @see PrestaShop\PrestaShop\Core\Addon\Module\ModuleManager->disable($name)
*/
public static function disableByName($name)
{
// If $name is not an array, we set it as an array
if (!is_array($name)) {
$name = [$name];
}
$res = true;
// Disable each module
foreach ($name as $n) {
if (Validate::isModuleName($n)) {
$res &= Module::getInstanceByName($n)->disable();
}
}
return $res;
}
/**
* Desactivate current module.
*
* @param bool $force_all If true, disable module for all shop
*
* @return bool
*/
public function disable($force_all = false)
{
$result = true;
if ($this->getOverrides() != null) {
$result &= $this->uninstallOverrides();
}
// Disable module for all shops or contextual shops
$whereIdShop = $force_all ? '' : ' AND `id_shop` IN(' . implode(', ', Shop::getContextListShopID()) . ')';
$result &= Db::getInstance()->delete('module_shop', '`id_module` = ' . (int) $this->id . $whereIdShop);
// if module has no more shop associations, set module.active = 0
if (!$this->hasShopAssociations()) {
$result &= Db::getInstance()->update('module', ['active' => 0], 'id_module = ' . (int) $this->id);
}
return (bool) $result;
}
public function hasShopAssociations(): bool
{
$sql = "SELECT m.id_module FROM %smodule m INNER JOIN %smodule_shop ms ON ms.id_module = m.id_module WHERE m.id_module = '%s'";
$result = Db::getInstance()->getRow(sprintf($sql, _DB_PREFIX_, _DB_PREFIX_, (int) $this->id));
return isset($result['id_module']);
}
/**
* Display flags in forms for translations.
*
* @deprecated since 1.6.0.10
*
* @param array $languages All languages available
* @param int $default_language Default language id
* @param string $ids Multilingual div ids in form
* @param string $id Current div id]
* @param bool $return define the return way : false for a display, true for a return
* @param bool $use_vars_instead_of_ids use an js vars instead of ids seperate by "ยค"
*
* @return bool|string|void
*/
public function displayFlags($languages, $default_language, $ids, $id, $return = false, $use_vars_instead_of_ids = false)
{
if (count($languages) == 1) {
return false;
}
$output = '