PageRenderTime 55ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/controllers/admin/AdminThemesController.php

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