PageRenderTime 58ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/classes/module/Module.php

https://bitbucket.org/enurkov/prestashop
PHP | 1997 lines | 1273 code | 241 blank | 483 comment | 217 complexity | 7465b5d60b24cdedb7ad0dd4d300b61e MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. * 2007-2012 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. * @author PrestaShop SA <contact@prestashop.com>
  22. * @copyright 2007-2012 PrestaShop SA
  23. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  24. * International Registered Trademark & Property of PrestaShop SA
  25. */
  26. abstract class ModuleCore
  27. {
  28. /** @var integer Module ID */
  29. public $id = null;
  30. /** @var float Version */
  31. public $version;
  32. public $database_version;
  33. /**
  34. * @since 1.5.0.1
  35. * @var string Registered Version in database
  36. */
  37. public $registered_version;
  38. /** @var array filled with known compliant PS versions */
  39. public $ps_versions_compliancy = array('min' => '1.4', 'max' => '1.6');
  40. /** @var array filled with modules needed for install */
  41. public $dependencies = array();
  42. /** @var string Unique name */
  43. public $name;
  44. /** @var string Human name */
  45. public $displayName;
  46. /** @var string A little description of the module */
  47. public $description;
  48. /** @var string author of the module */
  49. public $author;
  50. /** @var int need_instance */
  51. public $need_instance = 1;
  52. /** @var string Admin tab correponding to the module */
  53. public $tab = null;
  54. /** @var boolean Status */
  55. public $active = false;
  56. /** @var string Fill it if the module is installed but not yet set up */
  57. public $warning;
  58. /** @var array to store the limited country */
  59. public $limited_countries = array();
  60. /** @var array used by AdminTab to determine which lang file to use (admin.php or module lang file) */
  61. public static $classInModule = array();
  62. /** @var array current language translations */
  63. protected $_lang = array();
  64. /** @var string Module web path (eg. '/shop/modules/modulename/') */
  65. protected $_path = null;
  66. /**
  67. * @since 1.5.0.1
  68. * @var string Module local path (eg. '/home/prestashop/modules/modulename/')
  69. */
  70. protected $local_path = null;
  71. /** @var protected array filled with module errors */
  72. protected $_errors = array();
  73. /** @var protected array filled with module success */
  74. protected $_confirmations = array();
  75. /** @var protected string main table used for modules installed */
  76. protected $table = 'module';
  77. /** @var protected string identifier of the main table */
  78. protected $identifier = 'id_module';
  79. /** @var protected array cache filled with modules informations */
  80. protected static $modules_cache;
  81. /** @var protected array cache filled with modules instances */
  82. protected static $_INSTANCE = array();
  83. /** @var protected boolean filled with config xml generation mode */
  84. protected static $_generate_config_xml_mode = false;
  85. /** @var protected array filled with cache translations */
  86. protected static $l_cache = array();
  87. /** @var protected array filled with cache permissions (modules / employee profiles) */
  88. protected static $cache_permissions = array();
  89. /** @var Context */
  90. protected $context;
  91. /** @var Smarty_Data */
  92. protected $smarty;
  93. const CACHE_FILE_MODULES_LIST = '/config/xml/modules_list.xml';
  94. const CACHE_FILE_DEFAULT_COUNTRY_MODULES_LIST = '/config/xml/default_country_modules_list.xml';
  95. const CACHE_FILE_CUSTOMER_MODULES_LIST = '/config/xml/customer_modules_list.xml';
  96. const CACHE_FILE_MUST_HAVE_MODULES_LIST = '/config/xml/must_have_modules_list.xml';
  97. /**
  98. * Constructor
  99. *
  100. * @param string $name Module unique name
  101. * @param Context $context
  102. */
  103. public function __construct($name = null, Context $context = null)
  104. {
  105. // Load context and smarty
  106. $this->context = $context ? $context : Context::getContext();
  107. $this->smarty = $this->context->smarty->createData($this->context->smarty);
  108. // If the module has no name we gave him its id as name
  109. if ($this->name == null)
  110. $this->name = $this->id;
  111. // If the module has the name we load the corresponding data from the cache
  112. if ($this->name != null)
  113. {
  114. // If cache is not generated, we generate it
  115. if (self::$modules_cache == null && !is_array(self::$modules_cache))
  116. {
  117. // Join clause is done to check if the module is activated in current shop context
  118. $sql_limit_shop = 'SELECT COUNT(*) FROM `'._DB_PREFIX_.'module_shop` ms WHERE m.`id_module` = ms.`id_module` AND ms.`id_shop` = '.(int)Context::getContext()->shop->id;
  119. $sql = 'SELECT m.`id_module`, m.`name`, ('.$sql_limit_shop.') as total FROM `'._DB_PREFIX_.'module` m';
  120. // Result is cached
  121. self::$modules_cache = array();
  122. $result = Db::getInstance()->executeS($sql);
  123. foreach ($result as $row)
  124. {
  125. self::$modules_cache[$row['name']] = $row;
  126. self::$modules_cache[$row['name']]['active'] = ($row['total'] > 0) ? 1 : 0;
  127. }
  128. }
  129. // We load configuration from the cache
  130. if (isset(self::$modules_cache[$this->name]))
  131. {
  132. if (isset(self::$modules_cache[$this->name]['id_module']))
  133. $this->id = self::$modules_cache[$this->name]['id_module'];
  134. foreach (self::$modules_cache[$this->name] as $key => $value)
  135. if (key_exists($key, $this))
  136. $this->{$key} = $value;
  137. $this->_path = __PS_BASE_URI__.'modules/'.$this->name.'/';
  138. }
  139. $this->local_path = _PS_MODULE_DIR_.$this->name.'/';
  140. }
  141. $this->database_version = $this->version;
  142. }
  143. /**
  144. * Insert module into datable
  145. */
  146. public function install()
  147. {
  148. // Check module name validation
  149. if (!Validate::isModuleName($this->name))
  150. die(Tools::displayError());
  151. // Check PS version compliancy
  152. if (version_compare(_PS_VERSION_, $this->ps_versions_compliancy['min']) < 0 || version_compare(_PS_VERSION_, $this->ps_versions_compliancy['max']) >= 0)
  153. {
  154. $this->_errors[] = $this->l('The version of your module is not compliant with your PrestaShop version.');
  155. return false;
  156. }
  157. // Check module dependencies
  158. if (count($this->dependencies) > 0)
  159. foreach ($this->dependencies as $dependency)
  160. if (!Db::getInstance()->getRow('SELECT `id_module` FROM `'._DB_PREFIX_.'module` WHERE `name` = \''.pSQL($dependency).'\''))
  161. {
  162. $error = $this->l('Before installing this module, you have to installed these/this module(s) first :').'<br />';
  163. foreach ($this->dependencies as $d)
  164. $error .= '- '.$d.'<br />';
  165. $this->_errors[] = $error;
  166. return false;
  167. }
  168. // Check if module is installed
  169. $result = Db::getInstance()->getRow('SELECT `id_module` FROM `'._DB_PREFIX_.'module` WHERE `name` = \''.pSQL($this->name).'\'');
  170. if ($result)
  171. {
  172. $this->_errors[] = $this->l('This module has already been installed.');
  173. return false;
  174. }
  175. // Install overrides
  176. try {
  177. $this->installOverrides();
  178. } catch (Exception $e) {
  179. $this->_errors[] = sprintf(Tools::displayError('Unable to install override: %s'), $e->getMessage());
  180. //$this->uninstallOverrides(); remove this line because if module a install an override, then module b install same override, this line will remove override of module a (if you find a bug related to this line please don't forget what i say before)
  181. return false;
  182. }
  183. // Install module and retrieve the installation id
  184. $result = Db::getInstance()->insert($this->table, array('name' => $this->name, 'active' => 1, 'version' => $this->version));
  185. if (!$result)
  186. {
  187. $this->_errors[] = $this->l('Technical error : PrestaShop could not installed this module.');
  188. return false;
  189. }
  190. $this->id = Db::getInstance()->Insert_ID();
  191. Cache::clean('Module::isInstalled'.$this->name);
  192. // Enable the module for current shops in context
  193. $this->enable();
  194. // Permissions management
  195. Db::getInstance()->execute('
  196. INSERT INTO `'._DB_PREFIX_.'module_access` (`id_profile`, `id_module`, `view`, `configure`) (
  197. SELECT id_profile, '.(int)$this->id.', 1, 1
  198. FROM '._DB_PREFIX_.'access a
  199. WHERE id_tab = (
  200. SELECT `id_tab` FROM '._DB_PREFIX_.'tab
  201. WHERE class_name = \'AdminModules\' LIMIT 1)
  202. AND a.`view` = 1)');
  203. Db::getInstance()->execute('
  204. INSERT INTO `'._DB_PREFIX_.'module_access` (`id_profile`, `id_module`, `view`, `configure`) (
  205. SELECT id_profile, '.(int)$this->id.', 1, 0
  206. FROM '._DB_PREFIX_.'access a
  207. WHERE id_tab = (
  208. SELECT `id_tab` FROM '._DB_PREFIX_.'tab
  209. WHERE class_name = \'AdminModules\' LIMIT 1)
  210. AND a.`view` = 0)');
  211. // Adding Restrictions for client groups
  212. Group::addRestrictionsForModule($this->id, Shop::getShops(true, null, true));
  213. return true;
  214. }
  215. /**
  216. * Set errors, warning or success message of a module upgrade
  217. *
  218. * @param $upgrade_detail
  219. */
  220. protected function setUpgradeMessage($upgrade_detail)
  221. {
  222. // Store information if a module has been upgraded (memory optimization)
  223. if ($upgrade_detail['available_upgrade'])
  224. {
  225. if ($upgrade_detail['success'])
  226. {
  227. $this->_confirmations[] = $this->l('Current version: ').$this->version;
  228. $this->_confirmations[] = $upgrade_detail['number_upgraded'].' '.$this->l('file upgrade applied');
  229. }
  230. else
  231. {
  232. if (!$upgrade_detail['number_upgraded'])
  233. $this->_errors[] = $this->l('None upgrades have been applied');
  234. else
  235. {
  236. $this->_errors[] = $this->l('Upgraded from: ').$upgrade_detail['upgraded_from'].$this->l(' to ').
  237. $upgrade_detail['upgraded_to'];
  238. $this->_errors[] = $upgrade_detail['number_upgrade_left'].' '.$this->l('upgrade left');
  239. }
  240. $this->_errors[] = $this->l('To prevent any problem, this module has been turned off');
  241. }
  242. }
  243. }
  244. /**
  245. * Init the upgrade module
  246. *
  247. * @static
  248. * @param $module_name
  249. * @param $module_version
  250. * @return bool
  251. */
  252. public static function initUpgradeModule($module)
  253. {
  254. // Init cache upgrade details
  255. self::$modules_cache[$module->name]['upgrade'] = array(
  256. 'success' => false, // bool to know if upgrade succeed or not
  257. 'available_upgrade' => 0, // Number of available module before any upgrade
  258. 'number_upgraded' => 0, // Number of upgrade done
  259. 'number_upgrade_left' => 0,
  260. 'upgrade_file_left' => array(), // List of the upgrade file left
  261. 'version_fail' => 0, // Version of the upgrade failure
  262. 'upgraded_from' => 0, // Version number before upgrading anything
  263. 'upgraded_to' => 0, // Last upgrade applied
  264. );
  265. // Need Upgrade will check and load upgrade file to the moduleCache upgrade case detail
  266. $ret = $module->installed && Module::needUpgrade($module);
  267. return $ret;
  268. }
  269. /**
  270. * Run the upgrade for a given module name and version
  271. *
  272. * @return array
  273. */
  274. public function runUpgradeModule()
  275. {
  276. $upgrade = &self::$modules_cache[$this->name]['upgrade'];
  277. foreach ($upgrade['upgrade_file_left'] as $num => $file_detail)
  278. {
  279. // Default variable required in the included upgrade file need to be set by default there:
  280. // upgrade_version, success_upgrade
  281. $upgrade_result = false;
  282. include($file_detail['file']);
  283. // Call the upgrade function if defined
  284. if (function_exists($file_detail['upgrade_function']))
  285. $upgrade_result = $file_detail['upgrade_function']($this);
  286. $upgrade['success'] = $upgrade_result;
  287. // Set detail when an upgrade succeed or failed
  288. if ($upgrade_result)
  289. {
  290. $upgrade['number_upgraded'] += 1;
  291. $upgrade['upgraded_to'] = $file_detail['version'];
  292. unset($upgrade['upgrade_file_left'][$num]);
  293. }
  294. else
  295. {
  296. $upgrade['version_fail'] = $file_detail['version'];
  297. // If any errors, the module is disabled
  298. $this->disable();
  299. break;
  300. }
  301. }
  302. $upgrade['number_upgrade_left'] = count($upgrade['upgrade_file_left']);
  303. // Update module version in DB with the last succeed upgrade
  304. if ($upgrade['upgraded_to'])
  305. Module::upgradeModuleVersion($this->name, $upgrade['upgraded_to']);
  306. $this->setUpgradeMessage($upgrade);
  307. return $upgrade;
  308. }
  309. /**
  310. * Upgrade the registered version to a new one
  311. *
  312. * @static
  313. * @param $name
  314. * @param $version
  315. * @return bool
  316. */
  317. public static function upgradeModuleVersion($name, $version)
  318. {
  319. return Db::getInstance()->execute('
  320. UPDATE `'._DB_PREFIX_.'module` m
  321. SET m.version = \''.bqSQL($version).'\'
  322. WHERE m.name = \''.bqSQL($name).'\'');
  323. }
  324. /**
  325. * Check if a module need to be upgraded.
  326. * This method modify the module_cache adding an upgrade list file
  327. *
  328. * @static
  329. * @param $module_name
  330. * @param $module_version
  331. * @return bool
  332. */
  333. public static function needUpgrade($module)
  334. {
  335. self::$modules_cache[$module->name]['upgrade']['upgraded_from'] = $module->database_version;
  336. // Check the version of the module with the registered one and look if any upgrade file exist
  337. if (Tools::version_compare($module->version, $module->database_version, '>'))
  338. {
  339. $module = Module::getInstanceByName($module->name);
  340. if ($module instanceof Module)
  341. return $module->loadUpgradeVersionList($module->name, $module->version, $module->database_version);
  342. }
  343. return null;
  344. }
  345. /**
  346. * Load the available list of upgrade of a specified module
  347. * with an associated version
  348. *
  349. * @static
  350. * @param $module_name
  351. * @param $module_version
  352. * @param $registered_version
  353. * @return bool to know directly if any files have been found
  354. */
  355. protected static function loadUpgradeVersionList($module_name, $module_version, $registered_version)
  356. {
  357. $list = array();
  358. $upgrade_path = _PS_MODULE_DIR_.$module_name.'/upgrade/';
  359. // Check if folder exist and it could be read
  360. if (file_exists($upgrade_path) && ($files = scandir($upgrade_path)))
  361. {
  362. // Read each file name
  363. foreach ($files as $file)
  364. if (!in_array($file, array('.', '..', '.svn', 'index.php')))
  365. {
  366. $tab = explode('-', $file);
  367. $file_version = basename($tab[1], '.php');
  368. // Compare version, if minor than actual, we need to upgrade the module
  369. if (count($tab) == 2 &&
  370. (Tools::version_compare($file_version, $module_version, '<=') &&
  371. Tools::version_compare($file_version, $registered_version, '>')))
  372. {
  373. $list[] = array(
  374. 'file' => $upgrade_path.$file,
  375. 'version' => $file_version,
  376. 'upgrade_function' => 'upgrade_module_'.str_replace('.', '_', $file_version));
  377. }
  378. }
  379. }
  380. // No files upgrade, then upgrade succeed
  381. if (count($list) == 0)
  382. {
  383. self::$modules_cache[$module_name]['upgrade']['success'] = true;
  384. Module::upgradeModuleVersion($module_name, $module_version);
  385. }
  386. usort($list, 'ps_module_version_sort');
  387. // Set the list to module cache
  388. self::$modules_cache[$module_name]['upgrade']['upgrade_file_left'] = $list;
  389. self::$modules_cache[$module_name]['upgrade']['available_upgrade'] = count($list);
  390. return (bool)count($list);
  391. }
  392. /**
  393. * Return the status of the upgraded module
  394. *
  395. * @static
  396. * @param $module_name
  397. * @return bool
  398. */
  399. public static function getUpgradeStatus($module_name)
  400. {
  401. return (isset(self::$modules_cache[$module_name]) &&
  402. self::$modules_cache[$module_name]['upgrade']['success']);
  403. }
  404. /**
  405. * Delete module from datable
  406. *
  407. * @return boolean result
  408. */
  409. public function uninstall()
  410. {
  411. // Check module installation id validation
  412. if (!Validate::isUnsignedId($this->id))
  413. {
  414. $this->_errors[] = $this->l('The module is not installed.');
  415. return false;
  416. }
  417. // Uninstall overrides
  418. if (!$this->uninstallOverrides())
  419. return false;
  420. // Retrieve hooks used by the module
  421. $sql = 'SELECT `id_hook` FROM `'._DB_PREFIX_.'hook_module` WHERE `id_module` = '.(int)$this->id;
  422. $result = Db::getInstance()->executeS($sql);
  423. foreach ($result as $row)
  424. {
  425. $sql = 'DELETE FROM `'._DB_PREFIX_.'hook_module` WHERE `id_module` = '.(int)$this->id.' AND `id_hook` = '.(int)$row['id_hook'];
  426. Db::getInstance()->execute($sql);
  427. $this->cleanPositions($row['id_hook']);
  428. }
  429. // Disable the module for all shops
  430. $this->disable(true);
  431. // Delete permissions module access
  432. Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'module_access` WHERE `id_module` = '.(int)$this->id);
  433. // Remove restrictions for client groups
  434. Group::truncateRestrictionsByModule($this->id);
  435. // Uninstall the module
  436. if (Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'module` WHERE `id_module` = '.(int)$this->id))
  437. {
  438. Cache::clean('Module::isInstalled'.$this->name);
  439. return true;
  440. }
  441. return false;
  442. }
  443. /**
  444. * This function enable module $name. If an $name is an array,
  445. * this will enable all of them
  446. *
  447. * @param array|string $name
  448. * @return true if succeed
  449. * @since 1.4.1
  450. */
  451. public static function enableByName($name)
  452. {
  453. // If $name is not an array, we set it as an array
  454. if (!is_array($name))
  455. $name = array($name);
  456. $res = true;
  457. // Enable each module
  458. foreach ($name as $n)
  459. if (Validate::isModuleName($n))
  460. $res &= Module::getInstanceByName($n)->enable();
  461. return $res;
  462. }
  463. /**
  464. * Activate current module.
  465. *
  466. * @param bool $forceAll If true, enable module for all shop
  467. */
  468. public function enable($forceAll = false)
  469. {
  470. // Retrieve all shops where the module is enabled
  471. $list = Shop::getContextListShopID();
  472. $sql = 'SELECT `id_shop` FROM `'._DB_PREFIX_.'module_shop`
  473. WHERE `id_module` = '.$this->id.
  474. ((!$forceAll) ? ' AND `id_shop` IN('.implode(', ', $list).')' : '');
  475. // Store the results in an array
  476. $items = array();
  477. if ($results = Db::getInstance($sql)->executeS($sql))
  478. foreach ($results as $row)
  479. $items[] = $row['id_shop'];
  480. // Enable module in the shop where it is not enabled yet
  481. foreach ($list as $id)
  482. if (!in_array($id, $items))
  483. Db::getInstance()->insert('module_shop', array(
  484. 'id_module' => $this->id,
  485. 'id_shop' => $id,
  486. ));
  487. return true;
  488. }
  489. /**
  490. * This function disable module $name. If an $name is an array,
  491. * this will disable all of them
  492. *
  493. * @param array|string $name
  494. * @return true if succeed
  495. * @since 1.4.1
  496. */
  497. public static function disableByName($name)
  498. {
  499. // If $name is not an array, we set it as an array
  500. if (!is_array($name))
  501. $name = array($name);
  502. $res = true;
  503. // Disable each module
  504. foreach ($name as $n)
  505. if (Validate::isModuleName($n))
  506. $res &= Module::getInstanceByName($n)->disable();
  507. return $res;
  508. }
  509. /**
  510. * Desactivate current module.
  511. *
  512. * @param bool $forceAll If true, disable module for all shop
  513. */
  514. public function disable($forceAll = false)
  515. {
  516. // Disable module for all shops
  517. $sql = 'DELETE FROM `'._DB_PREFIX_.'module_shop` WHERE `id_module` = '.(int)$this->id.' '.((!$forceAll) ? ' AND `id_shop` IN('.implode(', ', Shop::getContextListShopID()).')' : '');
  518. Db::getInstance()->execute($sql);
  519. }
  520. /**
  521. * Display flags in forms for translations
  522. *
  523. * @param array $languages All languages available
  524. * @param integer $default_language Default language id
  525. * @param string $ids Multilingual div ids in form
  526. * @param string $id Current div id]
  527. * @param boolean $return define the return way : false for a display, true for a return
  528. * @param boolean $use_vars_instead_of_ids use an js vars instead of ids seperate by "¤"
  529. */
  530. public function displayFlags($languages, $default_language, $ids, $id, $return = false, $use_vars_instead_of_ids = false)
  531. {
  532. if (count($languages) == 1)
  533. return false;
  534. $output = '
  535. <div class="displayed_flag">
  536. <img src="../img/l/'.$default_language.'.jpg" class="pointer" id="language_current_'.$id.'" onclick="toggleLanguageFlags(this);" alt="" />
  537. </div>
  538. <div id="languages_'.$id.'" class="language_flags">
  539. '.$this->l('Choose language:').'<br /><br />';
  540. foreach ($languages as $language)
  541. if ($use_vars_instead_of_ids)
  542. $output .= '<img src="../img/l/'.(int)$language['id_lang'].'.jpg" class="pointer" alt="'.$language['name'].'" title="'.$language['name'].'" onclick="changeLanguage(\''.$id.'\', '.$ids.', '.$language['id_lang'].', \''.$language['iso_code'].'\');" /> ';
  543. else
  544. $output .= '<img src="../img/l/'.(int)$language['id_lang'].'.jpg" class="pointer" alt="'.$language['name'].'" title="'.$language['name'].'" onclick="changeLanguage(\''.$id.'\', \''.$ids.'\', '.$language['id_lang'].', \''.$language['iso_code'].'\');" /> ';
  545. $output .= '</div>';
  546. if ($return)
  547. return $output;
  548. echo $output;
  549. }
  550. /**
  551. * Connect module to a hook
  552. *
  553. * @param string $hook_name Hook name
  554. * @param array $shop_list List of shop linked to the hook (if null, link hook to all shops)
  555. * @return boolean result
  556. */
  557. public function registerHook($hook_name, $shop_list = null)
  558. {
  559. // Check hook name validation and if module is installed
  560. if (!Validate::isHookName($hook_name))
  561. throw new PrestaShopException('Invalid hook name');
  562. if (!isset($this->id) || !is_numeric($this->id))
  563. return false;
  564. // Retrocompatibility
  565. if ($alias = Hook::getRetroHookName($hook_name))
  566. $hook_name = $alias;
  567. // Get hook id
  568. $id_hook = Hook::getIdByName($hook_name);
  569. // If hook does not exist, we create it
  570. if (!$id_hook)
  571. {
  572. $new_hook = new Hook();
  573. $new_hook->name = pSQL($hook_name);
  574. $new_hook->title = pSQL($hook_name);
  575. $new_hook->add();
  576. $id_hook = $new_hook->id;
  577. if (!$id_hook)
  578. return false;
  579. }
  580. // If shop lists is null, we fill it with all shops
  581. if (is_null($shop_list))
  582. $shop_list = Shop::getShops(true, null, true);
  583. $return = true;
  584. foreach ($shop_list as $shop_id)
  585. {
  586. // Check if already register
  587. $sql = 'SELECT hm.`id_module`
  588. FROM `'._DB_PREFIX_.'hook_module` hm, `'._DB_PREFIX_.'hook` h
  589. WHERE hm.`id_module` = '.(int)($this->id).' AND h.`id_hook` = '.$id_hook.'
  590. AND h.`id_hook` = hm.`id_hook` AND `id_shop` = '.(int)$shop_id;
  591. if (Db::getInstance()->getRow($sql))
  592. continue;
  593. // Get module position in hook
  594. $sql = 'SELECT MAX(`position`) AS position
  595. FROM `'._DB_PREFIX_.'hook_module`
  596. WHERE `id_hook` = '.(int)$id_hook.' AND `id_shop` = '.(int)$shop_id;
  597. if (!$position = Db::getInstance()->getValue($sql))
  598. $position = 0;
  599. // Register module in hook
  600. $return &= Db::getInstance()->insert('hook_module', array(
  601. 'id_module' => (int)$this->id,
  602. 'id_hook' => (int)$id_hook,
  603. 'id_shop' => (int)$shop_id,
  604. 'position' => (int)($position + 1),
  605. ));
  606. }
  607. return $return;
  608. }
  609. /**
  610. * Unregister module from hook
  611. *
  612. * @param mixed $id_hook Hook id (can be a hook name since 1.5.0)
  613. * @param array $shop_list List of shop
  614. * @return boolean result
  615. */
  616. public function unregisterHook($hook_id, $shop_list = null)
  617. {
  618. // Get hook id if a name is given as argument
  619. if (!is_numeric($hook_id))
  620. {
  621. // Retrocompatibility
  622. $hook_id = Hook::getIdByName($hook_id);
  623. if (!$hook_id)
  624. return false;
  625. }
  626. // Unregister module on hook by id
  627. $sql = 'DELETE FROM `'._DB_PREFIX_.'hook_module`
  628. WHERE `id_module` = '.(int)$this->id.' AND `id_hook` = '.(int)$hook_id
  629. .(($shop_list) ? ' AND `id_shop` IN('.implode(', ', $shop_list).')' : '');
  630. $result = Db::getInstance()->execute($sql);
  631. // Clean modules position
  632. $this->cleanPositions($hook_id, $shop_list);
  633. return $result;
  634. }
  635. /**
  636. * Unregister exceptions linked to module
  637. *
  638. * @param int $id_hook Hook id
  639. * @param array $shop_list List of shop
  640. * @return boolean result
  641. */
  642. public function unregisterExceptions($hook_id, $shop_list = null)
  643. {
  644. $sql = 'DELETE FROM `'._DB_PREFIX_.'hook_module_exceptions`
  645. WHERE `id_module` = '.(int)$this->id.' AND `id_hook` = '.(int)$hook_id
  646. .(($shop_list) ? ' AND `id_shop` IN('.implode(', ', $shop_list).')' : '');
  647. return Db::getInstance()->execute($sql);
  648. }
  649. /**
  650. * Add exceptions for module->Hook
  651. *
  652. * @param int $id_hook Hook id
  653. * @param array $excepts List of file name
  654. * @param array $shop_list List of shop
  655. * @return boolean result
  656. */
  657. public function registerExceptions($id_hook, $excepts, $shop_list = null)
  658. {
  659. // If shop lists is null, we fill it with all shops
  660. if (is_null($shop_list))
  661. $shop_list = Shop::getContextListShopID();
  662. // Save modules exception for each shop
  663. foreach ($shop_list as $shop_id)
  664. {
  665. foreach ($excepts as $except)
  666. {
  667. if (!$except)
  668. continue;
  669. $insertException = array(
  670. 'id_module' => (int)$this->id,
  671. 'id_hook' => (int)$id_hook,
  672. 'id_shop' => (int)$shop_id,
  673. 'file_name' => pSQL($except),
  674. );
  675. $result = Db::getInstance()->insert('hook_module_exceptions', $insertException);
  676. if (!$result)
  677. return false;
  678. }
  679. }
  680. return true;
  681. }
  682. /**
  683. * Edit exceptions for module->Hook
  684. *
  685. * @param int $hookID Hook id
  686. * @param array $excepts List of shopID and file name
  687. * @return boolean result
  688. */
  689. public function editExceptions($id_hook, $excepts)
  690. {
  691. $result = true;
  692. foreach ($excepts as $shop_id => $except)
  693. {
  694. $shop_list = ($shop_id == 0) ? Shop::getContextListShopID() : array($shop_id);
  695. $this->unregisterExceptions($id_hook, $shop_list);
  696. $result &= $this->registerExceptions($id_hook, $except, $shop_list);
  697. }
  698. return $result;
  699. }
  700. /**
  701. * This function is used to determine the module name
  702. * of an AdminTab which belongs to a module, in order to keep translation
  703. * related to a module in its directory (instead of $_LANGADM)
  704. *
  705. * @param mixed $currentClass the
  706. * @return boolean|string if the class belongs to a module, will return the module name. Otherwise, return false.
  707. */
  708. public static function getModuleNameFromClass($currentClass)
  709. {
  710. // Module can now define AdminTab keeping the module translations method,
  711. // i.e. in modules/[module name]/[iso_code].php
  712. if (!isset(self::$classInModule[$currentClass]) && class_exists($currentClass))
  713. {
  714. global $_MODULES;
  715. $_MODULE = array();
  716. $reflectionClass = new ReflectionClass($currentClass);
  717. $filePath = realpath($reflectionClass->getFileName());
  718. $realpathModuleDir = realpath(_PS_MODULE_DIR_);
  719. if (substr(realpath($filePath), 0, strlen($realpathModuleDir)) == $realpathModuleDir)
  720. {
  721. // For controllers in module/controllers path
  722. if (basename(dirname(dirname($filePath))) == 'controllers')
  723. self::$classInModule[$currentClass] = basename(dirname(dirname(dirname($filePath))));
  724. // For old AdminTab controllers
  725. else
  726. self::$classInModule[$currentClass] = substr(dirname($filePath), strlen($realpathModuleDir) + 1);
  727. $file = _PS_MODULE_DIR_.self::$classInModule[$currentClass].'/'.Context::getContext()->language->iso_code.'.php';
  728. if (Tools::file_exists_cache($file) && include_once($file))
  729. $_MODULES = !empty($_MODULES) ? array_merge($_MODULES, $_MODULE) : $_MODULE;
  730. }
  731. else
  732. self::$classInModule[$currentClass] = false;
  733. }
  734. // return name of the module, or false
  735. return self::$classInModule[$currentClass];
  736. }
  737. /**
  738. * Return an instance of the specified module
  739. *
  740. * @param string $module_name Module name
  741. * @return Module
  742. */
  743. public static function getInstanceByName($module_name)
  744. {
  745. if (!Validate::isModuleName($module_name))
  746. die(Tools::displayError());
  747. if (!isset(self::$_INSTANCE[$module_name]))
  748. {
  749. if (Tools::file_exists_cache(_PS_MODULE_DIR_.$module_name.'/'.$module_name.'.php'))
  750. {
  751. include_once(_PS_MODULE_DIR_.$module_name.'/'.$module_name.'.php');
  752. if (class_exists($module_name, false))
  753. return self::$_INSTANCE[$module_name] = new $module_name;
  754. }
  755. return false;
  756. }
  757. return self::$_INSTANCE[$module_name];
  758. }
  759. /**
  760. * Return an instance of the specified module
  761. *
  762. * @param integer $id_module Module ID
  763. * @return Module instance
  764. */
  765. public static function getInstanceById($id_module)
  766. {
  767. static $id2name = null;
  768. if (is_null($id2name))
  769. {
  770. $id2name = array();
  771. $sql = 'SELECT `id_module`, `name` FROM `'._DB_PREFIX_.'module`';
  772. if ($results = Db::getInstance()->executeS($sql))
  773. foreach ($results as $row)
  774. $id2name[$row['id_module']] = $row['name'];
  775. }
  776. if (isset($id2name[$id_module]))
  777. return Module::getInstanceByName($id2name[$id_module]);
  778. return false;
  779. }
  780. public static function configXmlStringFormat($string)
  781. {
  782. return str_replace('\'', '\\\'', Tools::htmlentitiesDecodeUTF8($string));
  783. }
  784. public static function getModuleName($module)
  785. {
  786. // Config file
  787. $configFile = _PS_MODULE_DIR_.$module.'/config.xml';
  788. if (!file_exists($configFile))
  789. return 'Module '.ucfirst($module);
  790. // Load config.xml
  791. libxml_use_internal_errors(true);
  792. $xml_module = simplexml_load_file($configFile);
  793. foreach (libxml_get_errors() as $error)
  794. {
  795. libxml_clear_errors();
  796. return 'Module '.ucfirst($module);
  797. }
  798. libxml_clear_errors();
  799. // Find translations
  800. global $_MODULES;
  801. $file = _PS_MODULE_DIR_.$module.'/'.Context::getContext()->language->iso_code.'.php';
  802. if (Tools::file_exists_cache($file) && include_once($file))
  803. if (isset($_MODULE) && is_array($_MODULE))
  804. $_MODULES = !empty($_MODULES) ? array_merge($_MODULES, $_MODULE) : $_MODULE;
  805. // Return Name
  806. return Translate::getModuleTranslation((string)$xml_module->name, Module::configXmlStringFormat($xml_module->displayName), (string)$xml_module->name);
  807. }
  808. /**
  809. * Return available modules
  810. *
  811. * @param boolean $useConfig in order to use config.xml file in module dir
  812. * @return array Modules
  813. */
  814. public static function getModulesOnDisk($useConfig = false, $loggedOnAddons = false, $id_employee = false)
  815. {
  816. global $_MODULES;
  817. // Init var
  818. $module_list = array();
  819. $module_name_list = array();
  820. $modulesNameToCursor = array();
  821. $errors = array();
  822. // Get modules directory list and memory limit
  823. $modules_dir = Module::getModulesDirOnDisk();
  824. $memory_limit = Tools::getMemoryLimit();
  825. $modules_installed = array();
  826. $result = Db::getInstance()->executeS('
  827. SELECT name, version, interest
  828. FROM `'._DB_PREFIX_.'module`
  829. LEFT JOIN `'._DB_PREFIX_.'module_preference` ON (`module` = `name` AND `id_employee` = '.(int)$id_employee.')');
  830. foreach ($result as $row)
  831. $modules_installed[$row['name']] = $row;
  832. foreach ($modules_dir as $module)
  833. {
  834. // Memory usage checking
  835. if (function_exists('memory_get_usage') && $memory_limit != '-1')
  836. {
  837. $current_memory = memory_get_usage(true);
  838. // memory_threshold in MB
  839. $memory_threshold = (Tools::isX86_64arch() ? 3 : 1.5);
  840. if (($memory_limit - $current_memory) <= ($memory_threshold * 1024 * 1024))
  841. {
  842. $errors[] = Tools::displayError('All modules cannot be loaded due to memory limit restrictions, please increase your memory_limit value on your server configuration');
  843. break;
  844. }
  845. }
  846. // Check if config.xml module file exists and if it's not outdated
  847. $configFile = _PS_MODULE_DIR_.$module.'/config.xml';
  848. $xml_exist = file_exists($configFile);
  849. if ($xml_exist)
  850. $needNewConfigFile = (filemtime($configFile) < filemtime(_PS_MODULE_DIR_.$module.'/'.$module.'.php'));
  851. else
  852. $needNewConfigFile = true;
  853. // If config.xml exists and that the use config flag is at true
  854. if ($useConfig && $xml_exist)
  855. {
  856. // Load config.xml
  857. libxml_use_internal_errors(true);
  858. $xml_module = simplexml_load_file($configFile);
  859. foreach (libxml_get_errors() as $error)
  860. $errors[] = '['.$module.'] '.Tools::displayError('Error found in config file:').' '.htmlentities($error->message);
  861. libxml_clear_errors();
  862. // If no errors in Xml, no need instand and no need new config.xml file, we load only translations
  863. if (!count($errors) && (int)$xml_module->need_instance == 0 && !$needNewConfigFile)
  864. {
  865. $file = _PS_MODULE_DIR_.$module.'/'.Context::getContext()->language->iso_code.'.php';
  866. if (Tools::file_exists_cache($file) && include_once($file))
  867. if (isset($_MODULE) && is_array($_MODULE))
  868. $_MODULES = !empty($_MODULES) ? array_merge($_MODULES, $_MODULE) : $_MODULE;
  869. $item = new stdClass();
  870. $item->id = 0;
  871. $item->warning = '';
  872. foreach ($xml_module as $k => $v)
  873. $item->$k = (string)$v;
  874. $item->displayName = stripslashes(Translate::getModuleTranslation((string)$xml_module->name, Module::configXmlStringFormat($xml_module->displayName), (string)$xml_module->name));
  875. $item->description = stripslashes(Translate::getModuleTranslation((string)$xml_module->name, Module::configXmlStringFormat($xml_module->description), (string)$xml_module->name));
  876. $item->author = stripslashes(Translate::getModuleTranslation((string)$xml_module->name, Module::configXmlStringFormat($xml_module->author), (string)$xml_module->name));
  877. if (isset($xml_module->confirmUninstall))
  878. $item->confirmUninstall = Translate::getModuleTranslation((string)$xml_module->name, Module::configXmlStringFormat($xml_module->confirmUninstall), (string)$xml_module->name);
  879. $item->active = 0;
  880. $item->onclick_option = false;
  881. $module_list[] = $item;
  882. $module_name_list[] = '\''.pSQL($item->name).'\'';
  883. $modulesNameToCursor[strval($item->name)] = $item;
  884. }
  885. }
  886. // If use config flag is at false or config.xml does not exist OR need instance OR need a new config.xml file
  887. if (!$useConfig || !$xml_exist || (isset($xml_module->need_instance) && (int)$xml_module->need_instance == 1) || $needNewConfigFile)
  888. {
  889. // If class does not exists, we include the file
  890. if (!class_exists($module, false))
  891. {
  892. // Get content from php file
  893. $filepath = _PS_MODULE_DIR_.$module.'/'.$module.'.php';
  894. $file = trim(file_get_contents(_PS_MODULE_DIR_.$module.'/'.$module.'.php'));
  895. if (substr($file, 0, 5) == '<?php')
  896. $file = substr($file, 5);
  897. if (substr($file, -2) == '?>')
  898. $file = substr($file, 0, -2);
  899. // If (false) is a trick to not load the class with "eval".
  900. // This way require_once will works correctly
  901. if (eval('if (false){ '.$file.' }') !== false)
  902. require_once( _PS_MODULE_DIR_.$module.'/'.$module.'.php' );
  903. else
  904. $errors[] = sprintf(Tools::displayError('%1$s (parse error in %2$s)'), $module, substr($filepath, strlen(_PS_ROOT_DIR_)));
  905. }
  906. // If class exists, we just instanciate it
  907. if (class_exists($module, false))
  908. {
  909. $tmp_module = new $module;
  910. $item = new stdClass();
  911. $item->id = $tmp_module->id;
  912. $item->warning = $tmp_module->warning;
  913. $item->name = $tmp_module->name;
  914. $item->version = $tmp_module->version;
  915. $item->tab = $tmp_module->tab;
  916. $item->displayName = $tmp_module->displayName;
  917. $item->description = stripslashes($tmp_module->description);
  918. $item->author = $tmp_module->author;
  919. $item->limited_countries = $tmp_module->limited_countries;
  920. $item->parent_class = get_parent_class($module);
  921. $item->is_configurable = $tmp_module->is_configurable = method_exists($tmp_module, 'getContent') ? 1 : 0;
  922. $item->need_instance = isset($tmp_module->need_instance) ? $tmp_module->need_instance : 0;
  923. $item->active = $tmp_module->active;
  924. $item->currencies = isset($tmp_module->currencies) ? $tmp_module->currencies : null;
  925. $item->currencies_mode = isset($tmp_module->currencies_mode) ? $tmp_module->currencies_mode : null;
  926. $item->confirmUninstall = isset($tmp_module->confirmUninstall) ? $tmp_module->confirmUninstall : null;
  927. $item->onclick_option = method_exists($module, 'onclickOption') ? true : false;
  928. if ($item->onclick_option)
  929. {
  930. $href = Context::getContext()->link->getAdminLink('Module', true).'&module_name='.$tmp_module->name.'&tab_module='.$tmp_module->tab;
  931. $item->onclick_option_content = array();
  932. $option_tab = array('desactive', 'reset', 'configure', 'delete');
  933. foreach ($option_tab as $opt)
  934. $item->onclick_option_content[$opt] = $tmp_module->onclickOption($opt, $href);
  935. }
  936. $module_list[] = $item;
  937. if (!$xml_exist || $needNewConfigFile)
  938. {
  939. self::$_generate_config_xml_mode = true;
  940. $tmp_module->_generateConfigXml();
  941. self::$_generate_config_xml_mode = false;
  942. }
  943. unset($tmp_module);
  944. }
  945. else
  946. $errors[] = sprintf(Tools::displayError('%1$s (class missing in %2$s)'), $module, substr($filepath, strlen(_PS_ROOT_DIR_)));
  947. }
  948. }
  949. // Get modules information from database
  950. if (!empty($module_name_list))
  951. {
  952. $list = Shop::getContextListShopID();
  953. $sql = 'SELECT m.id_module, m.name, (
  954. SELECT COUNT(*) FROM '._DB_PREFIX_.'module_shop ms WHERE m.id_module = ms.id_module AND ms.id_shop IN ('.implode(',', $list).')
  955. ) as total
  956. FROM '._DB_PREFIX_.'module m
  957. WHERE m.name IN ('.implode(',', $module_name_list).')';
  958. $results = Db::getInstance()->executeS($sql);
  959. foreach ($results as $result)
  960. {
  961. $moduleCursor = $modulesNameToCursor[$result['name']];
  962. $moduleCursor->id = $result['id_module'];
  963. $moduleCursor->active = ($result['total'] == count($list)) ? 1 : 0;
  964. }
  965. }
  966. // Get Default Country Modules and customer module
  967. $files_list = array(
  968. array('type' => 'addonsNative', 'file' => _PS_ROOT_DIR_.self::CACHE_FILE_DEFAULT_COUNTRY_MODULES_LIST, 'loggedOnAddons' => 0),
  969. array('type' => 'addonsBought', 'file' => _PS_ROOT_DIR_.self::CACHE_FILE_CUSTOMER_MODULES_LIST, 'loggedOnAddons' => 1),
  970. array('type' => 'addonsMustHave', 'file' => _PS_ROOT_DIR_.self::CACHE_FILE_MUST_HAVE_MODULES_LIST, 'loggedOnAddons' => 0),
  971. );
  972. foreach ($files_list as $f)
  973. if (file_exists($f['file']) && ($f['loggedOnAddons'] == 0 || $loggedOnAddons))
  974. {
  975. $file = $f['file'];
  976. $content = Tools::file_get_contents($file);
  977. $xml = @simplexml_load_string($content, null, LIBXML_NOCDATA);
  978. if ($xml && isset($xml->module))
  979. foreach ($xml->module as $modaddons)
  980. {
  981. $flag_found = 0;
  982. foreach ($module_list as $k => $m)
  983. if ($m->name == $modaddons->name && !isset($m->available_on_addons))
  984. {
  985. $flag_found = 1;
  986. if ($m->version != $modaddons->version && version_compare($m->version, $modaddons->version) === -1)
  987. $module_list[$k]->version_addons = $modaddons->version;
  988. }
  989. if ($flag_found == 0)
  990. {
  991. $item = new stdClass();
  992. $item->id = 0;
  993. $item->warning = '';
  994. $item->type = strip_tags((string)$f['type']);
  995. $item->name = strip_tags((string)$modaddons->name);
  996. $item->version = strip_tags((string)$modaddons->version);
  997. $item->tab = strip_tags((string)$modaddons->tab);
  998. $item->displayName = strip_tags((string)$modaddons->displayName).' (Addons)';
  999. $item->description = stripslashes(strip_tags((string)$modaddons->description));
  1000. $item->author = strip_tags((string)$modaddons->author);
  1001. $item->limited_countries = array();
  1002. $item->parent_class = '';
  1003. $item->onclick_option = false;
  1004. $item->is_configurable = 0;
  1005. $item->need_instance = 0;
  1006. $item->not_on_disk = 1;
  1007. $item->available_on_addons = 1;
  1008. $item->active = 0;
  1009. if (isset($modaddons->img))
  1010. {
  1011. if (!file_exists('../img/tmp/'.md5($modaddons->name).'.jpg'))
  1012. if (!@copy($modaddons->img, '../img/tmp/'.md5($modaddons->name).'.jpg'))
  1013. @copy('../img/404.gif', '../img/tmp/'.md5($modaddons->name).'.jpg');
  1014. if (file_exists('../img/tmp/'.md5($modaddons->name).'.jpg'))
  1015. $item->image = '../img/tmp/'.md5($modaddons->name).'.jpg';
  1016. }
  1017. if ($item->type == 'addonsMustHave')
  1018. {
  1019. $item->addons_buy_url = strip_tags((string)$modaddons->url);
  1020. $prices = (array)$modaddons->price;
  1021. $id_default_currency = Configuration::get('PS_CURRENCY_DEFAULT');
  1022. foreach ($prices as $currency => $price)
  1023. if ($id_currency = Currency::getIdByIsoCode($currency))
  1024. if ($id_default_currency == $id_currency)
  1025. {
  1026. $item->price = (float)$price;
  1027. $item->id_currency = (int)$id_currency;
  1028. }
  1029. }
  1030. $module_list[] = $item;
  1031. }
  1032. }
  1033. }
  1034. foreach ($module_list as &$module)
  1035. if (isset($modules_installed[$module->name]))
  1036. {
  1037. $module->installed = true;
  1038. $module->database_version = $modules_installed[$module->name]['version'];
  1039. $module->interest = $modules_installed[$module->name]['interest'];
  1040. }
  1041. else
  1042. {
  1043. $module->installed = false;
  1044. $module->database_version = 0;
  1045. $module->interest = 0;
  1046. }
  1047. usort($module_list, create_function('$a,$b', '
  1048. if ($a->displayName == $b->displayName)
  1049. return 0;
  1050. return ($a->displayName < $b->displayName) ? -1 : 1;
  1051. '));
  1052. if ($errors)
  1053. {
  1054. echo '<div class="alert error"><h3>'.Tools::displayError('The following module(s) could not be loaded').':</h3><ol>';
  1055. foreach ($errors as $error)
  1056. echo '<li>'.$error.'</li>';
  1057. echo '</ol></div>';
  1058. }
  1059. return $module_list;
  1060. }
  1061. /**
  1062. * Return modules directory list
  1063. *
  1064. * @return array Modules Directory List
  1065. */
  1066. public static function getModulesDirOnDisk()
  1067. {
  1068. $module_list = array();
  1069. $modules = scandir(_PS_MODULE_DIR_);
  1070. foreach ($modules as $name)
  1071. {
  1072. if (is_dir(_PS_MODULE_DIR_.$name) && Tools::file_exists_cache(_PS_MODULE_DIR_.$name.'/'.$name.'.php'))
  1073. {
  1074. if (!Validate::isModuleName($name))
  1075. throw new PrestaShopException(sprintf('Module %s is not a valid module name', $name));
  1076. $module_list[] = $name;
  1077. }
  1078. }
  1079. return $module_list;
  1080. }
  1081. /**
  1082. * Return non native module
  1083. *
  1084. * @param int $position Take only positionnables modules
  1085. * @return array Modules
  1086. */
  1087. public static function getNonNativeModuleList()
  1088. {
  1089. $db = Db::getInstance();
  1090. $module_list_xml = _PS_ROOT_DIR_.self::CACHE_FILE_MODULES_LIST;
  1091. $native_modules = simplexml_load_file($module_list_xml);
  1092. $native_modules = $native_modules->modules;
  1093. foreach ($native_modules as $native_modules_type)
  1094. if (in_array($native_modules_type['type'], array('native', 'partner')))
  1095. {
  1096. $arr_native_modules[] = '""';
  1097. foreach ($native_modules_type->module as $module)
  1098. $arr_native_modules[] = '"'.pSQL($module['name']).'"';
  1099. }
  1100. return $db->executeS('SELECT * FROM `'._DB_PREFIX_.'module` m WHERE `name` NOT IN ('.implode(',', $arr_native_modules).') ');
  1101. }
  1102. /**
  1103. * Return installed modules
  1104. *
  1105. * @param int $position Take only positionnables modules
  1106. * @return array Modules
  1107. */
  1108. public static function getModulesInstalled($position = 0)
  1109. {
  1110. $sql = 'SELECT m.* FROM `'._DB_PREFIX_.'module` m ';
  1111. if ($position)
  1112. $sql .= 'LEFT JOIN `'._DB_PREFIX_.'hook_module` hm ON m.`id_module` = hm.`id_module`
  1113. LEFT JOIN `'._DB_PREFIX_.'hook` k ON hm.`id_hook` = k.`id_hook`
  1114. WHERE k.`position` = 1
  1115. GROUP BY m.id_module';
  1116. return Db::getInstance()->executeS($sql);
  1117. }
  1118. /**
  1119. * Execute modules for specified hook
  1120. *
  1121. * @param string $hook_name Hook Name
  1122. * @param array $hookArgs Parameters for the functions
  1123. * @return string modules output
  1124. */
  1125. public static function hookExec($hook_name, $hookArgs = array(), $id_module = null)
  1126. {
  1127. Tools::displayAsDeprecated();
  1128. return Hook::exec($hook_name, $hookArgs, $id_module);
  1129. }
  1130. public static function hookExecPayment()
  1131. {
  1132. Tools::displayAsDeprecated();
  1133. return Hook::exec('displayPayment');
  1134. }
  1135. public static function preCall($module_name)
  1136. {
  1137. return true;
  1138. }
  1139. /**
  1140. * Returns the list of the payment module associated to the current customer
  1141. * @see PaymentModule::getInstalledPaymentModules() if you don't care about the context
  1142. *
  1143. * @return array module informations
  1144. */
  1145. public static function getPaymentModules()
  1146. {
  1147. $context = Context::getContext();
  1148. if (isset($context->cart))
  1149. $billing = new Address((int)$context->cart->id_address_invoice);
  1150. $frontend = true;
  1151. $groups = array();
  1152. if (isset($context->employee))
  1153. $frontend = false;
  1154. elseif (isset($context->customer))
  1155. {
  1156. $groups = $context->customer->getGroups();
  1157. if (empty($groups))
  1158. $groups = array(Configuration::get('PS_UNIDENTIFIED_GROUP'));
  1159. }
  1160. $hookPayment = 'Payment';
  1161. if (Db::getInstance()->getValue('SELECT `id_hook` FROM `'._DB_PREFIX_.'hook` WHERE `name` = \'displayPayment\''))
  1162. $hookPayment = 'displayPayment';
  1163. $paypal_condition = '';
  1164. $iso_code = Country::getIsoById((int)Configuration::get('PS_COUNTRY_DEFAULT'));
  1165. $paypal_countries = array('ES', 'FR', 'PL', 'IT');
  1166. if (Context::getContext()->getMobileDevice() && Context::getContext()->shop->getTheme() == 'default' && in_array($iso_code, $paypal_countries))
  1167. $paypal_condition = ' AND m.`name` = \'paypal\'';
  1168. $list = Shop::getContextListShopID();
  1169. return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SELECT DISTINCT m.`id_module`, h.`id_hook`, m.`name`, hm.`position`
  1170. FROM `'._DB_PREFIX_.'module` m
  1171. '.($frontend ? 'LEFT JOIN `'._DB_PREFIX_.'module_country` mc ON (m.`id_module` = mc.`id_module` AND mc.id_shop = '.(int)$context->shop->id.')' : '').'
  1172. '.($frontend ? 'INNER JOIN `'._DB_PREFIX_.'module_group` mg ON (m.`id_module` = mg.`id_module` AND mg.id_shop = '.(int)$context->shop->id.')' : '').'
  1173. '.($frontend && isset($context->customer) ? 'INNER JOIN `'._DB_PREFIX_.'customer_group` cg on (cg.`id_group` = mg.`id_group`AND cg.`id_customer` = '.(int)$context->customer->id.')' : '').'
  1174. LEFT JOIN `'._DB_PREFIX_.'hook_module` hm ON hm.`id_module` = m.`id_module`
  1175. LEFT JOIN `'._DB_PREFIX_.'hook` h ON hm.`id_hook` = h.`id_hook`
  1176. WHERE h.`name` = \''.pSQL($hookPayment).'\'
  1177. '.(isset($billing) && $frontend ? 'AND mc.id_country = '.(int)$billing->id_country : '').'
  1178. AND (SELECT COUNT(*) FROM '._DB_PREFIX_.'module_shop ms WHERE ms.id_module = m.id_module AND ms.id_shop IN('.implode(', ', $list).')) = '.count($list).'
  1179. AND hm.id_shop IN('.implode(', ', $list).')
  1180. '.(count($groups) && $frontend ? 'AND (mg.`id_group` IN('.implode(', ', $groups).'))' : '').$paypal_condition.'
  1181. GROUP BY hm.id_hook, hm.id_module
  1182. ORDER BY hm.`position`, m.`name` DESC');
  1183. }
  1184. /**
  1185. * @deprecated 1.5.0 Use Translate::getModuleTranslation()
  1186. */
  1187. public static function findTranslation($name, $string, $source)
  1188. {
  1189. return Translate::getModuleTranslation($name, $string, $source);
  1190. }
  1191. /**
  1192. * Get translation for a given module text
  1193. *
  1194. * Note: $specific parameter is mandatory for library files.
  1195. * Otherwise, translation key will not match for Module library
  1196. * when module is loaded with eval() Module::getModulesOnDisk()
  1197. *
  1198. * @param string $string String to translate
  1199. * @param boolean|string $specific filename to use in translation key
  1200. * @return string Translation
  1201. */
  1202. public function l($string, $specific = false, $id_lang = null)
  1203. {
  1204. if (self::$_generate_config_xml_mode)
  1205. return $string;
  1206. return Translate::getModuleTranslation($this, $string, ($specific) ? $specific : $this->name);
  1207. }
  1208. /*
  1209. * Reposition module
  1210. *
  1211. * @param boolean $id_hook Hook ID
  1212. * @param boolean $way Up (1) or Down (0)
  1213. * @param int $position
  1214. */
  1215. public function updatePosition($id_hook, $way, $position = null)
  1216. {
  1217. foreach (Shop::getContextListShopID() as $shop_id)
  1218. {
  1219. $sql = 'SELECT hm.`id_module`, hm.`position`, hm.`id_hook`
  1220. FROM `'._DB_PREFIX_.'hook_module` hm
  1221. WHERE hm.`id_hook` = '.(int)$id_hook.' AND hm.`id_shop` = '.$shop_id.'
  1222. ORDER BY hm.`position` '.($way ? 'ASC' : 'DESC');
  1223. if (!$res = Db::getInstance()->executeS($sql))
  1224. continue;
  1225. foreach ($res as $key => $values)
  1226. if ((int)$values[$this->identifier] == (int)$this->id)
  1227. {
  1228. $k = $key;
  1229. break;
  1230. }
  1231. if (!isset($k) || !isset($res[$k]) || !isset($res[$k + 1]))
  1232. return false;
  1233. $from = $res[$k];
  1234. $to = $res[$k + 1];
  1235. if (isset($position) && !empty($position))
  1236. $to['position'] = (int)$position;
  1237. $sql = 'UPDATE `'._DB_PREFIX_.'hook_module`
  1238. SET `position`= position '.($way ? '-1' : '+1').'
  1239. WHERE position between '.(int)(min(array($from['position'], $to['position']))).' AND '.max(array($from['position'], $to['position'])).'
  1240. AND `id_hook` = '.(int)$from['id_hook'].' AND `id_shop` = '.$shop_id;
  1241. if (!Db::getInstance()->execute($sql))
  1242. return false;
  1243. $sql = 'UPDATE `'._DB_PREFIX_.'hook_module`
  1244. SET `position`='.(int)$to['position'].'
  1245. WHERE `'.pSQL($this->identifier).'` = '.(int)$from[$this->identifier].'
  1246. AND `id_hook` = '.(int)$to['id_hook'].' AND `id_shop` = '.$shop_id;
  1247. if (!Db::getInstance()->execute($sql))
  1248. return false;
  1249. }
  1250. return true;
  1251. }
  1252. /*
  1253. * Reorder modules position
  1254. *
  1255. * @param boolean $id_hook Hook ID
  1256. * @param array $shop_list List of shop
  1257. */
  1258. public function cleanPositions($id_hook, $shop_list = null)
  1259. {
  1260. $sql = 'SELECT `id_module`, `id_shop`
  1261. FROM `'._DB_PREFIX_.'hook_module`
  1262. WHERE `id_hook` = '.(int)$id_hook.'
  1263. '.((!is_null($shop_list) && $shop_list) ? ' AND `id_shop` IN('.implode(', ', $shop_list).')' : '').'
  1264. ORDER BY `position`';
  1265. $results = Db::getInstance()->executeS($sql);
  1266. $position = array();
  1267. foreach ($results as $row)
  1268. {
  1269. if (!isset($position[$row['id_shop']]))
  1270. $position[$row['id_shop']] = 1;
  1271. $sql = 'UPDATE `'._DB_PREFIX_.'hook_module`
  1272. SET `position` = '.$position[$row['id_shop']].'
  1273. WHERE `id_hook` = '.(int)$id_hook.'
  1274. AND `id_module` = '.$row['id_module'].' AND `id_shop` = '.$row['id_shop'];
  1275. Db::getInstance()->execute($sql);
  1276. $position[$row['id_shop']]++;
  1277. }
  1278. return true;
  1279. }
  1280. public function displayError($error)
  1281. {
  1282. $output = '
  1283. <div class="module_error alert error">
  1284. '.$error.'
  1285. </div>';
  1286. $this->error = true;
  1287. return $output;
  1288. }
  1289. public function displayConfirmation($string)
  1290. {
  1291. $output = '
  1292. <div class="module_confirmation conf confirm">
  1293. '.$string.'
  1294. </div>';
  1295. return $output;
  1296. }
  1297. /*
  1298. * Return exceptions for module in hook
  1299. *
  1300. * @param int $id_hook Hook ID
  1301. * @return array Exceptions
  1302. */
  1303. protected static $exceptionsCache = null;
  1304. public function getExceptions($hookID, $dispatch = false)
  1305. {
  1306. if (self::$exceptionsCache === null)
  1307. {
  1308. self::$exceptionsCache = array();
  1309. $sql = 'SELECT * FROM `'._DB_PREFIX_.'hook_module_exceptions`
  1310. WHERE `id_shop` IN ('.implode(', ', Shop::getContextListShopID()).')';
  1311. $result = Db::getInstance()->executeS($sql);
  1312. foreach ($result as $row)
  1313. {
  1314. if (!$row['file_name'])
  1315. continue;
  1316. $ke

Large files files are truncated, but you can click here to view the full file