[-] PROJECT : Modules now handle PrestaShop version compliancy and dependencies check

This commit is contained in:
fSerny
2011-12-15 20:11:43 +00:00
parent 3928434d64
commit 9249aac8d5
2 changed files with 161 additions and 96 deletions

View File

@@ -33,6 +33,12 @@ abstract class ModuleCore
/** @var float Version */
public $version;
/** @var array filled with known compliant PS versions */
public $ps_versions_compliancy = array('min' => '1.4', 'max' => '1.6');
/** @var array filled with modules needed for install */
public $dependencies = array();
/** @var string Unique name */
public $name;
@@ -54,50 +60,49 @@ abstract class ModuleCore
/** @var boolean Status */
public $active = false;
/** @var string Fill it if the module is installed but not yet set up */
public $warning;
/** @var array to store the limited country */
public $limited_countries = array();
/** @var array used by AdminTab to determine which lang file to use (admin.php or module lang file) */
public static $classInModule = array();
/** @var array current language translations */
protected $_lang = array();
/** @var string Module web path (eg. '/shop/modules/modulename/') */
protected $_path = NULL;
/** @var string Fill it if the module is installed but not yet set up */
public $warning;
/** @var string Message display before uninstall a module */
public $beforeUninstall = NULL;
/** @var protected array filled with module errors */
protected $_errors = false;
/** @var protected string main table used for modules installed */
protected $table = 'module';
/** @var protected string identifier of the main table */
protected $identifier = 'id_module';
public static $_db;
/** @var array to store the limited country */
public $limited_countries = array();
protected static $modulesCache;
/** @var protected array cache filled with modules informations */
protected static $modules_cache;
/** @var protected array cache filled with modules instances */
protected static $_INSTANCE = array();
protected static $_generateConfigXmlMode = false;
/** @var protected boolean filled with config xml generation mode */
protected static $_generate_config_xml_mode = false;
/** @var protected array filled with cache translations */
protected static $l_cache = array();
/** @var protected array filled with cache permissions (modules / employee profiles) */
protected static $cache_permissions = array();
/**
* @var array used by AdminTab to determine which lang file to use (admin.php or module lang file)
*/
public static $classInModule = array();
/** @var Context */
protected $context;
/**
* @var Smarty_Data
*/
/** @var Smarty_Data */
protected $smarty;
/**
@@ -108,36 +113,41 @@ abstract class ModuleCore
*/
public function __construct($name = null, Context $context = null)
{
// Load context and smarty
$this->context = $context ? $context : Context::getContext();
$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 (self::$modulesCache == NULL AND !is_array(self::$modulesCache))
// If cache is not generated, we generate it
if (self::$modules_cache == NULL AND !is_array(self::$modules_cache))
{
$list = $this->context->shop->getListOfID();
// Join clause is done to check if the module is activated in current shop context
$sql = 'SELECT m.id_module, m.name, (
SELECT COUNT(*) FROM '._DB_PREFIX_.'module_shop ms WHERE m.id_module = ms.id_module AND ms.id_shop IN ('.implode(',', $list).')
) as total
FROM '._DB_PREFIX_.'module m';
self::$modulesCache = array();
$list = $this->context->shop->getListOfID();
$sqlLimitShop = 'SELECT COUNT(*) FROM `'._DB_PREFIX_.'module_shop` ms WHERE m.`id_module` = ms.`id_module` AND ms.`id_shop` IN ('.implode(',', $list).')';
$sql = 'SELECT m.`id_module`, m.`name`, ('.$sqlLimitShop.') as total FROM `'._DB_PREFIX_.'module` m';
// Result is cached
self::$modules_cache = array();
$result = Db::getInstance()->executeS($sql);
foreach ($result as $row)
{
self::$modulesCache[$row['name']] = $row;
self::$modulesCache[$row['name']]['active'] = ($row['total'] == count($list)) ? true : false;
self::$modules_cache[$row['name']] = $row;
self::$modules_cache[$row['name']]['active'] = ($row['total'] == count($list)) ? true : false;
}
}
if (isset(self::$modulesCache[$this->name]))
// We load configuration from the cache
if (isset(self::$modules_cache[$this->name]))
{
$this->active = self::$modulesCache[$this->name]['active'];
$this->id = self::$modulesCache[$this->name]['id_module'];
foreach (self::$modulesCache[$this->name] AS $key => $value)
$this->active = self::$modules_cache[$this->name]['active'];
$this->id = self::$modules_cache[$this->name]['id_module'];
foreach (self::$modules_cache[$this->name] AS $key => $value)
if (key_exists($key, $this))
$this->{$key} = $value;
$this->_path = __PS_BASE_URI__.'modules/'.$this->name.'/';
@@ -145,6 +155,9 @@ abstract class ModuleCore
}
}
/**
* Get SQL modules restriction by shop
*/
protected function sqlShopRestriction($share = false, $alias = null)
{
return $this->context->shop->addSqlRestriction($share, $alias, 'shop');
@@ -155,20 +168,47 @@ abstract class ModuleCore
*/
public function install()
{
// Check module name validation
if (!Validate::isModuleName($this->name))
die(Tools::displayError());
$result = Db::getInstance()->getRow('
SELECT `id_module`
FROM `'._DB_PREFIX_.'module`
WHERE `name` = \''.pSQL($this->name).'\'');
if ($result)
return false;
$result = Db::getInstance()->AutoExecute(_DB_PREFIX_.$this->table, array('name' => $this->name, 'active' => 1), 'INSERT');
if (!$result)
// Check PS version compliancy
if (version_compare(_PS_VERSION_, $this->ps_versions_compliancy['min']) < 0 || version_compare(_PS_VERSION_, $this->ps_versions_compliancy['max']) > 0)
{
$this->_errors[] = $this->l('The version of your module is not compliant with your PrestaShop version.');
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 `name` = \''.pSQL($dependency).'\''))
{
$error = $this->l('Before installing this module, you have to installed these/this module(s) first :').'<br />';
foreach ($this->dependencies as $d)
$error .= '- '.$d.'<br />';
$this->_errors[] = $error;
return false;
}
// Check if module is installed
$result = Db::getInstance()->getRow('SELECT `id_module` FROM `'._DB_PREFIX_.'module` WHERE `name` = \''.pSQL($this->name).'\'');
if ($result)
{
$this->_errors[] = $this->l('This module has already been installed.');
return false;
}
// Install module and retrieve the installation id
$result = Db::getInstance()->autoExecute(_DB_PREFIX_.$this->table, array('name' => $this->name, 'active' => 1), 'INSERT');
if (!$result)
{
$this->_errors[] = $this->l('Technical error : PrestaShop could not installed this module.');
return false;
}
$this->id = Db::getInstance()->Insert_ID();
// Enable the module for all shops
$this->enable(true);
// Permissions management
@@ -186,6 +226,7 @@ abstract class ModuleCore
WHERE id_tab = (SELECT `id_tab` FROM '._DB_PREFIX_.'tab WHERE class_name = \'AdminModules\' LIMIT 1)
AND a.`view` = 0
)');
// Adding Restrictions for client groups
Group::addRestrictionsForModule($this->id, Shop::getShops(true, null, true));
@@ -199,31 +240,31 @@ abstract class ModuleCore
*/
public function uninstall()
{
// Check module installation id validation
if (!Validate::isUnsignedId($this->id))
return false;
$sql = 'SELECT id_hook
FROM '._DB_PREFIX_.'hook_module hm
WHERE id_module = '.(int)$this->id;
// Retrieve hooks used by the module
$sql = 'SELECT `id_hook` FROM `'._DB_PREFIX_.'hook_module` WHERE `id_module` = '.(int)$this->id;
$result = Db::getInstance()->executeS($sql);
foreach ($result AS $row)
{
$sql = 'DELETE FROM `'._DB_PREFIX_.'hook_module`
WHERE `id_module` = '.(int)$this->id.'
AND `id_hook` = '.(int)$row['id_hook'];
$sql = 'DELETE FROM `'._DB_PREFIX_.'hook_module` WHERE `id_module` = '.(int)$this->id.' AND `id_hook` = '.(int)$row['id_hook'];
Db::getInstance()->execute($sql);
$this->cleanPositions($row['id_hook']);
}
// Disable the module for all shops
$this->disable(true);
// Delete permissions module access
Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'module_access` WHERE `id_module` = '.(int)$this->id);
// Remove restrictions for client groups
Group::truncateRestrictionsByModule($this->id);
return Db::getInstance()->execute('
DELETE FROM `'._DB_PREFIX_.'module`
WHERE `id_module` = '.(int)$this->id);
// Uninstall the module
return Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'module` WHERE `id_module` = '.(int)$this->id);
}
/**
@@ -236,9 +277,11 @@ abstract class ModuleCore
*/
public static function enableByName($name)
{
// If $name is not an array, we set it as an array
if (!is_array($name))
$name = array($name);
// Enable each module
foreach ($name as $k => $v)
Module::getInstanceByName($name)->enable();
}
@@ -250,16 +293,17 @@ abstract class ModuleCore
*/
public function enable($forceAll = false)
{
// Retrieve all shops where the module is enabled
$list = $this->context->shop->getListOfID();
$sql = 'SELECT id_shop
FROM '._DB_PREFIX_.'module_shop
WHERE id_module = '.$this->id.'
'.((!$forceAll) ? 'AND id_shop IN('.implode(', ', $list).')' : '');
$sql = 'SELECT `id_shop` FROM `'._DB_PREFIX_.'module_shop` WHERE `id_module` = '.$this->id.' '.((!$forceAll) ? 'AND `id_shop` IN('.implode(', ', $list).')' : '');
// Store the results in an array
$items = array();
if ($results = Db::getInstance($sql)->executeS($sql))
foreach ($results as $row)
$items[] = $row['id_shop'];
// Enable module in the shop where it is not enabled yet
foreach ($list as $id)
if (!in_array($id, $items))
Db::getInstance()->autoExecute(_DB_PREFIX_.'module_shop', array(
@@ -280,9 +324,11 @@ abstract class ModuleCore
*/
public static function disableByName($name)
{
// If $name is not an array, we set it as an array
if (!is_array($name))
$name = array($name);
// Disable each module
foreach ($name as $k => $v)
Module::getInstanceByName($name)->disable();
@@ -296,9 +342,8 @@ abstract class ModuleCore
*/
public function disable($forceAll = false)
{
$sql = 'DELETE FROM '._DB_PREFIX_.'module_shop
WHERE id_module = '.$this->id.'
'.((!$forceAll) ? ' AND id_shop IN('.implode(', ', $this->context->shop->getListOfID()).')' : '');
// Disable module for all shops
$sql = 'DELETE FROM `'._DB_PREFIX_.'module_shop` WHERE `id_module` = '.$this->id.' '.((!$forceAll) ? ' AND `id_shop` IN('.implode(', ', $this->context->shop->getListOfID()).')' : '');
Db::getInstance()->execute($sql);
}
@@ -343,6 +388,7 @@ abstract class ModuleCore
*/
public function registerHook($hook_name, $shopList = null)
{
// Check hook name validation and if module is installed
if (!Validate::isHookName($hook_name))
die(Tools::displayError());
if (!isset($this->id) OR !is_numeric($this->id))
@@ -354,10 +400,10 @@ abstract class ModuleCore
$hook_name = Hook::$_hook_alias[$hook_name];
// Get hook id
$sql = 'SELECT `id_hook`
FROM `'._DB_PREFIX_.'hook`
WHERE `name` = \''.pSQL($hook_name).'\'';
$sql = 'SELECT `id_hook` FROM `'._DB_PREFIX_.'hook` WHERE `name` = \''.pSQL($hook_name).'\'';
$hookID = Db::getInstance()->getValue($sql);
// If hook does not exist, we create it
if (!$hookID)
{
$newHook = new Hook();
@@ -369,6 +415,7 @@ abstract class ModuleCore
return false;
}
// If shop lists is null, we fill it with all shops
if (is_null($shopList))
$shopList = Shop::getShops(true, null, true);
@@ -377,34 +424,33 @@ abstract class ModuleCore
{
// Check if already register
$sql = 'SELECT hm.`id_module`
FROM `'._DB_PREFIX_.'hook_module` hm, `'._DB_PREFIX_.'hook` h
WHERE hm.`id_module` = '.(int)($this->id).'
AND h.id_hook = '.$hookID.'
AND h.`id_hook` = hm.`id_hook`
AND id_shop = '.$shopID;
FROM `'._DB_PREFIX_.'hook_module` hm, `'._DB_PREFIX_.'hook` h
WHERE hm.`id_module` = '.(int)($this->id).' AND h.`id_hook` = '.$hookID.'
AND h.`id_hook` = hm.`id_hook` AND `id_shop` = '.(int)($shopID);
if (Db::getInstance()->getRow($sql))
continue;
// Get module position in hook
$sql = 'SELECT MAX(`position`) AS position
FROM `'._DB_PREFIX_.'hook_module`
WHERE `id_hook` = '.$hookID
.' AND id_shop = '.$shopID;
FROM `'._DB_PREFIX_.'hook_module`
WHERE `id_hook` = '.(int)$hookID.' AND `id_shop` = '.(int)$shopID;
if (!$position = Db::getInstance()->getValue($sql))
$position = 0;
// Register module in hook
$result = Db::getInstance()->autoExecute(_DB_PREFIX_.'hook_module', array(
'id_module' => $this->id,
'id_hook' => $hookID,
'id_shop' => $shopID,
'position' => $position + 1,
'id_module' => (int)$this->id,
'id_hook' => (int)$hookID,
'id_shop' => (int)$shopID,
'position' => (int)($position + 1),
), 'INSERT');
if (!$result)
$return &= false;
}
// Clean modules position
$this->cleanPositions($hookID, $shopList);
return $return;
}
@@ -425,20 +471,24 @@ abstract class ModuleCore
if (isset(Hook::$_hook_alias[$hook_id]))
$hook_id = Hook::$_hook_alias[$hook_id];
// Unregister module on hook by name
$sql = 'SELECT `id_hook`
FROM `'._DB_PREFIX_.'hook`
WHERE `name` = \''.pSQL($hook_id).'\'';
FROM `'._DB_PREFIX_.'hook`
WHERE `name` = \''.pSQL($hook_id).'\'';
$hook_id = Db::getInstance()->getValue($sql);
if (!$hook_id)
return false;
}
// Unregister module on hook by id
$sql = 'DELETE FROM `'._DB_PREFIX_.'hook_module`
WHERE `id_module` = '.(int)$this->id.'
AND `id_hook` = '.(int)$hook_id
.(($shopList) ? ' AND id_shop IN('.implode(', ', $shopList).')' : '');
WHERE `id_module` = '.(int)$this->id.' AND `id_hook` = '.(int)$hook_id
.(($shopList) ? ' AND `id_shop` IN('.implode(', ', $shopList).')' : '');
$result = Db::getInstance()->execute($sql);
// Clean modules position
$this->cleanPositions($hook_id, $shopList);
return $result;
}
@@ -451,11 +501,9 @@ abstract class ModuleCore
*/
public function unregisterExceptions($hook_id, $shopList = null)
{
$sql = 'DELETE
FROM `'._DB_PREFIX_.'hook_module_exceptions`
WHERE `id_module` = '.(int)$this->id.'
AND `id_hook` = '.(int)$hook_id
.(($shopList) ? ' AND id_shop IN('.implode(', ', $shopList).')' : '');
$sql = 'DELETE FROM `'._DB_PREFIX_.'hook_module_exceptions`
WHERE `id_module` = '.(int)$this->id.' AND `id_hook` = '.(int)$hook_id
.(($shopList) ? ' AND `id_shop` IN('.implode(', ', $shopList).')' : '');
return Db::getInstance()->execute($sql);
}
@@ -469,9 +517,11 @@ abstract class ModuleCore
*/
public function registerExceptions($id_hook, $excepts, $shopList = null)
{
// If shop lists is null, we fill it with all shops
if (is_null($shopList))
Context::getContext()->shop->getListOfID();
// Save modules exception for each shop
foreach ($shopList as $shopID)
{
foreach ($excepts AS $except)
@@ -480,10 +530,10 @@ abstract class ModuleCore
continue;
$result = Db::getInstance()->autoExecute(_DB_PREFIX_.'hook_module_exceptions', array(
'id_module' => $this->id,
'id_hook' => (int)$id_hook,
'id_shop' => $shopID,
'file_name' => pSQL($except),
'id_module' => (int)$this->id,
'id_hook' => (int)$id_hook,
'id_shop' => (int)$shopID,
'file_name' => pSQL($except),
), 'INSERT');
if (!$result)
return false;
@@ -492,6 +542,13 @@ abstract class ModuleCore
return true;
}
/**
* Edit exceptions for module->Hook
*
* @param int $hookID Hook id
* @param array $excepts List of shopID and file name
* @return boolean result
*/
public function editExceptions($hookID, $excepts)
{
$result = true;
@@ -713,10 +770,10 @@ abstract class ModuleCore
$moduleList[] = $item;
if (!$xml_exist OR $needNewConfigFile)
{
self::$_generateConfigXmlMode = true;
self::$_generate_config_xml_mode = true;
$tmpModule = new $module;
$tmpModule->_generateConfigXml();
self::$_generateConfigXmlMode = false;
self::$_generate_config_xml_mode = false;
}
}
else
@@ -985,7 +1042,7 @@ abstract class ModuleCore
*/
public function l($string, $specific = false, $id_lang = null)
{
if (self::$_generateConfigXmlMode)
if (self::$_generate_config_xml_mode)
return $string;
global $_MODULES, $_MODULE;
@@ -1318,5 +1375,7 @@ abstract class ModuleCore
{
return Db::getInstance()->getValue('SELECT id_module FROM `'._DB_PREFIX_.'module` WHERE name = "'.pSQL($name).'"');
}
public function getErrors() { return $this->_errors; }
}