* @copyright PrestaShop
* @license http://www.opensource.org/licenses/osl-3.0.php Open-source licence 3.0
* @version 1.3
*
*/
abstract class Module
{
/** @var integer Module ID */
public $id = NULL;
/** @var float Version */
public $version;
/** @var string Unique name */
public $name;
/** @var string Human name */
public $displayName;
/** @var string A little description of the module */
public $description;
/** @var string Admin tab correponding to the module */
public $tab = NULL;
/** @var boolean Status */
public $active = false;
/** @var array current language translations */
private $_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;
public $_errors = false;
protected $table = 'module';
protected $identifier = 'id_module';
static public $_db;
/**
* Constructor
*
* @param string $name Module unique name
*/
private static $modulesCache = NULL;
public function __construct($name = NULL)
{
global $cookie;
if ($this->name == NULL)
$this->name = $this->id;
if ($this->name != NULL)
{
if (self::$modulesCache == NULL AND !is_array(self::$modulesCache))
{
self::$modulesCache = array();
$result = Db::getInstance()->ExecuteS('SELECT * FROM `'.pSQL(_DB_PREFIX_.$this->table).'`');
foreach ($result as $row)
self::$modulesCache[$row['name']] = $row;
}
if (!isset(self::$modulesCache[$this->name]))
return false;
$this->active = true;
$this->id = self::$modulesCache[$this->name]['id_module'];
foreach (self::$modulesCache[$this->name] AS $key => $value)
if (key_exists($key, $this))
$this->{$key} = $value;
$this->_path = __PS_BASE_URI__.'modules/'.$this->name.'/';
}
}
/**
* Insert module into datable
*/
public function install()
{
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)
return false;
$this->id = Db::getInstance()->Insert_ID();
return true;
}
/**
* Delete module from datable
*
* @return boolean result
*/
public function uninstall()
{
if (!Validate::isUnsignedId($this->id))
return false;
$result = Db::getInstance()->ExecuteS('
SELECT `id_hook`
FROM `'._DB_PREFIX_.'hook_module` hm
WHERE `id_module` = '.intval($this->id));
foreach ($result AS $row)
{
Db::getInstance()->Execute('
DELETE FROM `'._DB_PREFIX_.'hook_module`
WHERE `id_module` = '.intval($this->id).'
AND `id_hook` = '.intval($row['id_hook']));
$this->cleanPositions($row['id_hook']);
}
return Db::getInstance()->Execute('
DELETE FROM `'._DB_PREFIX_.'module`
WHERE `id_module` = '.intval($this->id));
}
/**
* Connect module to a hook
*
* @param string $hook_name Hook name
* @return boolean result
*/
public function registerHook($hook_name)
{
if (!Validate::isHookName($hook_name))
die(Tools::displayError());
if (!isset($this->id) OR !is_numeric($this->id))
return false;
// Check if already register
$result = Db::getInstance()->getRow('
SELECT hm.`id_module` FROM `'._DB_PREFIX_.'hook_module` hm, `'._DB_PREFIX_.'hook` h
WHERE hm.`id_module` = '.intval($this->id).'
AND h.`name` = \''.pSQL($hook_name).'\'
AND h.`id_hook` = hm.`id_hook`');
if ($result)
return true;
// Get hook id
$result = Db::getInstance()->getRow('
SELECT `id_hook`
FROM `'._DB_PREFIX_.'hook`
WHERE `name` = \''.pSQL($hook_name).'\'');
if (!isset($result['id_hook']))
return false;
// Get module position in hook
$result2 = Db::getInstance()->getRow('
SELECT MAX(`position`) AS position
FROM `'._DB_PREFIX_.'hook_module`
WHERE `id_hook` = '.intval($result['id_hook']));
if (!$result2)
return false;
// Register module in hook
$return = Db::getInstance()->Execute('
INSERT INTO `'._DB_PREFIX_.'hook_module` (`id_module`, `id_hook`, `position`)
VALUES ('.intval($this->id).', '.intval($result['id_hook']).', '.intval($result2['position'] + 1).')');
$this->cleanPositions(intval($result['id_hook']));
return $return;
}
public function editHook($hook_name, $id_hook_old, $id_hook){
if (!Validate::isHookName($hook_name))
die(Tools::displayError());
if (!isset($this->id) OR !is_numeric($this->id))
return false;
// Check if already register
$result = Db::getInstance()->getRow('
SELECT hm.`id_module` FROM `'._DB_PREFIX_.'hook_module` hm, `'._DB_PREFIX_.'hook` h
WHERE hm.`id_module` = '.intval($this->id).'
AND h.`name` = \''.pSQL($hook_name).'\'
AND h.`id_hook` = hm.`id_hook`');
if ($result)
return true;
// Get hook id
$result = Db::getInstance()->getRow('
SELECT `id_hook`
FROM `'._DB_PREFIX_.'hook`
WHERE `name` = \''.pSQL($hook_name).'\'');
if (!isset($result['id_hook']))
return false;
// Get module position in hook
$result2 = Db::getInstance()->getRow('
SELECT MAX(`position`) AS position
FROM `'._DB_PREFIX_.'hook_module`
WHERE `id_hook` = '.intval($result['id_hook']));
if (!$result2)
return false;
// Register module in hook
$return = Db::getInstance()->Execute('
UPDATE `'._DB_PREFIX_.'hook_module` SET `id_hook` = '.intval($id_hook).'
WHERE `id_module` = '.intval($this->id).' AND `id_hook` = '.intval($id_hook_old).'');
$this->cleanPositions(intval($id_hook));
return $return;
}
/**
* Display flags in forms for translations
*
* @param array $languages All languages available
* @param integer $defaultLanguage Default language id
* @param string $ids Multilingual div ids in form
* @param string $id Current div id]
* #param boolean $return define the return way : false for a display, true for a return
*/
public function displayFlags($languages, $defaultLanguage, $ids, $id, $return = false)
{
if (sizeof($languages) == 1)
return false;
$defaultIso = Language::getIsoById($defaultLanguage);
$output = '
'.$this->l('Choose language:').'
';
foreach ($languages as $language)
$output .= '
';
$output .= '
';
if ($return)
return $output;
echo $output;
}
/**
* Unregister module from hook
*
* @param int $id_hook Hook id
* @return boolean result
*/
public function unregisterHook($hook_id)
{
return Db::getInstance()->Execute('
DELETE
FROM `'._DB_PREFIX_.'hook_module`
WHERE `id_module` = '.intval($this->id).'
AND `id_hook` = '.intval($hook_id));
}
/**
* Unregister exceptions linked to module
*
* @param int $id_hook Hook id
* @return boolean result
*/
public function unregisterExceptions($hook_id)
{
return Db::getInstance()->Execute('
DELETE
FROM `'._DB_PREFIX_.'hook_module_exceptions`
WHERE `id_module` = '.intval($this->id).'
AND `id_hook` = '.intval($hook_id));
}
/**
* Add exceptions for module->Hook
*
* @param int $id_hook Hook id
* @param array $excepts List of file name
* @return boolean result
*/
public function registerExceptions($id_hook, $excepts)
{
foreach ($excepts AS $except)
{
if (!empty($except))
{
$result = Db::getInstance()->Execute('
INSERT INTO `'._DB_PREFIX_.'hook_module_exceptions` (`id_module`, `id_hook`, `file_name`)
VALUES ('.intval($this->id).', '.intval($id_hook).', \''.pSQL(strval($except)).'\')');
if (!$result)
return false;
}
}
return true;
}
public function editExceptions($id_hook, $excepts){
// Cleaning...
$_sql = 'DELETE FROM `'._DB_PREFIX_.'hook_module_exceptions` WHERE `id_module` = '.intval($this->id).' AND `id_hook` ='.intval($id_hook);
Db::getInstance()->Execute($_sql);
//return $_sql;
return $this->registerExceptions($id_hook, $excepts);
}
/**
* Return an instance of the specified module
*
* @param string $moduleName Module name
* @return Module instance
*/
static public function getInstanceByName($moduleName)
{
if (!Tools::file_exists_cache(_PS_MODULE_DIR_.$moduleName.'/'.$moduleName.'.php'))
return false;
include_once(_PS_MODULE_DIR_.$moduleName.'/'.$moduleName.'.php');
if (!class_exists($moduleName, false))
return false;
return (new $moduleName);
}
/**
* Return an instance of the specified module
*
* @param integer $id_module Module ID
* @return Module instance
*/
static public function getInstanceById($id_module)
{
$result = Db::getInstance()->GetRow('
SELECT `name`
FROM `'._DB_PREFIX_.'module`
WHERE `id_module` = '.intval($id_module));
return ($result ? Module::getInstanceByName($result['name']) : false);
}
/**
* Return available modules
*
* @return array Modules
*/
public static function getModulesOnDisk()
{
$moduleList = array();
$errors = array();
$modules_dir = self::getModulesDirOnDisk();
foreach ($modules_dir AS $module)
{
$file = trim(file_get_contents(_PS_MODULE_DIR_.'/'.$module.'/'.$module.'.php'));
if (substr($file, 0, 5) == '')
$file = substr($file, 0, -2);
if (class_exists($module, false) OR eval($file) !== false)
$moduleList[] = new $module;
else
$errors[] = $module;
}
if (sizeof($errors))
{
echo ''.Tools::displayError('Parse error(s) in module(s)').'
';
foreach ($errors AS $error)
echo '- '.$error.'
';
echo '
';
}
return $moduleList;
}
public static function getModulesDirOnDisk()
{
$moduleList = array();
$modules = scandir(_PS_MODULE_DIR_);
foreach ($modules AS $name)
{
if (Tools::file_exists_cache($moduleFile = _PS_MODULE_DIR_.'/'.$name.'/'.$name.'.php'))
{
if (!Validate::isModuleName($name))
die(Tools::displayError().' (Module '.$name.')');
$moduleList[] = $name;
}
}
return $moduleList;
}
/**
* Return installed modules
*
* @param int $position Take only positionnables modules
* @return array Modules
*/
public static function getModulesInstalled($position = 0)
{
return Db::getInstance()->ExecuteS('
SELECT *
FROM `'._DB_PREFIX_.'module` m
'.($position ? '
LEFT JOIN `'._DB_PREFIX_.'hook_module` hm ON m.`id_module` = hm.`id_module`
LEFT JOIN `'._DB_PREFIX_.'hook` k ON hm.`id_hook` = k.`id_hook`
WHERE k.`position` = 1' : ''));
}
/*
* Execute modules for specified hook
*
* @param string $hook_name Hook Name
* @param array $hookArgs Parameters for the functions
* @return string modules output
*/
public static function hookExec($hook_name, $hookArgs = array(), $id_module = NULL)
{
if ((!empty($id_module) AND !Validate::isUnsignedId($id_module)) OR !Validate::isHookName($hook_name))
die(Tools::displayError());
global $cart, $cookie;
$altern = 0;
if (!isset($hookArgs['cookie']) OR !$hookArgs['cookie'])
$hookArgs['cookie'] = $cookie;
if (!isset($hookArgs['cart']) OR !$hookArgs['cart'])
$hookArgs['cart'] = $cart;
$result = Db::getInstance()->ExecuteS('
SELECT h.`id_hook`, m.`name`, hm.`position`
FROM `'._DB_PREFIX_.'module` m
LEFT JOIN `'._DB_PREFIX_.'hook_module` hm ON hm.`id_module` = m.`id_module`
LEFT JOIN `'._DB_PREFIX_.'hook` h ON hm.`id_hook` = h.`id_hook`
WHERE h.`name` = \''.pSQL($hook_name).'\'
AND m.`active` = 1
'.($id_module ? 'AND m.`id_module` = '.intval($id_module) : '').'
ORDER BY hm.`position`, m.`name` DESC');
if (!$result)
return false;
$output = '';
foreach ($result AS $k => $module)
{
$moduleInstance = Module::getInstanceByName($module['name']);
if (!$moduleInstance)
continue;
$exceptions = $moduleInstance->getExceptions(intval($module['id_hook']), intval($moduleInstance->id));
$fileindex = basename($_SERVER['PHP_SELF']);
$show = true;
if (!empty($exceptions) AND is_array($exceptions))
foreach ($exceptions as $exception)
if ($fileindex == $exception['file_name'])
$show = false;
if (is_callable(array($moduleInstance, 'hook'.$hook_name)) AND $show)
{
$hookArgs['altern'] = ++$altern;
$output .= call_user_func(array($moduleInstance, 'hook'.$hook_name), $hookArgs);
}
}
return $output;
}
public static function hookExecPayment()
{
global $cart, $cookie;
$hookArgs = array('cookie' => $cookie, 'cart' => $cart);
$id_customer = intval($cookie->id_customer);
$billing = new Address(intval($cart->id_address_invoice));
$output = '';
$result = Db::getInstance()->ExecuteS('
SELECT DISTINCT h.`id_hook`, m.`name`, hm.`position`
FROM `'._DB_PREFIX_.'module_country` mc
LEFT JOIN `'._DB_PREFIX_.'module` m ON m.`id_module` = mc.`id_module`
INNER JOIN `'._DB_PREFIX_.'module_group` mg ON (m.`id_module` = mg.`id_module`)
INNER JOIN `'._DB_PREFIX_.'customer_group` cg on (cg.`id_group` = mg.`id_group` AND cg.`id_customer` = '.intval($id_customer).')
LEFT JOIN `'._DB_PREFIX_.'hook_module` hm ON hm.`id_module` = m.`id_module`
LEFT JOIN `'._DB_PREFIX_.'hook` h ON hm.`id_hook` = h.`id_hook`
WHERE h.`name` = \'payment\'
AND mc.id_country = '.intval($billing->id_country).'
AND m.`active` = 1
ORDER BY hm.`position`, m.`name` DESC');
if ($result)
foreach ($result AS $k => $module)
if (($moduleInstance = Module::getInstanceByName($module['name'])) AND is_callable(array($moduleInstance, 'hookpayment')))
if (!$moduleInstance->currencies OR ($moduleInstance->currencies AND sizeof(Currency::checkPaymentCurrencies($moduleInstance->id))))
$output .= call_user_func(array($moduleInstance, 'hookpayment'), $hookArgs);
return $output;
}
/*
* Get translation for a given module text
*
* @param string $string String to translate
* @return string Translation
*/
public function l($string, $specific = false)
{
global $_MODULES, $_MODULE, $cookie;
$id_lang = (!isset($cookie) OR !is_object($cookie)) ? intval(Configuration::get('PS_LANG_DEFAULT')) : intval($cookie->id_lang);
$file = _PS_MODULE_DIR_.$this->name.'/'.Language::getIsoById($id_lang).'.php';
if (Tools::file_exists_cache($file) AND include_once($file))
$_MODULES = !empty($_MODULES) ? array_merge($_MODULES, $_MODULE) : $_MODULE;
if (!is_array($_MODULES))
return (str_replace('"', '"', $string));
$source = Tools::strtolower($specific ? $specific : get_class($this));
$string2 = str_replace('\'', '\\\'', $string);
$currentKey = '<{'.$this->name.'}'._THEME_NAME_.'>'.$source.'_'.md5($string2);
$defaultKey = '<{'.$this->name.'}prestashop>'.$source.'_'.md5($string2);
if (key_exists($currentKey, $_MODULES))
$ret = stripslashes($_MODULES[$currentKey]);
elseif (key_exists($defaultKey, $_MODULES))
$ret = stripslashes($_MODULES[$defaultKey]);
else
$ret = $string;
return str_replace('"', '"', $ret);
}
/*
* Reposition module
*
* @param boolean $id_hook Hook ID
* @param boolean $way Up (1) or Down (0)
* @param intger $position
*/
public function updatePosition($id_hook, $way, $position = NULL)
{
if (!$res = Db::getInstance()->ExecuteS('
SELECT hm.`id_module`, hm.`position`, hm.`id_hook`
FROM `'._DB_PREFIX_.'hook_module` hm
WHERE hm.`id_hook` = '.intval($id_hook).'
ORDER BY hm.`position` '.(intval($way) ? 'ASC' : 'DESC')))
return false;
foreach ($res AS $key => $values)
if (intval($values[$this->identifier]) == intval($this->id))
{
$k = $key ;
break ;
}
if (!isset($k) OR !isset($res[$k]) OR !isset($res[$k + 1]))
return false;
$from = $res[$k];
$to = $res[$k + 1];
if (isset($position) and !empty($position))
$to['position'] = intval($position);
return (Db::getInstance()->Execute('
UPDATE `'._DB_PREFIX_.'hook_module`
SET `position`= position '.($way ? '-1' : '+1').'
WHERE position between '.intval(min(array($from['position'], $to['position']))) .' AND '.intval(max(array($from['position'], $to['position']))).'
AND `id_hook`='.intval($from['id_hook']))
AND
Db::getInstance()->Execute('
UPDATE `'._DB_PREFIX_.'hook_module`
SET `position`='.intval($to['position']).'
WHERE `'.pSQL($this->identifier).'` = '.intval($from[$this->identifier]).' AND `id_hook`='.intval($to['id_hook']))
);
}
/*
* Reorder modules position
*
* @param boolean $id_hook Hook ID
*/
public function cleanPositions($id_hook)
{
$result = Db::getInstance()->ExecuteS('
SELECT `id_module`
FROM `'._DB_PREFIX_.'hook_module`
WHERE `id_hook` = '.intval($id_hook).'
ORDER BY `position`');
$sizeof = sizeof($result);
for ($i = 0; $i < $sizeof; ++$i)
Db::getInstance()->Execute('
UPDATE `'._DB_PREFIX_.'hook_module`
SET `position` = '.intval($i + 1).'
WHERE `id_hook` = '.intval($id_hook).'
AND `id_module` = '.intval($result[$i]['id_module']));
return true;
}
/*
* Return module position for a given hook
*
* @param boolean $id_hook Hook ID
* @return integer position
*/
public function getPosition($id_hook)
{
$result = Db::getInstance()->getRow('
SELECT `position`
FROM `'._DB_PREFIX_.'hook_module`
WHERE `id_hook` = '.intval($id_hook).'
AND `id_module` = '.intval($this->id));
return $result['position'];
}
public function displayError($error)
{
$output = '
'.$error.'
';
$this->error = true;
return $output;
}
public function displayConfirmation($string)
{
$output = '
'.$string.'
';
return $output;
}
/*
* Return exceptions for module in hook
*
* @param int $id_hook Hook ID
* @return array Exceptions
*/
private static $exceptionsCache = NULL;
public function getExceptions($id_hook)
{
if (self::$exceptionsCache == NULL AND !is_array(self::$exceptionsCache))
{
self::$exceptionsCache = array();
$result = Db::getInstance()->ExecuteS('
SELECT CONCAT(id_hook, \'-\', id_module) as `key`, `file_name` as value
FROM `'._DB_PREFIX_.'hook_module_exceptions`');
foreach ($result as $row)
{
if (!array_key_exists($row['key'], self::$exceptionsCache))
self::$exceptionsCache[$row['key']] = array();
self::$exceptionsCache[$row['key']][] = array('file_name' => $row['value']);
}
}
return (array_key_exists(intval($id_hook).'-'.intval($this->id), self::$exceptionsCache) ? self::$exceptionsCache[intval($id_hook).'-'.intval($this->id)] : array());
}
public static function display($file, $template)
{
global $smarty;
$previousTemplate = $smarty->currentTemplate;
$smarty->currentTemplate = substr(basename($template), 0, -4);
$smarty->assign('module_dir', __PS_BASE_URI__.'modules/'.basename($file, '.php').'/');
if (Tools::file_exists_cache(_PS_THEME_DIR_.'modules/'.basename($file, '.php').'/'.$template))
{
$smarty->assign('module_template_dir', _THEME_DIR_.'modules/'.basename($file, '.php').'/');
$result = $smarty->fetch(_PS_THEME_DIR_.'modules/'.basename($file, '.php').'/'.$template);
}
elseif (Tools::file_exists_cache(dirname($file).'/'.$template))
{
$smarty->assign('module_template_dir', __PS_BASE_URI__.'modules/'.basename($file, '.php').'/');
$result = $smarty->fetch(dirname($file).'/'.$template);
}
else
$result = Tools::displayError('No template found');
$smarty->currentTemplate = $previousTemplate;
return $result;
}
public static function isInstalled($moduleName)
{
Db::getInstance()->Execute('SELECT `id_module` FROM `'._DB_PREFIX_.'module` WHERE `name` = \''.pSQL($moduleName).'\'');
return (bool)Db::getInstance()->NumRows();
}
public function isRegisteredInHook($hook)
{
if (!$this->id)
return false;
return Db::getInstance()->getValue('
SELECT COUNT(*)
FROM `'._DB_PREFIX_.'hook_module` hm
LEFT JOIN `'._DB_PREFIX_.'hook` h ON (h.`id_hook` = hm.`id_hook`)
WHERE h.`name` = \''.pSQL($hook).'\'
AND hm.`id_module` = '.intval($this->id)
);
}
}