PageRenderTime 66ms CodeModel.GetById 23ms 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

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

  1. <?php
  2. /*
  3. * 2007-2014 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. * @author PrestaShop SA <contact@prestashop.com>
  22. * @copyright 2007-2014 PrestaShop SA
  23. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  24. * International Registered Trademark & Property of PrestaShop SA
  25. */
  26. 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 …

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