PageRenderTime 149ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/classes/module/Module.php

https://gitlab.com/mtellezgalindo/PrestaShop
PHP | 2679 lines | 1790 code | 346 blank | 543 comment | 323 complexity | 81c2539bb4f6ebe0b8cf967aed677bbb MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-3.0

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

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

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