PageRenderTime 75ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/controllers/admin/AdminThemesController.php

https://gitlab.com/mtellezgalindo/PrestaShop
PHP | 2967 lines | 2497 code | 337 blank | 133 comment | 427 complexity | 6283e241d2c00ffb7793d8e6cfa8bb41 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-3.0
  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. class AdminThemesControllerCore extends AdminController
  27. {
  28. const MAX_NAME_LENGTH = 128;
  29. public function __construct()
  30. {
  31. $this->bootstrap = true;
  32. parent::__construct();
  33. }
  34. /** This value is used in isThemeCompatible method. only version node with an
  35. * higher version number will be used in [theme]/config.xml
  36. * @since 1.4.0.11, check theme compatibility 1.4
  37. * @static
  38. */
  39. public static $check_features_version = '1.4';
  40. /** $check_features is a multidimensional array used to check [theme]/config.xml values,
  41. * and also checks prestashop current configuration if not match.
  42. * @static
  43. */
  44. public static $check_features = array(
  45. 'ccc' => array(
  46. 'attributes' => array(
  47. 'available' => array(
  48. 'value' => 'true',
  49. /*
  50. * accepted attribute value if value doesn't match, prestashop configuration value must have those values
  51. */
  52. 'check_if_not_valid' => array(
  53. 'PS_CSS_THEME_CACHE' => 0,
  54. 'PS_JS_THEME_CACHE' => 0,
  55. 'PS_HTML_THEME_COMPRESSION' => 0,
  56. 'PS_JS_HTML_THEME_COMPRESSION' => 0,
  57. ),
  58. ),
  59. ),
  60. 'error' => 'This theme may not correctly use PrestaShop\'s "combine, compress and cache" options.',
  61. 'tab' => 'AdminPerformance',
  62. ),
  63. 'guest_checkout' => array(
  64. 'attributes' => array(
  65. 'available' => array(
  66. 'value' => 'true',
  67. 'check_if_not_valid' => array('PS_GUEST_CHECKOUT_ENABLED' => 0)
  68. ),
  69. ),
  70. 'error' => 'This theme may not correctly use PrestaShop\'s "guest checkout" feature.',
  71. 'tab' => 'AdminPreferences',
  72. ),
  73. 'one_page_checkout' => array(
  74. 'attributes' => array(
  75. 'available' => array(
  76. 'value' => 'true',
  77. 'check_if_not_valid' => array('PS_ORDER_PROCESS_TYPE' => 0),
  78. ),
  79. ),
  80. 'error' => 'This theme may not correctly use PrestaShop\'s "one-page checkout" feature.',
  81. 'tab' => 'AdminPreferences',
  82. ),
  83. 'store_locator' => array(
  84. 'attributes' => array(
  85. 'available' => array(
  86. 'value' => 'true',
  87. 'check_if_not_valid' => array(
  88. 'PS_STORES_SIMPLIFIED' => 0,
  89. 'PS_STORES_DISPLAY_FOOTER' => 0
  90. ),
  91. )
  92. ),
  93. 'error' => 'This theme may not correctly use PrestaShop\'s "store locator" feature.',
  94. 'tab' => 'AdminStores',
  95. )
  96. );
  97. public $className = 'Theme';
  98. public $table = 'theme';
  99. protected $toolbar_scroll = false;
  100. private $img_error;
  101. public function init()
  102. {
  103. // No cache for auto-refresh uploaded logo
  104. header('Cache-Control: no-cache, must-revalidate');
  105. header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
  106. parent::init();
  107. $this->can_display_themes = (!Shop::isFeatureActive() || Shop::getContext() == Shop::CONTEXT_SHOP);
  108. libxml_use_internal_errors(true);
  109. // Download user themes from Addons
  110. if ($this->logged_on_addons)
  111. $this->downloadAddonsThemes();
  112. // Employee languages used for link and utm_source
  113. $lang = new Language($this->context->language->id);
  114. $iso_lang_uc = strtoupper($lang->iso_code);
  115. $this->fields_options = array(
  116. 'appearance' => array(
  117. 'title' => $this->l('Your current theme'),
  118. 'icon' => 'icon-html5',
  119. 'tabs' => array(
  120. 'logo' => $this->l('Logo'),
  121. 'logo2' => $this->l('Invoice & Email Logos'),
  122. 'icons' => $this->l('Icons'),
  123. 'mobile' => $this->l('Mobile'),
  124. ),
  125. 'fields' => array(
  126. 'PS_LOGO' => array(
  127. 'title' => $this->l('Header logo'),
  128. 'hint' => $this->l('Will appear on main page. Recommended height: 52px. Maximum height on default theme: 65px.'),
  129. 'type' => 'file',
  130. 'name' => 'PS_LOGO',
  131. 'tab' => 'logo',
  132. 'thumb' => _PS_IMG_.Configuration::get('PS_LOGO')
  133. ),
  134. 'PS_LOGO_MOBILE' => array(
  135. 'title' => $this->l('Header logo for mobile'),
  136. 'desc' => ((Configuration::get('PS_LOGO_MOBILE') === false) ? '<span class="light-warning">'.$this->l('Warning: No mobile logo has been defined. The header logo will be used instead.').'</span><br />' : ''),
  137. 'hint' => $this->l('Will appear on the main page of your mobile template. If left undefined, the header logo will be used.'),
  138. 'type' => 'file',
  139. 'name' => 'PS_LOGO_MOBILE',
  140. 'tab' => 'mobile',
  141. 'thumb' => (Configuration::get('PS_LOGO_MOBILE') !== false && file_exists(_PS_IMG_DIR_.Configuration::get('PS_LOGO_MOBILE'))) ? _PS_IMG_.Configuration::get('PS_LOGO_MOBILE') : _PS_IMG_.Configuration::get('PS_LOGO')
  142. ),
  143. 'PS_LOGO_MAIL' => array(
  144. 'title' => $this->l('Mail logo'),
  145. 'desc' => ((Configuration::get('PS_LOGO_MAIL') === false) ? '<span class="light-warning">'.$this->l('Warning: if no email logo is available, the main logo will be used instead.').'</span><br />' : ''),
  146. 'hint' => $this->l('Will appear on email headers. If undefined, the header logo will be used.'),
  147. 'type' => 'file',
  148. 'name' => 'PS_LOGO_MAIL',
  149. 'tab' => 'logo2',
  150. 'thumb' => (Configuration::get('PS_LOGO_MAIL') !== false && file_exists(_PS_IMG_DIR_.Configuration::get('PS_LOGO_MAIL'))) ? _PS_IMG_.Configuration::get('PS_LOGO_MAIL') : _PS_IMG_.Configuration::get('PS_LOGO')
  151. ),
  152. 'PS_LOGO_INVOICE' => array(
  153. 'title' => $this->l('Invoice logo'),
  154. 'desc' => ((Configuration::get('PS_LOGO_INVOICE') === false) ? '<span class="light-warning">'.$this->l('Warning: if no invoice logo is available, the main logo will be used instead.').'</span><br />' : ''),
  155. 'hint' => $this->l('Will appear on invoice headers.').' '.$this->l('Warning: you can use a PNG file for transparency, but it can take up to 1 second per page for processing. Please consider using JPG instead.'),
  156. 'type' => 'file',
  157. 'name' => 'PS_LOGO_INVOICE',
  158. 'tab' => 'logo2',
  159. 'thumb' => (Configuration::get('PS_LOGO_INVOICE') !== false && file_exists(_PS_IMG_DIR_.Configuration::get('PS_LOGO_INVOICE'))) ? _PS_IMG_.Configuration::get('PS_LOGO_INVOICE') : _PS_IMG_.Configuration::get('PS_LOGO')
  160. ),
  161. 'PS_FAVICON' => array(
  162. 'title' => $this->l('Favicon'),
  163. 'hint' => $this->l('Will appear in the address bar of your web browser.'),
  164. 'type' => 'file',
  165. 'name' => 'PS_FAVICON',
  166. 'tab' => 'icons',
  167. 'thumb' => _PS_IMG_.Configuration::get('PS_FAVICON').(Tools::getValue('conf') ? sprintf('?%04d', rand(0, 9999)) : '')
  168. ),
  169. 'PS_STORES_ICON' => array(
  170. 'title' => $this->l('Store icon'),
  171. 'hint' => $this->l('Will appear on the store locator (inside Google Maps).').'<br />'.$this->l('Suggested size: 30x30, transparent GIF.'),
  172. 'type' => 'file',
  173. 'name' => 'PS_STORES_ICON',
  174. 'tab' => 'icons',
  175. 'thumb' => _PS_IMG_.Configuration::get('PS_STORES_ICON')
  176. ),
  177. 'PS_ALLOW_MOBILE_DEVICE' => array(
  178. 'title' => $this->l('Enable the mobile theme'),
  179. 'hint' => $this->l('Allows visitors browsing on mobile devices to view a lighter version of your website.'),
  180. 'type' => 'radio',
  181. 'required' => true,
  182. 'validation' => 'isGenericName',
  183. 'tab' => 'mobile',
  184. 'choices' => array(
  185. 0 => $this->l('I\'d like to disable it.'),
  186. 1 => $this->l('I\'d like to enable it only on smartphones.'),
  187. 2 => $this->l('I\'d like to enable it only on tablets.'),
  188. 3 => $this->l('I\'d like to enable it on both smartphones and tablets.')
  189. )
  190. ),
  191. ),
  192. 'after_tabs' => array(
  193. 'cur_theme' => Theme::getThemeInfo($this->context->shop->id_theme),
  194. ),
  195. 'submit' => array('title' => $this->l('Save')),
  196. 'buttons' => array(
  197. 'storeLink' => array(
  198. 'title' => $this->l('Visit the theme catalog'),
  199. 'icon' => 'process-icon-themes',
  200. 'href' => 'http://addons.prestashop.com/en/3-templates-prestashop'
  201. .'?utm_source=back-office&utm_medium=theme-button'
  202. .'&utm_campaign=back-office-'.$iso_lang_uc
  203. .'&utm_content='.(defined('_PS_HOST_MODE_') ? 'cloud' : 'download'),
  204. 'js' => 'return !window.open(this.href)'
  205. )
  206. )
  207. ),
  208. );
  209. $installed_theme = Theme::getAllThemes(array($this->context->shop->id_theme));
  210. $non_installed_theme = ($this->context->mode == Context::MODE_HOST) ? array() : Theme::getNonInstalledTheme();
  211. if (count($installed_theme) || !empty($non_installed_theme))
  212. {
  213. $this->fields_options['theme'] = array(
  214. 'title' => sprintf($this->l('Select a theme for the "%s" shop'), $this->context->shop->name),
  215. 'description' => (!$this->can_display_themes) ? $this->l('You must select a shop from the above list if you wish to choose a theme.') : '',
  216. 'fields' => array(
  217. 'theme_for_shop' => array(
  218. 'type' => 'theme',
  219. 'themes' => $installed_theme,
  220. 'not_installed' => $non_installed_theme,
  221. 'id_theme' => $this->context->shop->id_theme,
  222. 'can_display_themes' => $this->can_display_themes,
  223. 'no_multishop_checkbox' => true
  224. ),
  225. ),
  226. );
  227. }
  228. }
  229. public function renderForm()
  230. {
  231. $get_available_themes = Theme::getAvailable(false);
  232. $available_theme_dir = array();
  233. $selected_theme_dir = null;
  234. $metas = Meta::getMetas();
  235. $formated_metas = array();
  236. $image_url = false;
  237. if ($this->object)
  238. {
  239. if ((int)$this->object->id > 0)
  240. {
  241. $theme = New Theme((int)$this->object->id);
  242. $theme_metas = Db::getInstance()->executeS('SELECT ml.`title`, m.`page`, tm.`left_column` as `left`, tm.`right_column` as `right`, m.`id_meta`, tm.`id_theme_meta`
  243. FROM '._DB_PREFIX_.'theme_meta as tm
  244. LEFT JOIN '._DB_PREFIX_.'meta m ON (m.`id_meta` = tm.`id_meta`)
  245. LEFT JOIN '._DB_PREFIX_.'meta_lang ml ON(ml.id_meta = m.id_meta AND ml.id_lang = '.(int)$this->context->language->id.')
  246. WHERE tm.`id_theme` = '.(int)$this->object->id.
  247. ((int)Context::getContext()->shop->id ? ' AND id_shop = '.(int)Context::getContext()->shop->id : ''));
  248. // if no theme_meta are found, we must create them
  249. if (empty($theme_metas))
  250. {
  251. $metas = Db::getInstance()->executeS('SELECT id_meta FROM '._DB_PREFIX_.'meta');
  252. $metas_default = array();
  253. foreach ($metas as $meta)
  254. {
  255. $tmp_meta['id_meta'] = (int)$meta['id_meta'];
  256. $tmp_meta['left'] = 1;
  257. $tmp_meta['right'] = 1;
  258. $metas_default[] = $tmp_meta;
  259. }
  260. $theme->updateMetas($metas_default);
  261. $theme_metas = Db::getInstance()->executeS('SELECT ml.`title`, m.`page`, tm.`left_column` as `left`, tm.`right_column` as `right`, m.`id_meta`, tm.`id_theme_meta`
  262. FROM '._DB_PREFIX_.'theme_meta as tm
  263. LEFT JOIN '._DB_PREFIX_.'meta m ON (m.`id_meta` = tm.`id_meta`)
  264. LEFT JOIN '._DB_PREFIX_.'meta_lang ml ON(ml.id_meta = m.id_meta AND ml.id_lang = '.(int)$this->context->language->id.')
  265. WHERE tm.`id_theme` = '.(int)$this->object->id);
  266. }
  267. $image_url = '<img alt="preview" src="'.__PS_BASE_URI__.'themes/'.$theme->directory.'/preview.jpg">';
  268. foreach ($theme_metas as $key => &$meta)
  269. if (!isset($meta['title']) || !$meta['title'] || $meta['title'] == '')
  270. $meta['title'] = $meta['page'];
  271. $formated_metas = $theme_metas;
  272. }
  273. $selected_theme_dir = $this->object->directory;
  274. }
  275. foreach ($get_available_themes as $k => $dirname)
  276. {
  277. $available_theme_dir[$k]['value'] = $dirname;
  278. $available_theme_dir[$k]['label'] = $dirname;
  279. $available_theme_dir[$k]['id'] = $dirname;
  280. };
  281. $this->fields_form = array(
  282. 'tinymce' => false,
  283. 'legend' => array(
  284. 'title' => $this->l('Theme'),
  285. 'icon' => 'icon-picture'
  286. ),
  287. 'input' => array(
  288. array(
  289. 'type' => 'text',
  290. 'label' => $this->l('Name of the theme'),
  291. 'name' => 'name',
  292. 'required' => true,
  293. 'hint' => $this->l('Invalid characters:').' <>;=#{}',
  294. ),
  295. array(
  296. 'type' => 'file',
  297. 'label' => $this->l('Preview image for the theme'),
  298. 'name' => 'image_preview',
  299. 'display_image' => true,
  300. 'hint' => sprintf($this->l('Maximum image size: %1s'), Tools::formatBytes(Tools::getMaxUploadSize())),
  301. 'image' => $image_url,
  302. ),
  303. array(
  304. 'type' => 'switch',
  305. 'label' => $this->l('Default left column'),
  306. 'name' => 'default_left_column',
  307. 'hint' => $this->l('Choose a default behavior when displaying the column in a new page added by you or by a module.'),
  308. 'values' => array(
  309. array(
  310. 'id' => 'default_left_column_on',
  311. 'value' => 1,
  312. 'label' => $this->l('Yes')
  313. ),
  314. array(
  315. 'id' => 'default_left_column_off',
  316. 'value' => 0,
  317. 'label' => $this->l('No')
  318. )
  319. ),
  320. ),
  321. array(
  322. 'type' => 'switch',
  323. 'label' => $this->l('Default right column'),
  324. 'name' => 'default_right_column',
  325. 'hint' => $this->l('Choose a default behavior when displaying the column in a new page added by you or by a module.'),
  326. 'values' => array(
  327. array(
  328. 'id' => 'default_right_column_on',
  329. 'value' => 1,
  330. 'label' => $this->l('Yes')
  331. ),
  332. array(
  333. 'id' => 'default_right_column_off',
  334. 'value' => 0,
  335. 'label' => $this->l('No')
  336. )
  337. ),
  338. ),
  339. array(
  340. 'type' => 'text',
  341. 'label' => $this->l('Number of products per page'),
  342. 'name' => 'product_per_page',
  343. 'hint' => $this->l('This value will be used when activating the theme.'),
  344. )
  345. ),
  346. 'submit' => array(
  347. 'title' => $this->l('Save'),
  348. )
  349. );
  350. // adding a new theme, you can create a directory, and copy from an existing theme
  351. if ($this->display == 'add' || !Validate::isLoadedObject($this->object))
  352. {
  353. $this->fields_form['input'][] = array(
  354. 'type' => 'text',
  355. 'label' => $this->l('Name of the theme\'s directory'),
  356. 'name' => 'directory',
  357. 'required' => true,
  358. 'hint' => $this->l('If the directory does not exist, PrestaShop will create it automatically.'),
  359. );
  360. $theme_query = Theme::getThemes();
  361. $this->fields_form['input'][] = array(
  362. 'type' => 'select',
  363. 'name' => 'based_on',
  364. 'label' => $this->l('Copy missing files from existing theme'),
  365. 'hint' => $this->l('If you create a new theme from scratch, it is recommended that you use the files from the default theme as a foundation.'),
  366. 'options' => array(
  367. 'id' => 'id',
  368. 'name' => 'name',
  369. 'default' => array(
  370. 'value' => 0,
  371. 'label' => '-'
  372. ),
  373. 'query' => $theme_query,
  374. )
  375. );
  376. $this->fields_form['input'][] = array(
  377. 'type' => 'switch',
  378. 'label' => $this->l('Responsive'),
  379. 'name' => 'responsive',
  380. 'hint' => $this->l('Please indicate if the theme is adapted to all screen sizes (mobile, tablet, desktop).'),
  381. 'values' => array(
  382. array(
  383. 'id' => 'responsive_on',
  384. 'value' => 1,
  385. 'label' => $this->l('Yes')
  386. ),
  387. array(
  388. 'id' => 'responsive_off',
  389. 'value' => 0,
  390. 'label' => $this->l('No')
  391. )
  392. ),
  393. );
  394. }
  395. else
  396. $this->fields_form['input'][] = array(
  397. 'type' => 'radio',
  398. 'label' => $this->l('Directory'),
  399. 'name' => 'directory',
  400. 'required' => true,
  401. 'br' => true,
  402. 'values' => $available_theme_dir,
  403. 'selected' => $selected_theme_dir,
  404. 'hint' => $this->l('Please select a valid theme directory.'),
  405. );
  406. $list = '';
  407. if (Tools::getIsset('update'.$this->table))
  408. {
  409. $fields_list = array(
  410. 'title' => array(
  411. 'title' => $this->l('Meta'),
  412. 'align' => 'center',
  413. 'width' => 'auto'
  414. ),
  415. 'left' => array(
  416. 'title' => $this->l('Left column'),
  417. 'active' => 'left',
  418. 'type' => 'bool',
  419. 'ajax' => true
  420. ),
  421. 'right' => array(
  422. 'title' => $this->l('Right column'),
  423. 'active' => 'right',
  424. 'type' => 'bool',
  425. 'ajax' => true
  426. ),
  427. );
  428. $helper_list = New HelperList();
  429. $helper_list->tpl_vars = array('icon' => 'icon-columns');
  430. $helper_list->title = $this->l('Appearance of columns');
  431. $helper_list->no_link = true;
  432. $helper_list->shopLinkType = '';
  433. $helper_list->identifier = 'id_theme_meta';
  434. $helper_list->table = 'meta';
  435. $helper_list->tpl_vars['show_filters'] = false;
  436. $helper_list->currentIndex = $this->context->link->getAdminLink('AdminThemes', false);
  437. $helper_list->token = Tools::getAdminTokenLite('AdminThemes');
  438. $list = $helper_list->generateList($formated_metas, $fields_list);
  439. }
  440. return parent::renderForm().$list;
  441. }
  442. public function renderList()
  443. {
  444. return parent::renderList();
  445. }
  446. /**
  447. * copy $base_theme_dir into $target_theme_dir.
  448. *
  449. * @param string $base_theme_dir relative path to base dir
  450. * @param string $target_theme_dir relative path to target dir
  451. *
  452. * @return boolean true if success
  453. */
  454. protected static function copyTheme($base_theme_dir, $target_theme_dir)
  455. {
  456. $res = true;
  457. $base_theme_dir = Tools::normalizeDirectory($base_theme_dir);
  458. $base_dir = _PS_ALL_THEMES_DIR_.$base_theme_dir;
  459. $target_theme_dir = Tools::normalizeDirectory($target_theme_dir);
  460. $target_dir = _PS_ALL_THEMES_DIR_.$target_theme_dir;
  461. $files = scandir($base_dir);
  462. foreach ($files as $file)
  463. {
  464. if (!in_array($file[0], array('.', '..', '.svn')))
  465. {
  466. if (is_dir($base_dir.$file))
  467. {
  468. if (!is_dir($target_dir.$file))
  469. mkdir($target_dir.$file, Theme::$access_rights);
  470. $res &= AdminThemesController::copyTheme($base_theme_dir.$file, $target_theme_dir.$file);
  471. }
  472. elseif (!file_exists($target_dir.$file))
  473. $res &= copy($base_dir.$file, $target_dir.$file);
  474. }
  475. }
  476. return $res;
  477. }
  478. public function downloadAddonsThemes()
  479. {
  480. if (!$this->logged_on_addons)
  481. return false;
  482. if (!$this->isFresh(Theme::CACHE_FILE_CUSTOMER_THEMES_LIST, 86400))
  483. file_put_contents(_PS_ROOT_DIR_.Theme::CACHE_FILE_CUSTOMER_THEMES_LIST, Tools::addonsRequest('customer_themes'));
  484. $customer_themes_list = file_get_contents(_PS_ROOT_DIR_.Theme::CACHE_FILE_CUSTOMER_THEMES_LIST);
  485. if (!empty($customer_themes_list) && $customer_themes_list_xml = simplexml_load_string($customer_themes_list))
  486. {
  487. foreach ($customer_themes_list_xml->theme as $addons_theme)
  488. {
  489. //get addons theme if folder does not exist
  490. $ids_themes = Tools::unSerialize(Configuration::get('PS_ADDONS_THEMES_IDS'));
  491. if (!is_array($ids_themes) || (is_array($ids_themes) && !in_array((string)$addons_theme->id, $ids_themes)))
  492. {
  493. $zip_content = Tools::addonsRequest('module', array(
  494. 'id_module' => pSQL($addons_theme->id),
  495. 'username_addons' => pSQL(trim($this->context->cookie->username_addons)),
  496. 'password_addons' => pSQL(trim($this->context->cookie->password_addons)))
  497. );
  498. $uniqid = uniqid();
  499. $sandbox = _PS_CACHE_DIR_.'sandbox'.DIRECTORY_SEPARATOR.$uniqid.DIRECTORY_SEPARATOR;
  500. mkdir($sandbox);
  501. file_put_contents($sandbox.(string)$addons_theme->name.'.zip', $zip_content);
  502. if ($this->extractTheme($sandbox.(string)$addons_theme->name.'.zip', $sandbox))
  503. if ($theme_directory = $this->installTheme(Theme::UPLOADED_THEME_DIR_NAME, $sandbox, false))
  504. $ids_themes[$theme_directory] = (string)$addons_theme->id;
  505. Tools::deleteDirectory($sandbox);
  506. }
  507. Configuration::updateValue('PS_ADDONS_THEMES_IDS', serialize($ids_themes));
  508. }
  509. }
  510. }
  511. public function processAdd()
  512. {
  513. if (Tools::getValue('directory') == '' || Tools::getValue('name') == '')
  514. {
  515. $this->errors[] = $this->l('Form invalid');
  516. $this->display = 'form';
  517. return false;
  518. }
  519. if (($new_dir = Tools::getValue('directory')) != '')
  520. {
  521. if (!Validate::isDirName($new_dir))
  522. {
  523. $this->display = 'add';
  524. return !($this->errors[] = sprintf(Tools::displayError('"%s" is not a valid directory name'), $new_dir));
  525. }
  526. if (Theme::getByDirectory($new_dir))
  527. {
  528. $this->display = 'add';
  529. return !($this->errors[] = Tools::displayError('A directory with this name already exists.'));
  530. }
  531. if (mkdir(_PS_ALL_THEMES_DIR_.$new_dir, Theme::$access_rights))
  532. $this->confirmations[] = $this->l('The directory was successfully created.');
  533. if (0 !== $id_based = (int)Tools::getValue('based_on'))
  534. {
  535. $base_theme = new Theme($id_based);
  536. $this->copyTheme($base_theme->directory, $new_dir);
  537. $base_theme = new Theme((int)Tools::getValue('based_on'));
  538. }
  539. if (isset($_FILES['image_preview']) && $_FILES['image_preview']['error'] == 0)
  540. {
  541. if (@getimagesize($_FILES['image_preview']['tmp_name']) && !ImageManager::validateUpload($_FILES['image_preview'], Tools::getMaxUploadSize()))
  542. move_uploaded_file($_FILES['image_preview']['tmp_name'], _PS_ALL_THEMES_DIR_.$new_dir.'/preview.jpg');
  543. else
  544. {
  545. $this->errors[] = $this->l('Image is not valid.');
  546. $this->display = 'form';
  547. return false;
  548. }
  549. }
  550. }
  551. $theme = parent::processAdd();
  552. if ((int)$theme->product_per_page == 0)
  553. {
  554. $theme->product_per_page = 1;
  555. $theme->save();
  556. }
  557. if (is_object($theme) && (int)$theme->id > 0)
  558. {
  559. $metas = Meta::getMetas();
  560. foreach ($metas as &$meta)
  561. {
  562. $meta['left'] = $theme->default_left_column;
  563. $meta['right'] = $theme->default_right_column;
  564. }
  565. $theme->updateMetas($metas, true);
  566. }
  567. return $theme;
  568. }
  569. public function processUpdate()
  570. {
  571. if (Tools::getIsset('id_theme') && Tools::getIsset('name') && Tools::getIsset('directory'))
  572. {
  573. $theme = New Theme((int)Tools::getValue('id_theme'));
  574. $theme->name = Tools::getValue('name');
  575. $theme->directory = Tools::getValue('directory');
  576. $theme->default_left_column = Tools::getValue('default_left_column');
  577. $theme->default_right_column = Tools::getValue('default_right_column');
  578. $nb_product_per_page = (int)Tools::getValue('product_per_page');
  579. if ($nb_product_per_page == 0)
  580. $nb_product_per_page = 1;
  581. $theme->product_per_page = $nb_product_per_page;
  582. if ($this->context->shop->id_theme == (int)Tools::getValue('id_theme'))
  583. Configuration::updateValue('PS_PRODUCTS_PER_PAGE', $nb_product_per_page);
  584. if (isset($_FILES['image_preview']) && $_FILES['image_preview']['error'] == 0)
  585. {
  586. if (@getimagesize($_FILES['image_preview']['tmp_name']) && !ImageManager::validateUpload($_FILES['image_preview'], 300000))
  587. move_uploaded_file($_FILES['image_preview']['tmp_name'], _PS_ALL_THEMES_DIR_.$theme->directory.'/preview.jpg');
  588. else
  589. {
  590. $this->errors[] = $this->l('Image is not valid.');
  591. $this->display = 'form';
  592. return false;
  593. }
  594. }
  595. $theme->update();
  596. }
  597. Tools::redirectAdmin(Context::getContext()->link->getAdminLink('AdminThemes').'&conf=29');
  598. }
  599. protected function processUpdateOptions()
  600. {
  601. parent::processUpdateOptions();
  602. if (!count($this->errors))
  603. Tools::redirectAdmin(Context::getContext()->link->getAdminLink('AdminThemes').'&conf=6');
  604. }
  605. public function processDelete()
  606. {
  607. $obj = $this->loadObject();
  608. if ($obj)
  609. {
  610. if ($obj->isUsed())
  611. {
  612. $this->errors[] = $this->l('The theme is being used by at least one shop. Please choose another theme before continuing.');
  613. return false;
  614. }
  615. $themes = array();
  616. foreach (Theme::getThemes() as $theme)
  617. {
  618. if ($theme->id != $obj->id)
  619. $themes[] = $theme->directory;
  620. }
  621. if (is_dir(_PS_ALL_THEMES_DIR_.$obj->directory) && !in_array($obj->directory, $themes))
  622. Tools::deleteDirectory(_PS_ALL_THEMES_DIR_.$obj->directory.'/');
  623. $ids_themes = Tools::unSerialize(Configuration::get('PS_ADDONS_THEMES_IDS'));
  624. if (array_key_exists($obj->directory, $ids_themes))
  625. unset($ids_themes[$obj->directory]);
  626. $obj->removeMetas();
  627. }
  628. elseif ($obj === false && $theme_dir = Tools::getValue('theme_dir'))
  629. {
  630. $theme_dir = basename($theme_dir);
  631. if (Tools::deleteDirectory(_PS_ALL_THEMES_DIR_.$theme_dir.'/'))
  632. Tools::redirectAdmin(Context::getContext()->link->getAdminLink('AdminThemes').'&conf=2');
  633. else
  634. $this->errors[] = Tools::displayError('The folder cannot be deleted');
  635. }
  636. return parent::processDelete();
  637. }
  638. public function initPageHeaderToolbar()
  639. {
  640. parent::initPageHeaderToolbar();
  641. if (empty($this->display))
  642. {
  643. $this->page_header_toolbar_btn['import_theme'] = array(
  644. 'href' => self::$currentIndex.'&action=importtheme&token='.$this->token,
  645. 'desc' => $this->l('Add new theme', null, null, false),
  646. 'icon' => 'process-icon-new'
  647. );
  648. if ($this->context->mode)
  649. unset($this->toolbar_btn['new']);
  650. $this->page_header_toolbar_btn['export_theme'] = array(
  651. 'href' => self::$currentIndex.'&action=exporttheme&token='.$this->token,
  652. 'desc' => $this->l('Export theme', null, null, false),
  653. 'icon' => 'process-icon-export'
  654. );
  655. }
  656. if ($this->display == 'importtheme')
  657. $this->toolbar_title[] = $this->l('Import theme');
  658. elseif ($this->display == 'exporttheme')
  659. $this->toolbar_title[] = $this->l('Export theme');
  660. else
  661. $this->toolbar_title[] = $this->l('Theme');
  662. $title = implode(' '.Configuration::get('PS_NAVIGATION_PIPE').' ', $this->toolbar_title);
  663. $this->page_header_toolbar_title = $title;
  664. }
  665. private function checkParentClass($name)
  666. {
  667. if (!$obj = Module::getInstanceByName($name))
  668. return false;
  669. if (is_callable(array($obj, 'validateOrder')))
  670. return false;
  671. if (is_callable(array($obj, 'getDateBetween')))
  672. return false;
  673. if (is_callable(array($obj, 'getGridEngines')))
  674. return false;
  675. if (is_callable(array($obj, 'getGraphEngines')))
  676. return false;
  677. if (is_callable(array($obj, 'hookAdminStatsModules')))
  678. return false;
  679. else
  680. return true;
  681. }
  682. private function checkNames()
  683. {
  684. $author = Tools::getValue('name');
  685. $theme_name = Tools::getValue('theme_name');
  686. if (!$author || !Validate::isGenericName($author) || strlen($author) > self::MAX_NAME_LENGTH)
  687. $this->errors[] = $this->l('Please enter a valid author name');
  688. elseif (!$theme_name || !Validate::isGenericName($theme_name) || strlen($theme_name) > self::MAX_NAME_LENGTH)
  689. $this->errors[] = $this->l('Please enter a valid theme name');
  690. if (count($this->errors) > 0)
  691. return false;
  692. return true;
  693. }
  694. private function checkDocumentation()
  695. {
  696. $extensions = array(
  697. '.pdf',
  698. '.txt'
  699. );
  700. if (isset($_FILES['documentation']) && $_FILES['documentation']['name'] != '')
  701. {
  702. $extension = strrchr($_FILES['documentation']['name'], '.');
  703. $name = Tools::getValue('documentationName');
  704. if (!in_array($extension, $extensions))
  705. $this->errors[] = $this->l('File extension must be .txt or .pdf');
  706. elseif ($_FILES['documentation']['error'] > 0 || $_FILES['documentation']['size'] > 1048576)
  707. $this->errors[] = $this->l('An error occurred during documentation upload');
  708. elseif (!$name || !Validate::isGenericName($name) || strlen($name) > self::MAX_NAME_LENGTH)
  709. $this->errors[] = $this->l('Please enter a valid documentation name');
  710. }
  711. if (count($this->errors) > 0)
  712. return false;
  713. return true;
  714. }
  715. private function checkVersionsAndCompatibility()
  716. {
  717. $exp = '#^[0-9]+[.]+[0-9.]*[0-9]$#';
  718. if (!preg_match('#^[0-9][.][0-9]$#', Tools::getValue('theme_version')) ||
  719. !preg_match($exp, Tools::getValue('compa_from')) || !preg_match($exp, Tools::getValue('compa_to')) ||
  720. version_compare(Tools::getValue('compa_from'), Tools::getValue('compa_to')) == 1
  721. )
  722. $this->errors[] = $this->l('Syntax error on version field. Only digits and periods (.) are allowed, and the compatibility version should be increasing or at least be equal to the previous version.');
  723. if (count($this->errors) > 0)
  724. return false;
  725. return true;
  726. }
  727. private function checkPostedDatas()
  728. {
  729. $mail = Tools::getValue('email');
  730. $website = Tools::getValue('website');
  731. if ($mail && !preg_match('#^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,6}$#', $mail))
  732. $this->errors[] = $this->l('There is an error in your email syntax!');
  733. elseif ($website && (!Validate::isURL($website) || !Validate::isAbsoluteUrl($website)))
  734. $this->errors[] = $this->l('There is an error in your URL syntax!');
  735. elseif (!$this->checkVersionsAndCompatibility() || !$this->checkNames() || !$this->checkDocumentation())
  736. return false;
  737. else
  738. return true;
  739. return false;
  740. }
  741. private function archiveThisFile($obj, $file, $server_path, $archive_path)
  742. {
  743. if (is_dir($server_path.$file))
  744. {
  745. $dir = scandir($server_path.$file);
  746. foreach ($dir as $row)
  747. {
  748. if ($row != '.' && $row != '..')
  749. $this->archiveThisFile($obj, $row, $server_path.$file.'/', $archive_path.$file.'/');
  750. }
  751. }
  752. elseif (!$obj->addFile($server_path.$file, $archive_path.$file))
  753. $this->error = true;
  754. }
  755. private function generateArchive()
  756. {
  757. $zip = new ZipArchive();
  758. $zip_file_name = md5(time()).'.zip';
  759. if ($zip->open(_PS_CACHE_DIR_.$zip_file_name, ZipArchive::OVERWRITE) === true)
  760. {
  761. if (!$zip->addFromString('Config.xml', $this->xml_file))
  762. $this->errors[] = $this->l('Cannot create config file.');
  763. if (isset($_FILES['documentation']))
  764. if (!empty($_FILES['documentation']['tmp_name']) &&
  765. !empty($_FILES['documentation']['name']) &&
  766. !$zip->addFile($_FILES['documentation']['tmp_name'], 'doc/'.$_FILES['documentation']['name']))
  767. $this->errors[] = $this->l('Cannot copy documentation.');
  768. $given_path = realpath(_PS_ALL_THEMES_DIR_.Tools::getValue('theme_directory'));
  769. if ($given_path !== false)
  770. {
  771. $ps_all_theme_dir_lenght = strlen(realpath(_PS_ALL_THEMES_DIR_));
  772. $to_compare_path = substr($given_path, 0, $ps_all_theme_dir_lenght);
  773. if ($to_compare_path != realpath(_PS_ALL_THEMES_DIR_))
  774. $this->errors[] = $this->l('Wrong theme directory path');
  775. else
  776. {
  777. $this->archiveThisFile($zip, Tools::getValue('theme_directory'), _PS_ALL_THEMES_DIR_, 'themes/');
  778. foreach ($this->to_export as $row)
  779. {
  780. if (!in_array($row, $this->native_modules))
  781. $this->archiveThisFile($zip, $row, _PS_ROOT_DIR_.'/modules/', 'modules/');
  782. }
  783. }
  784. }
  785. else
  786. $this->errors[] = $this->l('Wrong theme directory path');
  787. $zip->close();
  788. if (!$this->errors)
  789. {
  790. if (ob_get_length() > 0)
  791. ob_end_clean();
  792. ob_start();
  793. header('Pragma: public');
  794. header('Expires: 0');
  795. header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  796. header('Cache-Control: public');
  797. header('Content-Description: File Transfer');
  798. header('Content-type: application/octet-stream');
  799. header('Content-Disposition: attachment; filename="'.$zip_file_name.'"');
  800. header('Content-Transfer-Encoding: binary');
  801. ob_end_flush();
  802. readfile(_PS_CACHE_DIR_.$zip_file_name);
  803. @unlink(_PS_CACHE_DIR_.$zip_file_name);
  804. exit;
  805. }
  806. }
  807. $this->errors[] = $this->l('An error occurred during the archive generation');
  808. }
  809. private function generateXML($theme_to_export, $metas)
  810. {
  811. $theme = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><!-- Copyright PrestaShop --><theme></theme>');
  812. $theme->addAttribute('version', Tools::getValue('theme_version'));
  813. $theme->addAttribute('name', Tools::htmlentitiesUTF8(Tools::getValue('theme_name')));
  814. $theme->addAttribute('directory', Tools::htmlentitiesUTF8(Tools::getValue('theme_directory')));
  815. $author = $theme->addChild('author');
  816. $author->addAttribute('name', Tools::htmlentitiesUTF8(Tools::getValue('name')));
  817. $author->addAttribute('email', Tools::htmlentitiesUTF8(Tools::getValue('email')));
  818. $author->addAttribute('url', Tools::htmlentitiesUTF8(Tools::getValue('website')));
  819. $descriptions = $theme->addChild('descriptions');
  820. $languages = Language::getLanguages();
  821. foreach ($languages as $language)
  822. {
  823. $val = Tools::htmlentitiesUTF8(Tools::getValue('body_title_'.$language['id_lang']));
  824. $description = $descriptions->addChild('description', Tools::htmlentitiesUTF8($val));
  825. $description->addAttribute('iso', $language['iso_code']);
  826. }
  827. $variations = $theme->addChild('variations');
  828. $variation = $variations->addChild('variation');
  829. $variation->addAttribute('name', Tools::htmlentitiesUTF8(Tools::getValue('theme_name')));
  830. $variation->addAttribute('directory', Tools::getValue('theme_directory'));
  831. $variation->addAttribute('responsive', $theme_to_export->responsive);
  832. $variation->addAttribute('default_left_column', $theme_to_export->default_left_column);
  833. $variation->addAttribute('default_right_column', $theme_to_export->default_right_column);
  834. $variation->addAttribute('product_per_page', $theme_to_export->product_per_page);
  835. $variation->addAttribute('from', Tools::getValue('compa_from'));
  836. $variation->addAttribute('to', Tools::getValue('compa_to'));
  837. $docs = $theme->addChild('docs');
  838. if (isset($this->user_doc))
  839. foreach ($this->user_doc as $row)
  840. {
  841. $array = explode('¤', $row);
  842. $doc = $docs->addChild('doc');
  843. $doc->addAttribute('name', $array[0]);
  844. $doc->addAttribute('path', $array[1]);
  845. }
  846. $metas_xml = $theme->addChild('metas');
  847. foreach ($metas as $row)
  848. {
  849. $meta_obj = New Meta((int)$row['id_meta']);
  850. $meta_xml = $metas_xml->addChild('meta');
  851. $meta_xml->addAttribute('meta_page', $meta_obj->page);
  852. $meta_xml->addAttribute('left', $row['left_column']);
  853. $meta_xml->addAttribute('right', $row['right_column']);
  854. }
  855. $modules = $theme->addChild('modules');
  856. if (isset($this->to_export))
  857. foreach ($this->to_export as $row)
  858. {
  859. if (!in_array($row, $this->native_modules))
  860. {
  861. $module = $modules->addChild('module');
  862. $module->addAttribute('action', 'install');
  863. $module->addAttribute('name', $row);
  864. }
  865. }
  866. foreach ($this->to_enable as $row)
  867. {
  868. $module = $modules->addChild('module');
  869. $module->addAttribute('action', 'enable');
  870. $module->addAttribute('name', $row);
  871. }
  872. foreach ($this->to_disable as $row)
  873. {
  874. $module = $modules->addChild('module');
  875. $module->addAttribute('action', 'disable');
  876. $module->addAttribute('name', $row);
  877. }
  878. $hooks = $modules->addChild('hooks');
  879. foreach ($this->to_hook as $row)
  880. {
  881. $array = explode(';', $row);
  882. $hook = $hooks->addChild('hook');
  883. $hook->addAttribute('module', $array[0]);
  884. $hook->addAttribute('hook', $array[1]);
  885. $hook->addAttribute('position', $array[2]);
  886. if (!empty($array[3]))
  887. $hook->addAttribute('exceptions', $array[3]);
  888. }
  889. $images = $theme->addChild('images');
  890. foreach ($this->image_list as $row)
  891. {
  892. $array = explode(';', $row);
  893. $image = $images->addChild('image');
  894. $image->addAttribute('name', Tools::htmlentitiesUTF8($array[0]));
  895. $image->addAttribute('width', $array[1]);
  896. $image->addAttribute('height', $array[2]);
  897. $image->addAttribute('products', $array[3]);
  898. $image->addAttribute('categories', $array[4]);
  899. $image->addAttribute('manufacturers', $array[5]);
  900. $image->addAttribute('suppliers', $array[6]);
  901. $image->addAttribute('scenes', $array[7]);
  902. }
  903. $this->xml_file = $theme->asXML();
  904. }
  905. public function processExportTheme()
  906. {
  907. if (Tools::isSubmit('name'))
  908. {
  909. if ($this->checkPostedDatas())
  910. {
  911. $filename = Tools::htmlentitiesUTF8($_FILES['documentation']['name']);
  912. $name = Tools::htmlentitiesUTF8(Tools::getValue('documentationName'));
  913. $this->user_doc = array($name.'¤doc/'.$filename);
  914. $table = Db::getInstance()->executeS('
  915. SELECT name, width, height, products, categories, manufacturers, suppliers, scenes
  916. FROM `'._DB_PREFIX_.'image_type`');
  917. $this->image_list = array();
  918. foreach ($table as $row)
  919. {
  920. $this->image_list[] = $row['name'].';'.$row['width'].';'.$row['height'].';'.
  921. ($row['products'] == 1 ? 'true' : 'false').';'.
  922. ($row['categories'] == 1 ? 'true' : 'false').';'.
  923. ($row['manufacturers'] == 1 ? 'true' : 'false').';'.
  924. ($row['suppliers'] == 1 ? 'true' : 'false').';'.
  925. ($row['scenes'] == 1 ? 'true' : 'false');
  926. }
  927. $id_shop = Db::getInstance()->getValue('SELECT `id_shop` FROM `'._DB_PREFIX_.'shop` WHERE `id_theme` = '.(int)Tools::getValue('id_theme_export'));
  928. // Select the list of module for this shop
  929. $this->module_list = Db::getInstance()->executeS('
  930. SELECT m.`id_module`, m.`name`, m.`active`, ms.`id_shop`
  931. FROM `'._DB_PREFIX_.'module` m
  932. LEFT JOIN `'._DB_PREFIX_.'module_shop` ms On (m.`id_module` = ms.`id_module`)
  933. WHERE ms.`id_shop` = '.(int)$id_shop.'
  934. ');
  935. // Select the list of hook for this shop
  936. $this->hook_list = Db::getInstance()->executeS('
  937. SELECT h.`id_hook`, h.`name` as name_hook, hm.`position`, hm.`id_module`, m.`name` as name_module, GROUP_CONCAT(hme.`file_name`, ",") as exceptions
  938. FROM `'._DB_PREFIX_.'hook` h
  939. LEFT JOIN `'._DB_PREFIX_.'hook_module` hm ON hm.`id_hook` = h.`id_hook`
  940. LEFT JOIN `'._DB_PREFIX_.'module` m ON hm.`id_module` = m.`id_module`
  941. LEFT OUTER JOIN `'._DB_PREFIX_.'hook_module_exceptions` hme ON (hme.`id_module` = hm.`id_module` AND hme.`id_hook` = h.`id_hook`)
  942. WHERE hm.`id_shop` = '.(int)$id_shop.'
  943. GROUP BY `id_module`, `id_hook`
  944. ORDER BY `name_module`
  945. ');
  946. $this->native_modules = $this->getNativeModule();
  947. foreach ($this->hook_list as &$row)
  948. $row['exceptions'] = trim(preg_replace('/(,,+)/', ',', $row['exceptions']), ',');
  949. $this->to_install = array();
  950. $this->to_enable = array();
  951. $this->to_hook = array();
  952. foreach ($this->module_list as $array)
  953. {
  954. if (!self::checkParentClass($array['name']))
  955. continue;
  956. if (in_array($array['name'], $this->native_modules))
  957. {
  958. if ($array['active'] == 1)
  959. $this->to_enable[] = $array['name'];
  960. else
  961. $this->to_disable[] = $array['name'];
  962. }
  963. elseif ($array['active'] == 1)
  964. $this->to_install[] = $array['name'];
  965. }
  966. foreach ($this->native_modules as $str)
  967. {
  968. $flag = 0;
  969. if (!self::checkParentClass($str))
  970. continue;
  971. foreach ($this->module_list as $tmp)
  972. {
  973. if (in_array($str, $tmp))
  974. {
  975. $flag = 1;
  976. break;
  977. }
  978. }
  979. if ($flag == 0)
  980. $this->to_disable[] = $str;
  981. }
  982. foreach ($_POST as $key => $value)
  983. if (strncmp($key, 'modulesToExport_module', strlen('modulesToExport_module')) == 0)
  984. $this->to_export[] = $value;
  985. if ($this->to_install)
  986. foreach ($this->to_install as $string)
  987. {
  988. foreach ($this->hook_list as $tmp)
  989. {
  990. if ($tmp['name_module'] == $string)
  991. $this->to_hook[] = $string.';'.$tmp['name_hook'].';'.$tmp['position'].';'.$tmp['exceptions'];
  992. }
  993. }
  994. if ($this->to_enable)
  995. foreach ($this->to_enable as $string)
  996. {
  997. foreach ($this->hook_list as $tmp)
  998. {
  999. if ($tmp['name_module'] == $string)
  1000. $this->to_hook[] = $string.';'.$tmp['name_hook'].';'.$tmp['position'].';'.$tmp['exceptions'];
  1001. }
  1002. }
  1003. $theme_to_export = New Theme((int)Tools::getValue('id_theme_export'));
  1004. $metas = $theme_to_export->getMetas();
  1005. $this->generateXML($theme_to_export, $metas);
  1006. $this->generateArchive();
  1007. }
  1008. else
  1009. $this->display = 'exporttheme';
  1010. }
  1011. else
  1012. $this->display = 'exporttheme';
  1013. }
  1014. private function renderExportTheme1()
  1015. {
  1016. $to_install = array();
  1017. $module_list = Db::getInstance()->executeS('
  1018. SELECT m.`id_module`, m.`name`, m.`active`, ms.`id_shop`
  1019. FROM `'._DB_PREFIX_.'module` m
  1020. LEFT JOIN `'._DB_PREFIX_.'module_shop` ms On (m.`id_module` = ms.`id_module`)
  1021. WHERE ms.`id_shop` = '.(int)$this->context->shop->id.'
  1022. ');
  1023. // Select the list of hook for this shop
  1024. $hook_list = Db::getInstance()->executeS('
  1025. SELECT h.`id_hook`, h.`name` as name_hook, hm.`position`, hm.`id_module`, m.`name` as name_module, GROUP_CONCAT(hme.`file_name`, ",") as exceptions
  1026. FROM `'._DB_PREFIX_.'hook` h
  1027. LEFT JOIN `'._DB_PREFIX_.'hook_module` hm ON hm.`id_hook` = h.`id_hook`
  1028. LEFT JOIN `'._DB_PREFIX_.'module` m ON hm.`id_module` = m.`id_module`
  1029. LEFT OUTER JOIN `'._DB_PREFIX_.'hook_module_exceptions` hme ON (hme.`id_module` = hm.`id_module` AND hme.`id_hook` = h.`id_hook`)
  1030. WHERE hm.`id_shop` = '.(int)$this->context->shop->id.'
  1031. GROUP BY `id_module`, `id_hook`
  1032. ORDER BY `name_module`
  1033. ');
  1034. foreach ($hook_list as &$row)
  1035. $row['exceptions'] = trim(preg_replace('/(,,+)/', ',', $row['exceptions']), ',');
  1036. $native_modules = $this->getNativeModule();
  1037. foreach ($module_list as $array)
  1038. {
  1039. if (!self::checkParentClass($array['name']))
  1040. continue;
  1041. if (in_array($array['name'], $native_modules))
  1042. {
  1043. if ($array['active'] == 1)
  1044. $to_enable[] = $array['name'];
  1045. else
  1046. $to_disable[] = $array['name'];
  1047. }
  1048. elseif ($array['active'] == 1)
  1049. $to_install[] = $array['name'];
  1050. }
  1051. foreach ($native_modules as $str)
  1052. {
  1053. $flag = 0;
  1054. if (!$this->checkParentClass($str))
  1055. continue;
  1056. foreach ($module_list as $tmp)
  1057. {
  1058. if (in_array($str, $tmp))
  1059. {
  1060. $flag = 1;
  1061. break;
  1062. }
  1063. }
  1064. if ($flag == 0)
  1065. $to_disable[] = $str;
  1066. }
  1067. $employee = $this->context->employee;
  1068. $mail = Tools::getValue('email') ? Tools::getValue('email') : $employee->email;
  1069. $author = Tools::getValue('author_name') ? Tools::getValue('author_name') : $employee->firstname.' '.$employee->lastname;
  1070. $website = Tools::getValue('website') ? Tools::getValue('website') : Tools::getHttpHost(true);
  1071. $this->formatHelperArray($to_install);
  1072. $theme = New Theme(Tools::getValue('id_theme_export'));
  1073. $fields_form = array(
  1074. 'form' => array(
  1075. 'tinymce' => false,
  1076. 'legend' => array(
  1077. 'title' => $this->l('Theme configuration'),
  1078. 'icon' => 'icon-picture'
  1079. ),
  1080. 'input' => array(
  1081. array(
  1082. 'type' => 'hidden',
  1083. 'name' => 'id_theme_export'
  1084. ),
  1085. array(
  1086. 'type' => 'text',
  1087. 'name' => 'name',
  1088. 'label' => $this->l('Name'),
  1089. ),
  1090. array(
  1091. 'type' => 'text',
  1092. 'name' => 'email',
  1093. 'label' => $this->l('Email'),
  1094. ),
  1095. array(
  1096. 'type' => 'text',
  1097. 'name' => 'website',
  1098. 'label' => $this->l('Website'),
  1099. ),
  1100. array(
  1101. 'type' => 'text',
  1102. 'name' => 'theme_name',
  1103. 'label' => $this->l('Theme name'),
  1104. ),
  1105. array(
  1106. 'type' => 'text',
  1107. 'name' => 'theme_directory',
  1108. 'label' => $this->l('Theme directory'),
  1109. ),
  1110. array(
  1111. 'type' => 'text',
  1112. 'name' => 'body_title',
  1113. 'lang' => true,
  1114. 'label' => $this->l('Description'),
  1115. ),
  1116. array(
  1117. 'type' => 'text',
  1118. 'name' => 'theme_version',
  1119. 'label' => $this->l('Theme version'),
  1120. ),
  1121. array(
  1122. 'type' => 'text',
  1123. 'name' => 'compa_from',
  1124. 'label' => $this->l('Compatible from'),
  1125. ),
  1126. array(
  1127. 'type' => 'text',
  1128. 'name' => 'compa_to',
  1129. 'label' => $this->l('Compatible to'),
  1130. ),
  1131. array(
  1132. 'type' => 'file',
  1133. 'name' => 'documentation',
  1134. 'label' => $this->l('Documentation'),
  1135. ),
  1136. array(
  1137. 'type' => 'text',
  1138. 'name' => 'documentationName',
  1139. 'label' => $this->l('Documentation name'),
  1140. ),
  1141. ),
  1142. 'submit' => array(
  1143. 'title' => $this->l('Save'),
  1144. )
  1145. )
  1146. );
  1147. if (count($to_install) > 0)
  1148. {
  1149. $fields_form['form']['input'][] = array(
  1150. 'type' => 'checkbox',
  1151. 'label' => $this->l('Select the theme\'s modules that you wish to export'),
  1152. 'values' => array(
  1153. 'query' => $this->formatHelperArray($to_install),
  1154. 'id' => 'id',
  1155. 'name' => 'name'
  1156. ),
  1157. 'name' => 'modulesToExport',
  1158. );
  1159. }
  1160. $default_language = (int)$this->context->language->id;
  1161. $languages = $this->getLanguages();
  1162. foreach ($languages as $language)
  1163. $fields_value['body_title'][$language['id_lang']] = '';
  1164. $helper = new HelperForm();
  1165. $helper->languages = $languages;
  1166. $helper->default_form_language = $default_language;
  1167. $fields_value['name'] = $author;
  1168. $fields_value['email'] = $mail;
  1169. $fields_value['website'] = $website;
  1170. $fields_value['theme_name'] = $theme->name;
  1171. $fields_value['theme_directory'] = $theme->directory;
  1172. $fields_value['theme_version'] = '1.0';
  1173. $fields_value['compa_from'] = _PS_VERSION_;
  1174. $fields_value['compa_to'] = _PS_VERSION_;
  1175. $fields_value['id_theme_export'] = Tools::getValue('id_theme_export');
  1176. $fields_value['documentationName'] = $this->l('documentation');
  1177. $toolbar_btn['save'] = array(
  1178. 'href' => '',
  1179. 'desc' => $this->l('Save')
  1180. );
  1181. $helper->currentIndex = $this->context->link->getAdminLink('AdminThemes', false).'&action=exporttheme';
  1182. $helper->token = Tools::getAdminTokenLite('AdminThemes');
  1183. $helper->show_toolbar = true;
  1184. $helper->fields_value = $fields_value;
  1185. $helper->toolbar_btn = $toolbar_btn;
  1186. $helper->override_folder = $this->tpl_folder;
  1187. return $helper->generateForm(array($fields_form));
  1188. }
  1189. public function renderExportTheme()
  1190. {
  1191. if (Tools::getIsset('id_theme_export') && (int)Tools::getValue('id_theme_export') > 0)
  1192. return $this->renderExportTheme1();
  1193. $theme_list = Theme::getThemes();
  1194. $fields_form = array(
  1195. 'form' => array(
  1196. 'tinymce' => false,
  1197. 'legend' => array(
  1198. 'title' => $this->l('Theme'),
  1199. 'icon' => 'icon-picture'
  1200. ),
  1201. 'input' => array(
  1202. array(
  1203. 'type' => 'select',
  1204. 'name' => 'id_theme_export',
  1205. 'label' => $this->l('Choose the theme that you want to export'),
  1206. 'options' => array(
  1207. 'id' => 'id',
  1208. 'name' => 'name',
  1209. 'query' => $theme_list,
  1210. )
  1211. ),
  1212. ),
  1213. 'submit' => array(
  1214. 'title' => $this->l('Save'),
  1215. )
  1216. )
  1217. );
  1218. $toolbar_btn['save'] = array(
  1219. 'href' => '#',
  1220. 'desc' => $this->l('Export')
  1221. );
  1222. $fields_value['id_theme_export'] = array();
  1223. $helper = new HelperForm();
  1224. $helper->currentIndex = $this->context->link->getAdminLink('AdminThemes', false).'&action=exporttheme';
  1225. $helper->token = Tools::getAdminTokenLite('AdminThemes');
  1226. $helper->show_toolbar = true;
  1227. $helper->fields_value = $fields_value;
  1228. $helper->toolbar_btn = $toolbar_btn;
  1229. $helper->override_folder = $this->tpl_folder;
  1230. return $helper->generateForm(array($fields_form));
  1231. }
  1232. private function checkXmlFields($xml_file)
  1233. {
  1234. if (!file_exists($xml_file) || !$xml = simplexml_load_file($xml_file))
  1235. return false;
  1236. if (!$xml['version'] || !$xml['name'])
  1237. return false;
  1238. foreach ($xml->variations->variation as $val)
  1239. {
  1240. if (!$val['name'] || !$val['directory'] || !$val['from'] || !$val['to'])
  1241. return false;
  1242. }
  1243. foreach ($xml->modules->module as $val)
  1244. {
  1245. if (!$val['action'] || !$val['name'])
  1246. return false;
  1247. }
  1248. foreach ($xml->modules->hooks->hook as $val)
  1249. {
  1250. if (!$val['module'] || !$val['hook'] || !$val['position'])
  1251. return false;
  1252. }
  1253. return true;
  1254. }
  1255. private function recurseCopy($src, $dst)
  1256. {
  1257. if (!$dir = opendir($src))
  1258. return;
  1259. if (!file_exists($dst))
  1260. mkdir($dst);
  1261. while (($file = readdir($dir)) !== false)
  1262. {
  1263. if (strncmp($file, '.', 1) != 0)
  1264. {
  1265. if (is_dir($src.'/'.$file))
  1266. self::recurseCopy($src.'/'.$file, $dst.'/'.$file);
  1267. elseif (is_readable($src.'/'.$file) && $file != 'Thumbs.db' && $file != '.DS_Store' && substr($file, -1) != '~')
  1268. copy($src.'/'.$file, $dst.'/'.$file);
  1269. }
  1270. }
  1271. closedir($dir);
  1272. }
  1273. public function processImportTheme()
  1274. {
  1275. $this->display = 'importtheme';
  1276. if ($this->context->mode == Context::MODE_HOST)
  1277. return true;
  1278. if (isset($_FILES['themearchive']) && isset($_POST['filename']) && Tools::isSubmit('theme_archive_server'))
  1279. {
  1280. $uniqid = uniqid();
  1281. $sandbox = _PS_CACHE_DIR_.'sandbox'.DIRECTORY_SEPARATOR.$uniqid.DIRECTORY_SEPARATOR;
  1282. mkdir($sandbox);
  1283. $archive_uploaded = false;
  1284. if (Tools::getValue('filename') != '')
  1285. {
  1286. $uploader = new Uploader('themearchive');
  1287. $uploader->setCheckFileSize(false);
  1288. $uploader->setAcceptTypes(array('zip'));
  1289. $uploader->setSavePath($sandbox);
  1290. $file = $uploader->process(Theme::UPLOADED_THEME_DIR_NAME.'.zip');
  1291. if ($file[0]['error'] === 0)
  1292. {
  1293. if (Tools::ZipTest($sandbox.Theme::UPLOADED_THEME_DIR_NAME.'.zip'))
  1294. $archive_uploaded = true;
  1295. else
  1296. $this->errors[] = $this->l('Zip file seems to be broken');
  1297. }
  1298. else
  1299. $this->errors[] = $file[0]['error'];
  1300. }
  1301. elseif (Tools::getValue('themearchiveUrl') != '')
  1302. {
  1303. if (!Validate::isModuleUrl($url = Tools::getValue('themearchiveUrl'), $this->errors))
  1304. $this->errors[] = $this->l('Only zip files are allowed');
  1305. elseif (!Tools::copy($url, $sandbox.Theme::UPLOADED_THEME_DIR_NAME.'.zip'))
  1306. $this->errors[] = $this->l('Error during the file download');
  1307. elseif (Tools::ZipTest($sandbox.Theme::UPLOADED_THEME_DIR_NAME.'.zip'))
  1308. $archive_uploaded = true;
  1309. else
  1310. $this->errors[] = $this->l('Zip file seems to be broken');
  1311. }
  1312. elseif (Tools::getValue('theme_archive_server') != '')
  1313. {
  1314. $filename = _PS_ALL_THEMES_DIR_.Tools::getValue('theme_archive_server');
  1315. if (substr($filename, -4) != '.zip')
  1316. $this->errors[] = $this->l('Only zip files are allowed');
  1317. elseif (!copy($filename, $sandbox.Theme::UPLOADED_THEME_DIR_NAME.'.zip'))
  1318. $this->errors[] = $this->l('An error has occurred during the file copy.');
  1319. elseif (Tools::ZipTest($sandbox.Theme::UPLOADED_THEME_DIR_NAME.'.zip'))
  1320. $archive_uploaded = true;
  1321. else
  1322. $this->errors[] = $this->l('Zip file seems to be broken');
  1323. }
  1324. else
  1325. $this->errors[] = $this->l('You must upload or enter a location of your zip');
  1326. if ($archive_uploaded)
  1327. if ($this->extractTheme($sandbox.Theme::UPLOADED_THEME_DIR_NAME.'.zip', $sandbox))
  1328. $this->installTheme(Theme::UPLOADED_THEME_DIR_NAME, $sandbox);
  1329. Tools::deleteDirectory($sandbox);
  1330. if (count($this->errors) > 0)
  1331. $this->display = 'importtheme';
  1332. else
  1333. Tools::redirectAdmin(Context::getContext()->link->getAdminLink('AdminThemes').'&conf=18');
  1334. }
  1335. }
  1336. protected function extractTheme($theme_zip_file, $sandbox)
  1337. {
  1338. if (Tools::ZipExtract($theme_zip_file, $sandbox.Theme::UPLOADED_THEME_DIR_NAME.'/'))
  1339. return true;
  1340. $this->errors[] = $this->l('Error during zip extraction');
  1341. return false;
  1342. }
  1343. protected function installTheme($theme_dir, $sandbox = false, $redirect = true)
  1344. {
  1345. if (!$sandbox)
  1346. {
  1347. $uniqid = uniqid();
  1348. $sandbox = _PS_CACHE_DIR_.'sandbox'.DIRECTORY_SEPARATOR.$uniqid.DIRECTORY_SEPARATOR;
  1349. mkdir($sandbox);
  1350. Tools::recurseCopy(_PS_ALL_THEMES_DIR_.$theme_dir, $sandbox.$theme_dir);
  1351. }
  1352. $xml_file = $sandbox.$theme_dir.'/Config.xml';
  1353. if (!$this->checkXmlFields($xml_file))
  1354. $this->errors[] = $this->l('Bad configuration file');
  1355. else
  1356. {
  1357. $imported_theme = $this->importThemeXmlConfig(simplexml_load_file($xml_file));
  1358. foreach ($imported_theme as $theme)
  1359. {
  1360. if (Validate::isLoadedObject($theme))
  1361. {
  1362. if (!copy($sandbox.$theme_dir.'/Config.xml', _PS_ROOT_DIR_.'/config/xml/themes/'.$theme->directory.'.xml'))
  1363. $this->errors[] = $this->l('Can\'t copy configuration file');
  1364. $target_dir = _PS_ALL_THEMES_DIR_.$theme->directory;
  1365. if (file_exists($target_dir))
  1366. Tools::deleteDirectory($target_dir);
  1367. $theme_doc_dir = $target_dir.'/docs/';
  1368. if (file_exists($theme_doc_dir))
  1369. Tools::deleteDirectory($theme_doc_dir);
  1370. mkdir($target_dir);
  1371. mkdir($theme_doc_dir);
  1372. Tools::recurseCopy($sandbox.$theme_dir.'/themes/'.$theme->directory.'/', $target_dir.'/');
  1373. Tools::recurseCopy($sandbox.$theme_dir.'/doc/', $theme_doc_dir);
  1374. Tools::recurseCopy($sandbox.$theme_dir.'/modules/', _PS_MODULE_DIR_);
  1375. }
  1376. else
  1377. $this->errors[] = $theme;
  1378. }
  1379. }
  1380. Tools::deleteDirectory($sandbox);
  1381. if (!count($this->errors))
  1382. {
  1383. if ($redirect)
  1384. Tools::redirectAdmin(Context::getContext()->link->getAdminLink('AdminThemes').'&conf=18');
  1385. else
  1386. return true;
  1387. }
  1388. else
  1389. return false;
  1390. }
  1391. protected function isThemeInstalled($theme_name)
  1392. {
  1393. $themes = Theme::getThemes();
  1394. foreach ($themes as $theme_object)
  1395. {
  1396. if ($theme_object->name == $theme_name)
  1397. return true;
  1398. }
  1399. return false;
  1400. }
  1401. /**
  1402. * @param SimpleXMLElement $xml
  1403. * @param bool $theme_dir only used if the theme directory to import is already located on the shop
  1404. *
  1405. * @return array|string return array of themes on success, otherwise the error as a string is returned
  1406. */
  1407. protected function importThemeXmlConfig(SimpleXMLElement $xml, $theme_dir = false)
  1408. {
  1409. $attr = $xml->attributes();
  1410. $th_name = (string)$attr->name;
  1411. if ($this->isThemeInstalled($th_name))
  1412. return array(sprintf($this->l('Theme %s already installed.'), $th_name));
  1413. $new_theme_array = array();
  1414. foreach ($xml->variations->variation as $variation)
  1415. {
  1416. $name = strval($variation['name']);
  1417. $new_theme = new Theme();
  1418. $new_theme->name = $name;
  1419. $new_theme->directory = strval($variation['directory']);
  1420. if ($theme_dir)
  1421. {
  1422. $new_theme->name = $theme_dir;
  1423. $new_theme->directory = $theme_dir;
  1424. }
  1425. if ($this->isThemeInstalled($new_theme->name))
  1426. continue;
  1427. $new_theme->product_per_page = Configuration::get('PS_PRODUCTS_PER_PAGE');
  1428. if (isset($variation['product_per_page']))
  1429. $new_theme->product_per_page = intval($variation['product_per_page']);
  1430. $new_theme->responsive = false;
  1431. if (isset($variation['responsive']))
  1432. $new_theme->responsive = (bool)strval($variation['responsive']);
  1433. $new_theme->default_left_column = true;
  1434. $new_theme->default_right_column = true;
  1435. if (isset($variation['default_left_column']))
  1436. $new_theme->default_left_column = (bool)strval($variation['default_left_column']);
  1437. if (isset($variation['default_right_column']))
  1438. $new_theme->default_right_column = (bool)strval($variation['default_right_column']);
  1439. $fill_default_meta = true;
  1440. $metas_xml = array();
  1441. if ($xml->metas->meta)
  1442. {
  1443. foreach ($xml->metas->meta as $meta)
  1444. {
  1445. $meta_id = Db::getInstance()->getValue('SELECT id_meta FROM '._DB_PREFIX_.'meta WHERE page=\''.pSQL($meta['meta_page']).'\'');
  1446. if ((int)$meta_id > 0)
  1447. {
  1448. $tmp_meta = array();
  1449. $tmp_meta['id_meta'] = (int)$meta_id;
  1450. $tmp_meta['left'] = intval($meta['left']);
  1451. $tmp_meta['right'] = intval($meta['right']);
  1452. $metas_xml[(int)$meta_id] = $tmp_meta;
  1453. }
  1454. }
  1455. $fill_default_meta = false;
  1456. if (count($xml->metas->meta) < (int)Db::getInstance()->getValue('SELECT count(*) FROM '._DB_PREFIX_.'meta'))
  1457. $fill_default_meta = true;
  1458. }
  1459. if ($fill_default_meta == true)
  1460. {
  1461. $metas = Db::getInstance()->executeS('SELECT id_meta FROM '._DB_PREFIX_.'meta');
  1462. foreach ($metas as $meta)
  1463. {
  1464. if (!isset($metas_xml[(int)$meta['id_meta']]))
  1465. {
  1466. $tmp_meta['id_meta'] = (int)$meta['id_meta'];
  1467. $tmp_meta['left'] = $new_theme->default_left_column;
  1468. $tmp_meta['right'] = $new_theme->default_right_column;
  1469. $metas_xml[(int)$meta['id_meta']] = $tmp_meta;
  1470. }
  1471. }
  1472. }
  1473. if (!is_dir(_PS_ALL_THEMES_DIR_.$new_theme->directory))
  1474. if (!mkdir(_PS_ALL_THEMES_DIR_.$new_theme->directory))
  1475. return sprintf($this->l('Error while creating %s directory'), _PS_ALL_THEMES_DIR_.$new_theme->directory);
  1476. $new_theme->add();
  1477. if ($new_theme->id > 0)
  1478. {
  1479. $new_theme->updateMetas($metas_xml);
  1480. $new_theme_array[] = $new_theme;
  1481. }
  1482. else
  1483. $new_theme_array[] = sprintf($this->l('Error while installing theme %s'), $new_theme->name);
  1484. }
  1485. return $new_theme_array;
  1486. }
  1487. public function renderImportTheme()
  1488. {
  1489. $fields_form = array();
  1490. $toolbar_btn['save'] = array(
  1491. 'href' => '#',
  1492. 'desc' => $this->l('Save')
  1493. );
  1494. if ($this->context->mode != Context::MODE_HOST)
  1495. {
  1496. $fields_form[0] = array(
  1497. 'form' => array(
  1498. 'tinymce' => false,
  1499. 'legend' => array(
  1500. 'title' => $this->l('Import from your computer'),
  1501. 'icon' => 'icon-picture'
  1502. ),
  1503. 'input' => array(
  1504. array(
  1505. 'type' => 'file',
  1506. 'label' => $this->l('Zip file'),
  1507. 'desc' => $this->l('Browse your computer files and select the Zip file for your new theme.'),
  1508. 'name' => 'themearchive'
  1509. ),
  1510. ),
  1511. 'submit' => array(
  1512. 'id' => 'zip',
  1513. 'title' => $this->l('Save'),
  1514. )
  1515. ),
  1516. );
  1517. $fields_form[1] = array(
  1518. 'form' => array(
  1519. 'tinymce' => false,
  1520. 'legend' => array(
  1521. 'title' => $this->l('Import from the web'),
  1522. 'icon' => 'icon-picture'
  1523. ),
  1524. 'input' => array(
  1525. array(
  1526. 'type' => 'text',
  1527. 'label' => $this->l('Archive URL'),
  1528. 'desc' => $this->l('Indicate the complete URL to an online Zip file that contains your new theme. For instance, "http://example.com/files/theme.zip".'),
  1529. 'name' => 'themearchiveUrl'
  1530. ),
  1531. ),
  1532. 'submit' => array(
  1533. 'title' => $this->l('Save'),
  1534. )
  1535. ),
  1536. );
  1537. $theme_archive_server = array();
  1538. $files = scandir(_PS_ALL_THEMES_DIR_);
  1539. $theme_archive_server[] = '-';
  1540. foreach ($files as $file)
  1541. {
  1542. if (is_file(_PS_ALL_THEMES_DIR_.$file) && substr(_PS_ALL_THEMES_DIR_.$file, -4) == '.zip')
  1543. {
  1544. $theme_archive_server[] = array(
  1545. 'id' => basename(_PS_ALL_THEMES_DIR_.$file),
  1546. 'name' => basename(_PS_ALL_THEMES_DIR_.$file)
  1547. );
  1548. }
  1549. }
  1550. $fields_form[2] = array(
  1551. 'form' => array(
  1552. 'tinymce' => false,
  1553. 'legend' => array(
  1554. 'title' => $this->l('Import from FTP'),
  1555. 'icon' => 'icon-picture'
  1556. ),
  1557. 'input' => array(
  1558. array(
  1559. 'type' => 'select',
  1560. 'label' => $this->l('Select the archive'),
  1561. 'name' => 'theme_archive_server',
  1562. 'desc' => $this->l('This selector lists the Zip files that you uploaded in the \'/themes\' folder.'),
  1563. 'options' => array(
  1564. 'id' => 'id',
  1565. 'name' => 'name',
  1566. 'query' => $theme_archive_server,
  1567. )
  1568. ),
  1569. ),
  1570. 'submit' => array(
  1571. 'title' => $this->l('Save'),
  1572. )
  1573. ),
  1574. );
  1575. }
  1576. $this->context->smarty->assign(
  1577. array(
  1578. 'import_theme' => true,
  1579. 'logged_on_addons' => $this->logged_on_addons,
  1580. 'iso_code' => $this->context->language->iso_code,
  1581. 'add_new_theme_href' => self::$currentIndex.'&addtheme&token='.$this->token,
  1582. 'add_new_theme_label' => $this->l('Create a new theme'),
  1583. )
  1584. );
  1585. $create_new_theme_panel = $this->context->smarty->fetch('controllers/themes/helpers/view/importtheme_view.tpl');
  1586. $helper = new HelperForm();
  1587. $helper->currentIndex = $this->context->link->getAdminLink('AdminThemes', false).'&action=importtheme';
  1588. $helper->token = Tools::getAdminTokenLite('AdminThemes');
  1589. $helper->show_toolbar = true;
  1590. $helper->toolbar_btn = $toolbar_btn;
  1591. $helper->fields_value['themearchiveUrl'] = '';
  1592. $helper->fields_value['theme_archive_server'] = array();
  1593. $helper->multiple_fieldsets = true;
  1594. $helper->override_folder = $this->tpl_folder;
  1595. $helper->languages = $this->getLanguages();
  1596. $helper->default_form_language = (int)$this->context->language->id;
  1597. return $helper->generateForm($fields_form).$create_new_theme_panel;
  1598. }
  1599. public function initContent()
  1600. {
  1601. if ($this->display == 'list')
  1602. $this->display = '';
  1603. if (isset($this->display) && method_exists($this, 'render'.$this->display))
  1604. {
  1605. $this->content .= $this->initPageHeaderToolbar();
  1606. $this->content .= $this->{'render'.$this->display}();
  1607. $this->context->smarty->assign(array(
  1608. 'content' => $this->content,
  1609. 'show_page_header_toolbar' => $this->show_page_header_toolbar,
  1610. 'page_header_toolbar_title' => $this->page_header_toolbar_title,
  1611. 'page_header_toolbar_btn' => $this->page_header_toolbar_btn
  1612. ));
  1613. }
  1614. else
  1615. {
  1616. $content = '';
  1617. if (Configuration::hasKey('PS_LOGO') && trim(Configuration::get('PS_LOGO')) != ''
  1618. && file_exists(_PS_IMG_DIR_.Configuration::get('PS_LOGO')) && filesize(_PS_IMG_DIR_.Configuration::get('PS_LOGO')))
  1619. {
  1620. list($width, $height, $type, $attr) = getimagesize(_PS_IMG_DIR_.Configuration::get('PS_LOGO'));
  1621. Configuration::updateValue('SHOP_LOGO_HEIGHT', (int)round($height));
  1622. Configuration::updateValue('SHOP_LOGO_WIDTH', (int)round($width));
  1623. }
  1624. if (Configuration::get('PS_LOGO_MOBILE') && trim(Configuration::get('PS_LOGO_MOBILE')) != ''
  1625. && file_exists(_PS_IMG_DIR_.Configuration::get('PS_LOGO_MOBILE')) && filesize(_PS_IMG_DIR_.Configuration::get('PS_LOGO_MOBILE')))
  1626. {
  1627. list($width, $height, $type, $attr) = getimagesize(_PS_IMG_DIR_.Configuration::get('PS_LOGO_MOBILE'));
  1628. Configuration::updateValue('SHOP_LOGO_MOBILE_HEIGHT', (int)round($height));
  1629. Configuration::updateValue('SHOP_LOGO_MOBILE_WIDTH', (int)round($width));
  1630. }
  1631. $this->content .= $content;
  1632. return parent::initContent();
  1633. }
  1634. }
  1635. public function ajaxProcessGetAddonsThemes()
  1636. {
  1637. $parent_domain = Tools::getHttpHost(true).substr($_SERVER['REQUEST_URI'], 0, -1 * strlen(basename($_SERVER['REQUEST_URI'])));
  1638. $iso_lang = $this->context->language->iso_code;
  1639. $iso_currency = $this->context->currency->iso_code;
  1640. $iso_country = $this->context->country->iso_code;
  1641. $activity = Configuration::get('PS_SHOP_ACTIVITY');
  1642. $addons_url = 'http://addons.prestashop.com/iframe/search-1.6.php?psVersion='._PS_VERSION_.'&onlyThemes=1&isoLang='.$iso_lang.'&isoCurrency='.$iso_currency.'&isoCountry='.$iso_country.'&activity='.(int)$activity.'&parentUrl='.$parent_domain;
  1643. die(Tools::file_get_contents($addons_url));
  1644. }
  1645. /**
  1646. * This function checks if the theme designer has thunk to make his theme compatible 1.4,
  1647. * and noticed it on the $theme_dir/config.xml file. If not, some new functionnalities has
  1648. * to be desactivated
  1649. *
  1650. * @since 1.4
  1651. *
  1652. * @param string $theme_dir theme directory
  1653. *
  1654. * @return boolean Validity is ok or not
  1655. */
  1656. protected function _isThemeCompatible($theme_dir)
  1657. {
  1658. $return = true;
  1659. $check_version = AdminThemes::$check_features_version;
  1660. if (!is_file(_PS_ALL_THEMES_DIR_.$theme_dir.'/config.xml'))
  1661. {
  1662. $this->errors[] = Tools::displayError('The config.xml file is missing in your theme path.').'<br/>';
  1663. $xml = null;
  1664. }
  1665. else
  1666. {
  1667. $xml = @simplexml_load_file(_PS_ALL_THEMES_DIR_.$theme_dir.'/config.xml');
  1668. if (!$xml)
  1669. $this->errors[] = Tools::displayError('The config.xml file in your theme path is not a valid XML file.').'<br/>';
  1670. }
  1671. // will be set to false if any version node in xml is correct
  1672. $xml_version_too_old = true;
  1673. // foreach version in xml file,
  1674. // node means feature, attributes has to match
  1675. // the corresponding value in AdminThemes::$check_features[feature] array
  1676. $xmlArray = simpleXMLToArray($xml);
  1677. foreach ($xmlArray as $version)
  1678. {
  1679. if (isset($version['value']) && version_compare($version['value'], $check_version) >= 0)
  1680. {
  1681. foreach (AdminThemes::$check_features as $codeFeature => $arrConfigToCheck)
  1682. {
  1683. foreach ($arrConfigToCheck['attributes'] as $attr => $v)
  1684. {
  1685. if (!isset($version[$codeFeature]) || !isset($version[$codeFeature][$attr]) || $version[$codeFeature][$attr] != $v['value'])
  1686. if (!$this->_checkConfigForFeatures($codeFeature, $attr)) // feature missing in config.xml file, or wrong attribute value
  1687. $return = false;
  1688. }
  1689. }
  1690. $xml_version_too_old = false;
  1691. }
  1692. }
  1693. if ($xml_version_too_old && !$this->_checkConfigForFeatures(array_keys(AdminThemes::$check_features)))
  1694. {
  1695. $this->errors[] .= Tools::displayError('The config.xml file has not been created for this version of PrestaShop.');
  1696. $return = false;
  1697. }
  1698. return $return;
  1699. }
  1700. /**
  1701. * _checkConfigForFeatures
  1702. *
  1703. * @param array $arrFeature array of feature code to check
  1704. * @param mixed $configItem will precise the attribute which not matches. If empty, will check every attributes
  1705. *
  1706. * @return error message, or null if disabled
  1707. */
  1708. protected function _checkConfigForFeatures($arrFeatures, $configItem = array())
  1709. {
  1710. $return = true;
  1711. if (is_array($configItem))
  1712. {
  1713. foreach ($arrFeatures as $feature)
  1714. {
  1715. if (!count($configItem))
  1716. $configItem = array_keys(AdminThemes::$check_features[$feature]['attributes']);
  1717. }
  1718. foreach ($configItem as $attr)
  1719. {
  1720. $check = $this->_checkConfigForFeatures($arrFeatures, $attr);
  1721. if ($check == false)
  1722. $return = false;
  1723. }
  1724. return $return;
  1725. }
  1726. $return = true;
  1727. if (!is_array($arrFeatures))
  1728. $arrFeatures = array($arrFeatures);
  1729. foreach ($arrFeatures as $feature)
  1730. {
  1731. $arrConfigToCheck = AdminThemes::$check_features[$feature]['attributes'][$configItem]['check_if_not_valid'];
  1732. foreach ($arrConfigToCheck as $config_key => $config_val)
  1733. {
  1734. $config_get = Configuration::get($config_key);
  1735. if ($config_get != $config_val)
  1736. {
  1737. $this->errors[] = Tools::displayError(AdminThemes::$check_features[$feature]['error']).'.'
  1738. .(!empty(AdminThemes::$check_features[$feature]['tab'])
  1739. ? ' <a href="?tab='.AdminThemes::$check_features[$feature]['tab'].'&amp;token='
  1740. .Tools::getAdminTokenLite(AdminThemes::$check_features[$feature]['tab']).'" ><u>'
  1741. .Tools::displayError('You can disable this function.')
  1742. .'</u></a>' : ''
  1743. ).'<br/>';
  1744. $return = false;
  1745. break; // break for this attributes
  1746. }
  1747. }
  1748. }
  1749. return $return;
  1750. }
  1751. /**
  1752. *
  1753. * @param int $type
  1754. * $type = 0 both native & partner (default)
  1755. * $type = 1 native
  1756. * $type = 2 partner
  1757. *
  1758. *
  1759. * @return array
  1760. */
  1761. private function getNativeModule($type = 0)
  1762. {
  1763. $xml = simplexml_load_string(Tools::file_get_contents('http://api.prestashop.com/xml/modules_list_16.xml'));
  1764. if ($xml)
  1765. {
  1766. $natives = array();
  1767. switch ($type)
  1768. {
  1769. case 0:
  1770. foreach ($xml->modules as $row)
  1771. foreach ($row->module as $row2)
  1772. $natives[] = (string)$row2['name'];
  1773. break;
  1774. case 1:
  1775. foreach ($xml->modules as $row)
  1776. if ($row['type'] == 'native')
  1777. foreach ($row->module as $row2)
  1778. $natives[] = (string)$row2['name'];
  1779. break;
  1780. case 2:
  1781. foreach ($xml->modules as $row)
  1782. if ($row['type'] == 'partner')
  1783. foreach ($row->module as $row2)
  1784. $natives[] = (string)$row2['name'];
  1785. break;
  1786. }
  1787. if (count($natives) > 0)
  1788. return $natives;
  1789. }
  1790. return array(
  1791. 'addsharethis',
  1792. 'bankwire',
  1793. 'blockadvertising',
  1794. 'blockbanner',
  1795. 'blockbestsellers',
  1796. 'blockcart',
  1797. 'blockcategories',
  1798. 'blockcms',
  1799. 'blockcmsinfo',
  1800. 'blockcontact',
  1801. 'blockcontactinfos',
  1802. 'blockcurrencies',
  1803. 'blockcustomerprivacy',
  1804. 'blockfacebook',
  1805. 'blocklanguages',
  1806. 'blocklayered',
  1807. 'blocklink',
  1808. 'blockmanufacturer',
  1809. 'blockmyaccount',
  1810. 'blockmyaccountfooter',
  1811. 'blocknewproducts',
  1812. 'blocknewsletter',
  1813. 'blockpaymentlogo',
  1814. 'blockpermanentlinks',
  1815. 'blockreinsurance',
  1816. 'blockrss',
  1817. 'blocksearch',
  1818. 'blocksharefb',
  1819. 'blocksocial',
  1820. 'blockspecials',
  1821. 'blockstore',
  1822. 'blocksupplier',
  1823. 'blocktags',
  1824. 'blocktopmenu',
  1825. 'blockuserinfo',
  1826. 'blockviewed',
  1827. 'blockwishlist',
  1828. 'carriercompare',
  1829. 'cashondelivery',
  1830. 'cheque',
  1831. 'crossselling',
  1832. 'dashactivity',
  1833. 'dashgoals',
  1834. 'dashproducts',
  1835. 'dashtrends',
  1836. 'dateofdelivery',
  1837. 'editorial',
  1838. 'favoriteproducts',
  1839. 'feeder',
  1840. 'followup',
  1841. 'gapi',
  1842. 'graphnvd3',
  1843. 'gridhtml',
  1844. 'homefeatured',
  1845. 'homeslider',
  1846. 'loyalty',
  1847. 'mailalerts',
  1848. 'newsletter',
  1849. 'pagesnotfound',
  1850. 'productcomments',
  1851. 'productpaymentlogos',
  1852. 'productscategory',
  1853. 'producttooltip',
  1854. 'pscleaner',
  1855. 'referralprogram',
  1856. 'sekeywords',
  1857. 'sendtoafriend',
  1858. 'socialsharing',
  1859. 'statsbestcategories',
  1860. 'statsbestcustomers',
  1861. 'statsbestmanufacturers',
  1862. 'statsbestproducts',
  1863. 'statsbestsuppliers',
  1864. 'statsbestvouchers',
  1865. 'statscarrier',
  1866. 'statscatalog',
  1867. 'statscheckup',
  1868. 'statsdata',
  1869. 'statsequipment',
  1870. 'statsforecast',
  1871. 'statslive',
  1872. 'statsnewsletter',
  1873. 'statsorigin',
  1874. 'statspersonalinfos',
  1875. 'statsproduct',
  1876. 'statsregistrations',
  1877. 'statssales',
  1878. 'statssearch',
  1879. 'statsstock',
  1880. 'statsvisits',
  1881. 'themeconfigurator',
  1882. 'trackingfront',
  1883. 'vatnumber',
  1884. 'watermark'
  1885. );
  1886. }
  1887. private function getModules($xml)
  1888. {
  1889. $native_modules = $this->getNativeModule();
  1890. $theme_module = array();
  1891. $theme_module['to_install'] = array();
  1892. $theme_module['to_enable'] = array();
  1893. $theme_module['to_disable'] = array();
  1894. foreach ($xml->modules->module as $row)
  1895. {
  1896. if (strval($row['action']) == 'install' && !in_array(strval($row['name']), $native_modules))
  1897. $theme_module['to_install'][] = strval($row['name']);
  1898. elseif (strval($row['action']) == 'enable')
  1899. $theme_module['to_enable'][] = strval($row['name']);
  1900. elseif (strval($row['action']) == 'disable')
  1901. $theme_module['to_disable'][] = strval($row['name']);
  1902. }
  1903. return $theme_module;
  1904. }
  1905. private function formatHelperArray($origin_arr)
  1906. {
  1907. $fmt_arr = array();
  1908. foreach ($origin_arr as $module)
  1909. {
  1910. $display_name = $module;
  1911. $module_obj = Module::getInstanceByName($module);
  1912. if (Validate::isLoadedObject($module_obj))
  1913. $display_name = $module_obj->displayName;
  1914. $tmp = array();
  1915. $tmp['id'] = 'module'.$module;
  1916. $tmp['val'] = $module;
  1917. $tmp['name'] = $display_name;
  1918. $fmt_arr[] = $tmp;
  1919. }
  1920. return $fmt_arr;
  1921. }
  1922. private function formatHelperValuesArray($originArr)
  1923. {
  1924. $fmtArr = array();
  1925. foreach ($originArr as $key => $type)
  1926. foreach ($type as $module)
  1927. $fmtArr[$key.'_module'.$module] = true;
  1928. return $fmtArr;
  1929. }
  1930. public function renderChooseThemeModule()
  1931. {
  1932. $theme = New Theme((int)Tools::getValue('id_theme'));
  1933. $xml = false;
  1934. if (file_exists(_PS_ROOT_DIR_.'/config/xml/themes/'.$theme->directory.'.xml'))
  1935. $xml = simplexml_load_file(_PS_ROOT_DIR_.'/config/xml/themes/'.$theme->directory.'.xml');
  1936. elseif (file_exists(_PS_ROOT_DIR_.'/config/xml/themes/default.xml'))
  1937. $xml = simplexml_load_file(_PS_ROOT_DIR_.'/config/xml/themes/default.xml');
  1938. if ($xml)
  1939. {
  1940. $theme_module = $this->getModules($xml);
  1941. $toolbar_btn['save'] = array(
  1942. 'href' => '#',
  1943. 'desc' => $this->l('Save')
  1944. );
  1945. $to_install = array();
  1946. $to_enable = array();
  1947. $to_disable = array();
  1948. if (isset($theme_module['to_install']))
  1949. $to_install = $this->formatHelperArray($theme_module['to_install']);
  1950. if (isset($theme_module['to_enable']))
  1951. $to_enable = $this->formatHelperArray($theme_module['to_enable']);
  1952. if (isset($theme_module['to_disable']))
  1953. $to_disable = $this->formatHelperArray($theme_module['to_disable']);
  1954. $fields_form = array(
  1955. 'form' => array(
  1956. 'tinymce' => false,
  1957. 'legend' => array(
  1958. 'title' => $this->l('Modules to install'),
  1959. 'icon' => 'icon-picture'
  1960. ),
  1961. 'description' => $this->l('Themes often include their own modules in order to work properly. This option enables you to choose which modules should be enabled and which should be disabled. If you are unsure of what to do next, just press the "Save" button and proceed to the next step.'),
  1962. 'input' => array(
  1963. array(
  1964. 'type' => 'shop',
  1965. 'label' => $this->l('Shop association'),
  1966. 'name' => 'checkBoxShopAsso_theme'
  1967. ),
  1968. array(
  1969. 'type' => 'hidden',
  1970. 'name' => 'id_theme',
  1971. ),
  1972. ),
  1973. 'submit' => array(
  1974. 'title' => $this->l('Save'),
  1975. ),
  1976. )
  1977. );
  1978. if (count($to_install) > 0)
  1979. $fields_form['form']['input'][] = array(
  1980. 'type' => 'checkbox',
  1981. 'label' => $this->l('Select the theme\'s modules you wish to install'),
  1982. 'values' => array(
  1983. 'query' => $to_install,
  1984. 'id' => 'id',
  1985. 'name' => 'name'
  1986. ),
  1987. 'name' => 'to_install',
  1988. 'expand' => array(
  1989. 'print_total' => count($to_install),
  1990. 'default' => 'show',
  1991. 'show' => array('text' => $this->l('Show'), 'icon' => 'plus-sign-alt'),
  1992. 'hide' => array('text' => $this->l('Hide'), 'icon' => 'minus-sign-alt')
  1993. ),
  1994. );
  1995. if (count($to_enable) > 0)
  1996. $fields_form['form']['input'][] = array(
  1997. 'type' => 'checkbox',
  1998. 'label' => $this->l('Select the theme\'s modules you wish to enable'),
  1999. 'values' => array(
  2000. 'query' => $to_enable,
  2001. 'id' => 'id',
  2002. 'name' => 'name'
  2003. ),
  2004. 'name' => 'to_enable',
  2005. 'expand' => array(
  2006. 'print_total' => count($to_enable),
  2007. 'default' => 'show',
  2008. 'show' => array('text' => $this->l('Show'), 'icon' => 'plus-sign-alt'),
  2009. 'hide' => array('text' => $this->l('Hide'), 'icon' => 'minus-sign-alt')
  2010. ),
  2011. );
  2012. if (count($to_disable) > 0)
  2013. $fields_form['form']['input'][] = array(
  2014. 'type' => 'checkbox',
  2015. 'label' => $this->l('Select the theme\'s modules you wish to disable'),
  2016. 'values' => array(
  2017. 'query' => $to_disable,
  2018. 'id' => 'id',
  2019. 'name' => 'name'
  2020. ),
  2021. 'name' => 'to_disable',
  2022. 'expand' => array(
  2023. 'print_total' => count($to_disable),
  2024. 'default' => 'show',
  2025. 'show' => array('text' => $this->l('Show'), 'icon' => 'plus-sign-alt'),
  2026. 'hide' => array('text' => $this->l('Hide'), 'icon' => 'minus-sign-alt')
  2027. ),
  2028. );
  2029. $shops = array();
  2030. $shop = New Shop(Configuration::get('PS_SHOP_DEFAULT'));
  2031. $tmp['id_shop'] = $shop->id;
  2032. $tmp['id_theme'] = $shop->id_theme;
  2033. $shops[] = $tmp;
  2034. if (Shop::isFeatureActive())
  2035. $shops = Shop::getShops();
  2036. $current_shop = Context::getContext()->shop->id;
  2037. foreach ($shops as $shop)
  2038. {
  2039. $shop_theme = New Theme((int)$shop['id_theme']);
  2040. if ((int)Tools::getValue('id_theme') == (int)$shop['id_theme'])
  2041. continue;
  2042. $old_xml_name = 'default.xml';
  2043. if (file_exists(_PS_ROOT_DIR_.'/config/xml/themes/'.$shop_theme->directory.'.xml'))
  2044. $old_xml_name = $shop_theme->directory.'.xml';
  2045. $shop_xml = simplexml_load_file(_PS_ROOT_DIR_.'/config/xml/themes/'.$old_xml_name);
  2046. $theme_shop_module = $this->getModules($shop_xml);
  2047. $to_shop_uninstall = array_merge($theme_shop_module['to_install'], $theme_shop_module['to_enable']);
  2048. $to_shop_uninstall = preg_grep('/dash/', $to_shop_uninstall, PREG_GREP_INVERT);
  2049. $to_shop_uninstall_clean = array_diff($to_shop_uninstall, $theme_module['to_enable']);
  2050. $to_shop_uninstall_formated = $this->formatHelperArray($to_shop_uninstall_clean);
  2051. if (count($to_shop_uninstall_formated) == 0)
  2052. continue;
  2053. $class = '';
  2054. if ($shop['id_shop'] == $current_shop)
  2055. $theme_module['to_disable_shop'.$shop['id_shop']] = array_merge($theme_shop_module['to_install'], $to_shop_uninstall_clean);
  2056. else
  2057. $class = 'hide';
  2058. $fields_form['form']['input'][] = array(
  2059. 'type' => 'checkbox',
  2060. 'label' => sprintf($this->l('Select the modules from the old %1s theme that you wish to disable'), $shop_theme->directory),
  2061. 'form_group_class' => $class,
  2062. 'values' => array(
  2063. 'query' => $to_shop_uninstall_formated,
  2064. 'id' => 'id',
  2065. 'name' => 'name'
  2066. ),
  2067. 'expand' => array(
  2068. 'print_total' => count($to_shop_uninstall_formated),
  2069. 'default' => 'show',
  2070. 'show' => array('text' => $this->l('Show'), 'icon' => 'plus-sign-alt'),
  2071. 'hide' => array('text' => $this->l('Hide'), 'icon' => 'minus-sign-alt')
  2072. ),
  2073. 'name' => 'to_disable_shop'.$shop['id_shop']
  2074. );
  2075. }
  2076. $fields_value = $this->formatHelperValuesArray($theme_module);
  2077. $fields_value['id_theme'] = (int)Tools::getValue('id_theme');
  2078. $helper = new HelperForm();
  2079. $helper->currentIndex = $this->context->link->getAdminLink('AdminThemes', false).'&action=ThemeInstall';
  2080. $helper->token = Tools::getAdminTokenLite('AdminThemes');
  2081. $helper->submit_action = '';
  2082. $helper->show_toolbar = true;
  2083. $helper->toolbar_btn = $toolbar_btn;
  2084. $helper->fields_value = $fields_value;
  2085. $helper->languages = $this->getLanguages();
  2086. $helper->default_form_language = (int)$this->context->language->id;
  2087. $helper->table = 'theme';
  2088. $helper->override_folder = $this->tpl_folder;
  2089. return $helper->generateForm(array($fields_form));
  2090. }
  2091. Tools::redirectAdmin(Context::getContext()->link->getAdminLink('AdminThemes'));
  2092. }
  2093. private function updateImages($xml)
  2094. {
  2095. $return = array();
  2096. if (isset($xml->images->image))
  2097. foreach ($xml->images->image as $row)
  2098. {
  2099. Db::getInstance()->delete('image_type', '`name` = \''.pSQL($row['name']).'\'');
  2100. Db::getInstance()->execute('
  2101. INSERT INTO `'._DB_PREFIX_.'image_type` (`name`, `width`, `height`, `products`, `categories`, `manufacturers`, `suppliers`, `scenes`)
  2102. VALUES (\''.pSQL($row['name']).'\',
  2103. '.(int)$row['width'].',
  2104. '.(int)$row['height'].',
  2105. '.($row['products'] == 'true' ? 1 : 0).',
  2106. '.($row['categories'] == 'true' ? 1 : 0).',
  2107. '.($row['manufacturers'] == 'true' ? 1 : 0).',
  2108. '.($row['suppliers'] == 'true' ? 1 : 0).',
  2109. '.($row['scenes'] == 'true' ? 1 : 0).')');
  2110. $return['ok'][] = array(
  2111. 'name' => strval($row['name']),
  2112. 'width' => (int)$row['width'],
  2113. 'height' => (int)$row['height']
  2114. );
  2115. }
  2116. return $return;
  2117. }
  2118. private function hookModule($id_module, $module_hooks, $shop)
  2119. {
  2120. Db::getInstance()->execute('INSERT IGNORE INTO '._DB_PREFIX_.'module_shop (id_module, id_shop) VALUES('.(int)$id_module.', '.(int)$shop.')');
  2121. Db::getInstance()->execute($sql = 'DELETE FROM `'._DB_PREFIX_.'hook_module` WHERE `id_module` = '.(int)$id_module.' AND id_shop = '.(int)$shop);
  2122. foreach ($module_hooks as $hooks)
  2123. {
  2124. foreach ($hooks as $hook)
  2125. {
  2126. $sql_hook_module = 'INSERT INTO `'._DB_PREFIX_.'hook_module` (`id_module`, `id_shop`, `id_hook`, `position`)
  2127. VALUES ('.(int)$id_module.', '.(int)$shop.', '.(int)Hook::getIdByName($hook['hook']).', '.(int)$hook['position'].')';
  2128. if (count($hook['exceptions']) > 0)
  2129. {
  2130. foreach ($hook['exceptions'] as $exception)
  2131. {
  2132. $sql_hook_module_except = 'INSERT INTO `'._DB_PREFIX_.'hook_module_exceptions` (`id_module`, `id_hook`, `file_name`) VALUES ('.(int)$id_module.', '.(int)Hook::getIdByName($hook['hook']).', "'.pSQL($exception).'")';
  2133. Db::getInstance()->execute($sql_hook_module_except);
  2134. }
  2135. }
  2136. Db::getInstance()->execute($sql_hook_module);
  2137. }
  2138. }
  2139. }
  2140. public function processThemeInstall()
  2141. {
  2142. $shops_asso = $this->context->employee->getAssociatedShops();
  2143. if (Shop::isFeatureActive() && !Tools::getIsset('checkBoxShopAsso_theme') && count($shops_asso) > 1)
  2144. {
  2145. $this->errors[] = $this->l('You must choose at least one shop.');
  2146. $this->display = 'ChooseThemeModule';
  2147. return;
  2148. }
  2149. $theme = New Theme((int)Tools::getValue('id_theme'));
  2150. if (count($shops_asso) == 1)
  2151. $shops = $shops_asso;
  2152. else
  2153. {
  2154. $shops = array(Configuration::get('PS_SHOP_DEFAULT'));
  2155. if (Tools::isSubmit('checkBoxShopAsso_theme'))
  2156. $shops = Tools::getValue('checkBoxShopAsso_theme');
  2157. }
  2158. $xml = false;
  2159. if (file_exists(_PS_ROOT_DIR_.'/config/xml/themes/'.$theme->directory.'.xml'))
  2160. $xml = simplexml_load_file(_PS_ROOT_DIR_.'/config/xml/themes/'.$theme->directory.'.xml');
  2161. elseif (file_exists(_PS_ROOT_DIR_.'/config/xml/themes/default.xml'))
  2162. $xml = simplexml_load_file(_PS_ROOT_DIR_.'/config/xml/themes/default.xml');
  2163. if ($xml)
  2164. {
  2165. $module_hook = array();
  2166. foreach ($xml->modules->hooks->hook as $row)
  2167. {
  2168. $name = strval($row['module']);
  2169. $exceptions = (isset($row['exceptions']) ? explode(',', strval($row['exceptions'])) : array());
  2170. $module_hook[$name]['hook'][] = array(
  2171. 'hook' => strval($row['hook']),
  2172. 'position' => strval($row['position']),
  2173. 'exceptions' => $exceptions
  2174. );
  2175. }
  2176. $this->img_error = $this->updateImages($xml);
  2177. $this->modules_errors = array();
  2178. foreach ($shops as $id_shop)
  2179. {
  2180. foreach ($_POST as $key => $value)
  2181. {
  2182. if (strncmp($key, 'to_install', strlen('to_install')) == 0)
  2183. {
  2184. $module = Module::getInstanceByName($value);
  2185. if ($module)
  2186. {
  2187. $is_installed_success = true;
  2188. if (!Module::isInstalled($module->name))
  2189. $is_installed_success = $module->install();
  2190. if ($is_installed_success)
  2191. {
  2192. if (!Module::isEnabled($module->name))
  2193. $module->enable();
  2194. if ((int)$module->id > 0 && isset($module_hook[$module->name]))
  2195. $this->hookModule($module->id, $module_hook[$module->name], $id_shop);
  2196. }
  2197. else
  2198. $this->modules_errors[] = array('module_name' => $module->name, 'errors' => $module->getErrors());
  2199. unset($module_hook[$module->name]);
  2200. }
  2201. }
  2202. elseif (strncmp($key, 'to_enable', strlen('to_enable')) == 0)
  2203. {
  2204. $module = Module::getInstanceByName($value);
  2205. if ($module)
  2206. {
  2207. $is_installed_success = true;
  2208. if (!Module::isInstalled($module->name))
  2209. $is_installed_success = $module->install();
  2210. if ($is_installed_success)
  2211. {
  2212. if (!Module::isEnabled($module->name))
  2213. $module->enable();
  2214. if ((int)$module->id > 0 && isset($module_hook[$module->name]))
  2215. $this->hookModule($module->id, $module_hook[$module->name], $id_shop);
  2216. }
  2217. else
  2218. $this->modules_errors[] = array('module_name' => $module->name, 'errors' => $module->getErrors());
  2219. unset($module_hook[$module->name]);
  2220. }
  2221. }
  2222. elseif (strncmp($key, 'to_disable', strlen('to_disable')) == 0)
  2223. {
  2224. $key_exploded = explode('_', $key);
  2225. $id_shop_module = (int)substr($key_exploded[2], 4);
  2226. if ((int)$id_shop_module > 0 && $id_shop_module != (int)$id_shop)
  2227. continue;
  2228. $module_obj = Module::getInstanceByName($value);
  2229. if (Validate::isLoadedObject($module_obj))
  2230. {
  2231. if (Module::isEnabled($module_obj->name))
  2232. $module_obj->disable();
  2233. unset($module_hook[$module_obj->name]);
  2234. }
  2235. }
  2236. }
  2237. $shop = New Shop((int)$id_shop);
  2238. $shop->id_theme = (int)Tools::getValue('id_theme');
  2239. $this->context->shop->id_theme = $shop->id_theme;
  2240. $this->context->shop->update();
  2241. $shop->save();
  2242. if (Shop::isFeatureActive())
  2243. Configuration::updateValue('PS_PRODUCTS_PER_PAGE', (int)$theme->product_per_page, false, null, (int)$id_shop);
  2244. else
  2245. Configuration::updateValue('PS_PRODUCTS_PER_PAGE', (int)$theme->product_per_page);
  2246. }
  2247. $this->doc = array();
  2248. foreach ($xml->docs->doc as $row)
  2249. $this->doc[strval($row['name'])] = __PS_BASE_URI__.'themes/'.$theme->directory.'/docs/'.basename(strval($row['path']));
  2250. }
  2251. Tools::clearCache($this->context->smarty);
  2252. $this->theme_name = $theme->name;
  2253. $this->display = 'view';
  2254. }
  2255. public function renderView()
  2256. {
  2257. $this->tpl_view_vars = array(
  2258. 'doc' => $this->doc,
  2259. 'theme_name' => $this->theme_name,
  2260. 'img_error' => $this->img_error,
  2261. 'modules_errors' => $this->modules_errors,
  2262. 'back_link' => Context::getContext()->link->getAdminLink('AdminThemes'),
  2263. 'image_link' => Context::getContext()->link->getAdminLink('AdminImages')
  2264. );
  2265. return parent::renderView();
  2266. }
  2267. /**
  2268. * This functions make checks about AdminThemes configuration edition only.
  2269. *
  2270. * @since 1.4
  2271. */
  2272. public function postProcess()
  2273. {
  2274. if (Tools::isSubmit('submitOptionstheme') && Tools::isSubmit('id_theme') && !Tools::isSubmit('deletetheme')
  2275. && Tools::getValue('action') != 'ThemeInstall' && $this->context->shop->id_theme != Tools::getValue('id_theme'))
  2276. $this->display = 'ChooseThemeModule';
  2277. elseif (Tools::isSubmit('installThemeFromFolder') && ($this->context->mode != Context::MODE_HOST))
  2278. {
  2279. $theme_dir = Tools::getValue('theme_dir');
  2280. $this->installTheme($theme_dir);
  2281. }
  2282. else
  2283. {
  2284. // new check compatibility theme feature (1.4) :
  2285. $val = Tools::getValue('PS_THEME');
  2286. Configuration::updateValue('PS_IMG_UPDATE_TIME', time());
  2287. if (!empty($val) && !$this->_isThemeCompatible($val)) // don't submit if errors
  2288. unset($_POST['submitThemes'.$this->table]);
  2289. Tools::clearCache($this->context->smarty);
  2290. return parent::postProcess();
  2291. }
  2292. }
  2293. /**
  2294. * Update PS_LOGO
  2295. */
  2296. public function updateOptionPsLogo()
  2297. {
  2298. $this->updateLogo('PS_LOGO', 'logo');
  2299. }
  2300. /**
  2301. * Update PS_LOGO_MOBILE
  2302. */
  2303. public function updateOptionPsLogoMobile()
  2304. {
  2305. $this->updateLogo('PS_LOGO_MOBILE', 'logo_mobile');
  2306. }
  2307. /**
  2308. * Update PS_LOGO_MAIL
  2309. */
  2310. public function updateOptionPsLogoMail()
  2311. {
  2312. $this->updateLogo('PS_LOGO_MAIL', 'logo_mail');
  2313. }
  2314. /**
  2315. * Update PS_LOGO_INVOICE
  2316. */
  2317. public function updateOptionPsLogoInvoice()
  2318. {
  2319. $this->updateLogo('PS_LOGO_INVOICE', 'logo_invoice');
  2320. }
  2321. /**
  2322. * Update PS_STORES_ICON
  2323. */
  2324. public function updateOptionPsStoresIcon()
  2325. {
  2326. $this->updateLogo('PS_STORES_ICON', 'logo_stores');
  2327. }
  2328. /**
  2329. * Generic function which allows logo upload
  2330. *
  2331. * @param $field_name
  2332. * @param $logo_prefix
  2333. *
  2334. * @return bool
  2335. */
  2336. protected function updateLogo($field_name, $logo_prefix)
  2337. {
  2338. $id_shop = Context::getContext()->shop->id;
  2339. if (isset($_FILES[$field_name]['tmp_name']) && $_FILES[$field_name]['tmp_name'] && $_FILES[$field_name]['size'])
  2340. {
  2341. if ($error = ImageManager::validateUpload($_FILES[$field_name], Tools::getMaxUploadSize()))
  2342. {
  2343. $this->errors[] = $error;
  2344. return false;
  2345. }
  2346. $tmp_name = tempnam(_PS_TMP_IMG_DIR_, 'PS');
  2347. if (!$tmp_name || !move_uploaded_file($_FILES[$field_name]['tmp_name'], $tmp_name))
  2348. return false;
  2349. $ext = ($field_name == 'PS_STORES_ICON') ? '.gif' : '.jpg';
  2350. $logo_name = Tools::link_rewrite(Context::getContext()->shop->name).'-'
  2351. .$logo_prefix.'-'.(int)Configuration::get('PS_IMG_UPDATE_TIME').(int)$id_shop.$ext;
  2352. if (Context::getContext()->shop->getContext() == Shop::CONTEXT_ALL || $id_shop == 0
  2353. || Shop::isFeatureActive() == false)
  2354. $logo_name = Tools::link_rewrite(Context::getContext()->shop->name).'-'
  2355. .$logo_prefix.'-'.(int)Configuration::get('PS_IMG_UPDATE_TIME').$ext;
  2356. if ($field_name == 'PS_STORES_ICON')
  2357. {
  2358. if (!@ImageManager::resize($tmp_name, _PS_IMG_DIR_.$logo_name, null, null, 'gif', true))
  2359. $this->errors[] = Tools::displayError('An error occurred while attempting to copy your logo.');
  2360. }
  2361. else
  2362. {
  2363. if (!@ImageManager::resize($tmp_name, _PS_IMG_DIR_.$logo_name))
  2364. $this->errors[] = Tools::displayError('An error occurred while attempting to copy your logo.');
  2365. }
  2366. $id_shop = null;
  2367. $id_shop_group = null;
  2368. if (!count($this->errors) && @filemtime(_PS_IMG_DIR_.Configuration::get($field_name)))
  2369. {
  2370. if (Shop::isFeatureActive())
  2371. {
  2372. if (Shop::getContext() == Shop::CONTEXT_SHOP)
  2373. {
  2374. $id_shop = Shop::getContextShopID();
  2375. $id_shop_group = Shop::getContextShopGroupID();
  2376. Shop::setContext(Shop::CONTEXT_ALL);
  2377. $logo_all = Configuration::get($field_name);
  2378. Shop::setContext(Shop::CONTEXT_GROUP);
  2379. $logo_group = Configuration::get($field_name);
  2380. Shop::setContext(Shop::CONTEXT_SHOP);
  2381. $logo_shop = Configuration::get($field_name);
  2382. if ($logo_all != $logo_shop && $logo_group != $logo_shop && $logo_shop != false)
  2383. @unlink(_PS_IMG_DIR_.Configuration::get($field_name));
  2384. }
  2385. elseif (Shop::getContext() == Shop::CONTEXT_GROUP)
  2386. {
  2387. $id_shop_group = Shop::getContextShopGroupID();
  2388. Shop::setContext(Shop::CONTEXT_ALL);
  2389. $logo_all = Configuration::get($field_name);
  2390. Shop::setContext(Shop::CONTEXT_GROUP);
  2391. if ($logo_all != Configuration::get($field_name))
  2392. @unlink(_PS_IMG_DIR_.Configuration::get($field_name));
  2393. }
  2394. }
  2395. else
  2396. @unlink(_PS_IMG_DIR_.Configuration::get($field_name));
  2397. }
  2398. Configuration::updateValue($field_name, $logo_name, false, $id_shop_group, $id_shop);
  2399. @unlink($tmp_name);
  2400. }
  2401. }
  2402. /**
  2403. * Update PS_FAVICON
  2404. */
  2405. public function updateOptionPsFavicon()
  2406. {
  2407. $id_shop = Context::getContext()->shop->id;
  2408. if ($id_shop == Configuration::get('PS_SHOP_DEFAULT'))
  2409. $this->uploadIco('PS_FAVICON', _PS_IMG_DIR_.'favicon.ico');
  2410. if ($this->uploadIco('PS_FAVICON', _PS_IMG_DIR_.'favicon-'.(int)$id_shop.'.ico'))
  2411. Configuration::updateValue('PS_FAVICON', 'favicon-'.(int)$id_shop.'.ico');
  2412. Configuration::updateGlobalValue('PS_FAVICON', 'favicon.ico');
  2413. $this->redirect_after = self::$currentIndex.'&token='.$this->token;
  2414. }
  2415. /**
  2416. * Update theme for current shop
  2417. */
  2418. public function updateOptionThemeForShop()
  2419. {
  2420. if (!$this->can_display_themes)
  2421. return;
  2422. $id_theme = (int)Tools::getValue('id_theme');
  2423. if ($id_theme && $this->context->shop->id_theme != $id_theme)
  2424. {
  2425. $this->context->shop->id_theme = $id_theme;
  2426. $this->context->shop->update();
  2427. $this->redirect_after = self::$currentIndex.'&token='.$this->token;
  2428. }
  2429. }
  2430. protected function uploadIco($name, $dest)
  2431. {
  2432. if (isset($_FILES[$name]['tmp_name']) && !empty($_FILES[$name]['tmp_name']))
  2433. {
  2434. // Check ico validity
  2435. if ($error = ImageManager::validateIconUpload($_FILES[$name]))
  2436. $this->errors[] = $error;
  2437. // Copy new ico
  2438. elseif (!copy($_FILES[$name]['tmp_name'], $dest))
  2439. $this->errors[] = sprintf(Tools::displayError('An error occurred while uploading the favicon: cannot copy file "%s" to folder "%s".'), $_FILES[$name]['tmp_name'], $dest);
  2440. }
  2441. return !count($this->errors);
  2442. }
  2443. public function initProcess()
  2444. {
  2445. if ((isset($_GET['responsive'.$this->table]) || isset($_GET['responsive'])) && Tools::getValue($this->identifier))
  2446. {
  2447. if ($this->tabAccess['edit'] === '1')
  2448. $this->action = 'responsive';
  2449. else
  2450. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  2451. }
  2452. elseif ((isset($_GET['default_left_column'.$this->table]) || isset($_GET['default_left_column'])) && Tools::getValue($this->identifier))
  2453. {
  2454. if ($this->tabAccess['edit'] === '1')
  2455. $this->action = 'defaultleftcolumn';
  2456. else
  2457. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  2458. }
  2459. elseif ((isset($_GET['default_right_column'.$this->table]) || isset($_GET['default_right_column'])) && Tools::getValue($this->identifier))
  2460. {
  2461. if ($this->tabAccess['edit'] === '1')
  2462. $this->action = 'defaultrightcolumn';
  2463. else
  2464. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  2465. }
  2466. elseif (Tools::getIsset('id_theme_meta') && Tools::getIsset('leftmeta'))
  2467. {
  2468. if ($this->tabAccess['edit'] === '1')
  2469. $this->action = 'leftmeta';
  2470. else
  2471. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  2472. }
  2473. elseif (Tools::getIsset('id_theme_meta') && Tools::getIsset('rightmeta'))
  2474. {
  2475. if ($this->tabAccess['edit'] === '1')
  2476. $this->action = 'rightmeta';
  2477. else
  2478. $this->errors[] = Tools::displayError('You do not have permission to edit this.');
  2479. }
  2480. parent::initProcess();
  2481. // This is a composite page, we don't want the "options" display mode
  2482. if ($this->display == 'options' || $this->display == 'list')
  2483. $this->display = '';
  2484. }
  2485. public function printResponsiveIcon($value)
  2486. {
  2487. return ($value ? '<span class="list-action-enable action-enabled"><i class="icon-check"></i></span>' : '<span class="list-action-enable action-disabled"><i class="icon-remove"></i></span>');
  2488. }
  2489. public function processResponsive()
  2490. {
  2491. if (Validate::isLoadedObject($object = $this->loadObject()))
  2492. {
  2493. if ($object->toggleResponsive())
  2494. $this->redirect_after = self::$currentIndex.'&conf=5&token='.$this->token;
  2495. else
  2496. $this->errors[] = Tools::displayError('An error occurred while updating responsive status.');
  2497. }
  2498. else
  2499. $this->errors[] = Tools::displayError('An error occurred while updating the responsive status for this object.').
  2500. ' <b>'.$this->table.'</b> '.
  2501. Tools::displayError('(cannot load object)');
  2502. return $object;
  2503. }
  2504. public function processDefaultLeftColumn()
  2505. {
  2506. if (Validate::isLoadedObject($object = $this->loadObject()))
  2507. {
  2508. if ($object->toggleDefaultLeftColumn())
  2509. $this->redirect_after = self::$currentIndex.'&conf=5&token='.$this->token;
  2510. else
  2511. $this->errors[] = Tools::displayError('An error occurred while updating default left column status.');
  2512. }
  2513. else
  2514. $this->errors[] = Tools::displayError('An error occurred while updating the default left column status for this object.').
  2515. ' <b>'.$this->table.'</b> '.
  2516. Tools::displayError('(cannot load object)');
  2517. return $object;
  2518. }
  2519. public function processDefaultRightColumn()
  2520. {
  2521. if (Validate::isLoadedObject($object = $this->loadObject()))
  2522. {
  2523. if ($object->toggleDefaultRightColumn())
  2524. $this->redirect_after = self::$currentIndex.'&conf=5&token='.$this->token;
  2525. else
  2526. $this->errors[] = Tools::displayError('An error occurred while updating default right column status.');
  2527. }
  2528. else
  2529. $this->errors[] = Tools::displayError('An error occurred while updating the default right column status for this object.').
  2530. ' <b>'.$this->table.'</b> '.
  2531. Tools::displayError('(cannot load object)');
  2532. return $object;
  2533. }
  2534. public function ajaxProcessLeftMeta()
  2535. {
  2536. $theme_meta = Db::getInstance()->getRow(
  2537. 'SELECT * FROM '._DB_PREFIX_.'theme_meta WHERE id_theme_meta = '.(int)Tools::getValue('id_theme_meta')
  2538. );
  2539. $result = false;
  2540. if ($theme_meta)
  2541. {
  2542. $sql = 'UPDATE '._DB_PREFIX_.'theme_meta SET left_column='.(int)!(bool)$theme_meta['left_column'].' WHERE id_theme_meta='.(int)Tools::getValue('id_theme_meta');
  2543. $result = Db::getInstance()->execute($sql);
  2544. }
  2545. if ($result)
  2546. echo json_encode(array('success' => 1, 'text' => $this->l('The status has been updated successfully.')));
  2547. else
  2548. echo json_encode(array('success' => 0, 'text' => $this->l('An error occurred while updating this meta.')));
  2549. }
  2550. public function processLeftMeta()
  2551. {
  2552. $theme_meta = Db::getInstance()->getRow(
  2553. 'SELECT * FROM '._DB_PREFIX_.'theme_meta WHERE id_theme_meta = '.(int)Tools::getValue('id_theme_meta')
  2554. );
  2555. $result = false;
  2556. if ($theme_meta)
  2557. {
  2558. $sql = 'UPDATE '._DB_PREFIX_.'theme_meta SET left_column='.(int)!(bool)$theme_meta['left_column'].' WHERE id_theme_meta='.(int)Tools::getValue('id_theme_meta');
  2559. $result = Db::getInstance()->execute($sql);
  2560. }
  2561. if ($result)
  2562. $this->redirect_after = self::$currentIndex.'&updatetheme&id_theme='.$theme_meta['id_theme'].'&conf=5&token='.$this->token;
  2563. else
  2564. $this->errors[] = Tools::displayError('An error occurred while updating this meta.');
  2565. }
  2566. public function ajaxProcessRightMeta()
  2567. {
  2568. $theme_meta = Db::getInstance()->getRow(
  2569. 'SELECT * FROM '._DB_PREFIX_.'theme_meta WHERE id_theme_meta = '.(int)Tools::getValue('id_theme_meta')
  2570. );
  2571. $result = false;
  2572. if ($theme_meta)
  2573. {
  2574. $sql = 'UPDATE '._DB_PREFIX_.'theme_meta SET right_column='.(int)!(bool)$theme_meta['right_column'].' WHERE id_theme_meta='.(int)Tools::getValue('id_theme_meta');
  2575. $result = Db::getInstance()->execute($sql);
  2576. }
  2577. if ($result)
  2578. echo json_encode(array('success' => 1, 'text' => $this->l('The status has been updated successfully.')));
  2579. else
  2580. echo json_encode(array('success' => 0, 'text' => $this->l('An error occurred while updating this meta.')));
  2581. }
  2582. public function processRightMeta()
  2583. {
  2584. $theme_meta = Db::getInstance()->getRow(
  2585. 'SELECT * FROM '._DB_PREFIX_.'theme_meta WHERE id_theme_meta = '.(int)Tools::getValue('id_theme_meta')
  2586. );
  2587. $result = false;
  2588. if ($theme_meta)
  2589. {
  2590. $sql = 'UPDATE '._DB_PREFIX_.'theme_meta SET right_column='.(int)!(bool)$theme_meta['right_column'].' WHERE id_theme_meta='.(int)Tools::getValue('id_theme_meta');
  2591. $result = Db::getInstance()->execute($sql);
  2592. }
  2593. if ($result)
  2594. $this->redirect_after = self::$currentIndex.'&updatetheme&id_theme='.$theme_meta['id_theme'].'&conf=5&token='.$this->token;
  2595. else
  2596. $this->errors[] = Tools::displayError('An error occurred while updating this meta.');
  2597. }
  2598. /**
  2599. * Function used to render the options for this controller
  2600. */
  2601. public function renderOptions()
  2602. {
  2603. if (isset($this->display) && method_exists($this, 'render'.$this->display))
  2604. return $this->{'render'.$this->display}();
  2605. if ($this->fields_options && is_array($this->fields_options))
  2606. {
  2607. $helper = new HelperOptions($this);
  2608. $this->setHelperDisplay($helper);
  2609. $helper->toolbar_scroll = true;
  2610. $helper->title = $this->l('Theme appearance');
  2611. $helper->toolbar_btn = array(
  2612. 'save' => array(
  2613. 'href' => '#',
  2614. 'desc' => $this->l('Save')
  2615. )
  2616. );
  2617. $helper->id = $this->id;
  2618. $helper->tpl_vars = $this->tpl_option_vars;
  2619. $options = $helper->generateOptions($this->fields_options);
  2620. return $options;
  2621. }
  2622. }
  2623. public function setMedia()
  2624. {
  2625. parent::setMedia();
  2626. $this->addJS(_PS_JS_DIR_.'admin/themes.js');
  2627. if ($this->context->mode == Context::MODE_HOST && Tools::getValue('action') == 'importtheme')
  2628. $this->addJS(_PS_JS_DIR_.'admin/addons.js');
  2629. }
  2630. }