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

/controllers/admin/AdminThemesController.php

https://gitlab.com/mtellezgalindo/PrestaShop
PHP | 2967 lines | 2497 code | 337 blank | 133 comment | 427 complexity | 6283e241d2c00ffb7793d8e6cfa8bb41 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-3.0

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);
  108. libxml_use_internal_errors(true);
  109. // Download user themes from Addons
  110. if ($this->logged_on_addons)
  111. $this->downloadAddonsThemes();
  112. // Employee languages used for link and utm_source
  113. $lang = new Language($this->context->language->id);
  114. $iso_lang_uc = strtoupper($lang->iso_code);
  115. $this->fields_options = array(
  116. 'appearance' => array(
  117. 'title' => $this->l('Your current theme'),
  118. 'icon' => 'icon-html5',
  119. 'tabs' => array(
  120. 'logo' => $this->l('Logo'),
  121. 'logo2' => $this->l('Invoice & Email Logos'),
  122. 'icons' => $this->l('Icons'),
  123. 'mobile' => $this->l('Mobile'),
  124. ),
  125. 'fields' => array(
  126. 'PS_LOGO' => array(
  127. 'title' => $this->l('Header logo'),
  128. 'hint' => $this->l('Will appear on main page. Recommended height: 52px. Maximum height on default theme: 65px.'),
  129. 'type' => 'file',
  130. 'name' => 'PS_LOGO',
  131. 'tab' => 'logo',
  132. 'thumb' => _PS_IMG_.Configuration::get('PS_LOGO')
  133. ),
  134. 'PS_LOGO_MOBILE' => array(
  135. 'title' => $this->l('Header logo for mobile'),
  136. 'desc' => ((Configuration::get('PS_LOGO_MOBILE') === false) ? '<span class="light-warning">'.$this->l('Warning: No mobile logo has been defined. The header logo will be used instead.').'</span><br />' : ''),
  137. 'hint' => $this->l('Will appear on the main page of your mobile template. If left undefined, the header logo will be used.'),
  138. 'type' => 'file',
  139. 'name' => 'PS_LOGO_MOBILE',
  140. 'tab' => 'mobile',
  141. 'thumb' => (Configuration::get('PS_LOGO_MOBILE') !== false && file_exists(_PS_IMG_DIR_.Configuration::get('PS_LOGO_MOBILE'))) ? _PS_IMG_.Configuration::get('PS_LOGO_MOBILE') : _PS_IMG_.Configuration::get('PS_LOGO')
  142. ),
  143. 'PS_LOGO_MAIL' => array(
  144. 'title' => $this->l('Mail logo'),
  145. 'desc' => ((Configuration::get('PS_LOGO_MAIL') === false) ? '<span class="light-warning">'.$this->l('Warning: if no email logo is available, the main logo will be used instead.').'</span><br />' : ''),
  146. 'hint' => $this->l('Will appear on email headers. If undefined, the header logo will be used.'),
  147. 'type' => 'file',
  148. 'name' => 'PS_LOGO_MAIL',
  149. 'tab' => 'logo2',
  150. 'thumb' => (Configuration::get('PS_LOGO_MAIL') !== false && file_exists(_PS_IMG_DIR_.Configuration::get('PS_LOGO_MAIL'))) ? _PS_IMG_.Configuration::get('PS_LOGO_MAIL') : _PS_IMG_.Configuration::get('PS_LOGO')
  151. ),
  152. 'PS_LOGO_INVOICE' => array(
  153. 'title' => $this->l('Invoice logo'),
  154. 'desc' => ((Configuration::get('PS_LOGO_INVOICE') === false) ? '<span class="light-warning">'.$this->l('Warning: if no invoice logo is available, the main logo will be used instead.').'</span><br />' : ''),
  155. 'hint' => $this->l('Will appear on invoice headers.').' '.$this->l('Warning: you can use a PNG file for transparency, but it can take up to 1 second per page for processing. Please consider using JPG instead.'),
  156. 'type' => 'file',
  157. 'name' => 'PS_LOGO_INVOICE',
  158. 'tab' => 'logo2',
  159. 'thumb' => (Configuration::get('PS_LOGO_INVOICE') !== false && file_exists(_PS_IMG_DIR_.Configuration::get('PS_LOGO_INVOICE'))) ? _PS_IMG_.Configuration::get('PS_LOGO_INVOICE') : _PS_IMG_.Configuration::get('PS_LOGO')
  160. ),
  161. 'PS_FAVICON' => array(
  162. 'title' => $this->l('Favicon'),
  163. 'hint' => $this->l('Will appear in the address bar of your web browser.'),
  164. 'type' => 'file',
  165. 'name' => 'PS_FAVICON',
  166. 'tab' => 'icons',
  167. 'thumb' => _PS_IMG_.Configuration::get('PS_FAVICON').(Tools::getValue('conf') ? sprintf('?%04d', rand(0, 9999)) : '')
  168. ),
  169. 'PS_STORES_ICON' => array(
  170. 'title' => $this->l('Store icon'),
  171. 'hint' => $this->l('Will appear on the store locator (inside Google Maps).').'<br />'.$this->l('Suggested size: 30x30, transparent GIF.'),
  172. 'type' => 'file',
  173. 'name' => 'PS_STORES_ICON',
  174. 'tab' => 'icons',
  175. 'thumb' => _PS_IMG_.Configuration::get('PS_STORES_ICON')
  176. ),
  177. 'PS_ALLOW_MOBILE_DEVICE' => array(
  178. 'title' => $this->l('Enable the mobile theme'),
  179. 'hint' => $this->l('Allows visitors browsing on mobile devices to view a lighter version of your website.'),
  180. 'type' => 'radio',
  181. 'required' => true,
  182. 'validation' => 'isGenericName',
  183. 'tab' => 'mobile',
  184. 'choices' => array(
  185. 0 => $this->l('I\'d like to disable it.'),
  186. 1 => $this->l('I\'d like to enable it only on smartphones.'),
  187. 2 => $this->l('I\'d like to enable it only on tablets.'),
  188. 3 => $this->l('I\'d like to enable it on both smartphones and tablets.')
  189. )
  190. ),
  191. ),
  192. 'after_tabs' => array(
  193. 'cur_theme' => Theme::getThemeInfo($this->context->shop->id_theme),
  194. ),
  195. 'submit' => array('title' => $this->l('Save')),
  196. 'buttons' => array(
  197. 'storeLink' => array(
  198. 'title' => $this->l('Visit the theme catalog'),
  199. 'icon' => 'process-icon-themes',
  200. 'href' => 'http://addons.prestashop.com/en/3-templates-prestashop'
  201. .'?utm_source=back-office&utm_medium=theme-button'
  202. .'&utm_campaign=back-office-'.$iso_lang_uc
  203. .'&utm_content='.(defined('_PS_HOST_MODE_') ? 'cloud' : 'download'),
  204. 'js' => 'return !window.open(this.href)'
  205. )
  206. )
  207. ),
  208. );
  209. $installed_theme = Theme::getAllThemes(array($this->context->shop->id_theme));
  210. $non_installed_theme = ($this->context->mode == Context::MODE_HOST) ? array() : Theme::getNonInstalledTheme();
  211. if (count($installed_theme) || !empty($non_installed_theme))
  212. {
  213. $this->fields_options['theme'] = array(
  214. 'title' => sprintf($this->l('Select a theme for the "%s" shop'), $this->context->shop->name),
  215. 'description' => (!$this->can_display_themes) ? $this->l('You must select a shop from the above list if you wish to choose a theme.') : '',
  216. 'fields' => array(
  217. 'theme_for_shop' => array(
  218. 'type' => 'theme',
  219. 'themes' => $installed_theme,
  220. 'not_installed' => $non_installed_theme,
  221. 'id_theme' => $this->context->shop->id_theme,
  222. 'can_display_themes' => $this->can_display_themes,
  223. 'no_multishop_checkbox' => true
  224. ),
  225. ),
  226. );
  227. }
  228. }
  229. public function renderForm()
  230. {
  231. $get_available_themes = Theme::getAvailable(false);
  232. $available_theme_dir = array();
  233. $selected_theme_dir = null;
  234. $metas = Meta::getMetas();
  235. $formated_metas = array();
  236. $image_url = false;
  237. if ($this->object)
  238. {
  239. if ((int)$this->object->id > 0)
  240. {
  241. $theme = New Theme((int)$this->object->id);
  242. $theme_metas = Db::getInstance()->executeS('SELECT ml.`title`, m.`page`, tm.`left_column` as `left`, tm.`right_column` as `right`, m.`id_meta`, tm.`id_theme_meta`
  243. FROM '._DB_PREFIX_.'theme_meta as tm
  244. LEFT JOIN '._DB_PREFIX_.'meta m ON (m.`id_meta` = tm.`id_meta`)
  245. LEFT JOIN '._DB_PREFIX_.'meta_lang ml ON(ml.id_meta = m.id_meta AND ml.id_lang = '.(int)$this->context->language->id.')
  246. WHERE tm.`id_theme` = '.(int)$this->object->id.
  247. ((int)Context::getContext()->shop->id ? ' AND id_shop = '.(int)Context::getContext()->shop->id : ''));
  248. // if no theme_meta are found, we must create them
  249. if (empty($theme_metas))
  250. {
  251. $metas = Db::getInstance()->executeS('SELECT id_meta FROM '._DB_PREFIX_.'meta');
  252. $metas_default = array();
  253. foreach ($metas as $meta)
  254. {
  255. $tmp_meta['id_meta'] = (int)$meta['id_meta'];
  256. $tmp_meta['left'] = 1;
  257. $tmp_meta['right'] = 1;
  258. $metas_default[] = $tmp_meta;
  259. }
  260. $theme->updateMetas($metas_default);
  261. $theme_metas = Db::getInstance()->executeS('SELECT ml.`title`, m.`page`, tm.`left_column` as `left`, tm.`right_column` as `right`, m.`id_meta`, tm.`id_theme_meta`
  262. FROM '._DB_PREFIX_.'theme_meta as tm
  263. LEFT JOIN '._DB_PREFIX_.'meta m ON (m.`id_meta` = tm.`id_meta`)
  264. LEFT JOIN '._DB_PREFIX_.'meta_lang ml ON(ml.id_meta = m.id_meta AND ml.id_lang = '.(int)$this->context->language->id.')
  265. WHERE tm.`id_theme` = '.(int)$this->object->id);
  266. }
  267. $image_url = '<img alt="preview" src="'.__PS_BASE_URI__.'themes/'.$theme->directory.'/preview.jpg">';
  268. foreach ($theme_metas as $key => &$meta)
  269. if (!isset($meta['title']) || !$meta['title'] || $meta['title'] == '')
  270. $meta['title'] = $meta['page'];
  271. $formated_metas = $theme_metas;
  272. }
  273. $selected_theme_dir = $this->object->directory;
  274. }
  275. foreach ($get_available_themes as $k => $dirname)
  276. {
  277. $available_theme_dir[$k]['value'] = $dirname;
  278. $available_theme_dir[$k]['label'] = $dirname;
  279. $available_theme_dir[$k]['id'] = $dirname;
  280. };
  281. $this->fields_form = array(
  282. 'tinymce' => false,
  283. 'legend' => array(
  284. 'title' => $this->l('Theme'),
  285. 'icon' => 'icon-picture'
  286. ),
  287. 'input' => array(
  288. array(
  289. 'type' => 'text',
  290. 'label' => $this->l('Name of the theme'),
  291. 'name' => 'name',
  292. 'required' => true,
  293. 'hint' => $this->l('Invalid characters:').' <>;=#{}',
  294. ),
  295. array(
  296. 'type' => 'file',
  297. 'label' => $this->l('Preview image for the theme'),
  298. 'name' => 'image_preview',
  299. 'display_image' => true,
  300. 'hint' => sprintf($this->l('Maximum image size: %1s'), Tools::formatBytes(Tools::getMaxUploadSize())),
  301. 'image' => $image_url,
  302. ),
  303. array(
  304. 'type' => 'switch',
  305. 'label' => $this->l('Default left column'),
  306. 'name' => 'default_left_column',
  307. 'hint' => $this->l('Choose a default behavior when displaying the column in a new page added by you or by a module.'),
  308. 'values' => array(
  309. array(
  310. 'id' => 'default_left_column_on',
  311. 'value' => 1,
  312. 'label' => $this->l('Yes')
  313. ),
  314. array(
  315. 'id' => 'default_left_column_off',
  316. 'value' => 0,
  317. 'label' => $this->l('No')
  318. )
  319. ),
  320. ),
  321. array(
  322. 'type' => 'switch',
  323. 'label' => $this->l('Default right column'),
  324. 'name' => 'default_right_column',
  325. 'hint' => $this->l('Choose a default behavior when displaying the column in a new page added by you or by a module.'),
  326. 'values' => array(
  327. array(
  328. 'id' => 'default_right_column_on',
  329. 'value' => 1,
  330. 'label' => $this->l('Yes')
  331. ),
  332. array(
  333. 'id' => 'default_right_column_off',
  334. 'value' => 0,
  335. 'label' => $this->l('No')
  336. )
  337. ),
  338. ),
  339. array(
  340. 'type' => 'text',
  341. 'label' => $this->l('Number of products per page'),
  342. 'name' => 'product_per_page',
  343. 'hint' => $this->l('This value will be used when activating the theme.'),
  344. )
  345. ),
  346. 'submit' => array(
  347. 'title' => $this->l('Save'),
  348. )
  349. );
  350. // adding a new theme, you can create a directory, and copy from an existing theme
  351. if ($this->display == 'add' || !Validate::isLoadedObject($this->object))
  352. {
  353. $this->fields_form['input'][] = array(
  354. 'type' => 'text',
  355. 'label' => $this->l('Name of the theme\'s directory'),
  356. 'name' => 'directory',
  357. 'required' => true,
  358. 'hint' => $this->l('If the directory does not exist, PrestaShop will create it automatically.'),
  359. );
  360. $theme_query = Theme::getThemes();
  361. $this->fields_form['input'][] = array(
  362. 'type' => 'select',
  363. 'name' => 'based_on',
  364. 'label' => $this->l('Copy missing files from existing theme'),
  365. 'hint' => $this->l('If you create a new theme from scratch, it is recommended that you use the files from the default theme as a foundation.'),
  366. 'options' => array(
  367. 'id' => 'id',
  368. 'name' => 'name',
  369. 'default' => array(
  370. 'value' => 0,
  371. 'label' => '-'
  372. ),
  373. 'query' => $theme_query,
  374. )
  375. );
  376. $this->fields_form['input'][] = array(
  377. 'type' => 'switch',
  378. 'label' => $this->l('Responsive'),
  379. 'name' => 'responsive',
  380. 'hint' => $this->l('Please indicate if the theme is adapted to all screen sizes (mobile, tablet, desktop).'),
  381. 'values' => array(
  382. array(
  383. 'id' => 'responsive_on',
  384. 'value' => 1,
  385. 'label' => $this->l('Yes')
  386. ),
  387. array(
  388. 'id' => 'responsive_off',
  389. 'value' => 0,
  390. 'label' => $this->l('No')
  391. )
  392. ),
  393. );
  394. }
  395. else
  396. $this->fields_form['input'][] = array(
  397. 'type' => 'radio',
  398. 'label' => $this->l('Directory'),
  399. 'name' => 'directory',
  400. 'required' => true,
  401. 'br' => true,
  402. 'values' => $available_theme_dir,
  403. 'selected' => $selected_theme_dir,
  404. 'hint' => $this->l('Please select a valid theme directory.'),
  405. );
  406. $list = '';
  407. if (Tools::getIsset('update'.$this->table))
  408. {
  409. $fields_list = array(
  410. 'title' => array(
  411. 'title' => $this->l('Meta'),
  412. 'align' => 'center',
  413. 'width' => 'auto'
  414. ),
  415. 'left' => array(
  416. 'title' => $this->l('Left column'),
  417. 'active' => 'left',
  418. 'type' => 'bool',
  419. 'ajax' => true
  420. ),
  421. 'right' => array(
  422. 'title' => $this->l('Right column'),
  423. 'active' => 'right',
  424. 'type' => 'bool',
  425. 'ajax' => true
  426. ),
  427. );
  428. $helper_list = New HelperList();
  429. $helper_list->tpl_vars = array('icon' => 'icon-columns');
  430. $helper_list->title = $this->l('Appearance of columns');
  431. $helper_list->no_link = true;
  432. $helper_list->shopLinkType = '';
  433. $helper_list->identifier = 'id_theme_meta';
  434. $helper_list->table = 'meta';
  435. $helper_list->tpl_vars['show_filters'] = false;
  436. $helper_list->currentIndex = $this->context->link->getAdminLink('AdminThemes', false);
  437. $helper_list->token = Tools::getAdminTokenLite('AdminThemes');
  438. $list = $helper_list->generateList($formated_metas, $fields_list);
  439. }
  440. return parent::renderForm().$list;
  441. }
  442. public function renderList()
  443. {
  444. return parent::renderList();
  445. }
  446. /**
  447. * copy $base_theme_dir into $target_theme_dir.
  448. *
  449. * @param string $base_theme_dir relative path to base dir
  450. * @param string $target_theme_dir relative path to target dir
  451. *
  452. * @return boolean true if success
  453. */
  454. protected static function copyTheme($base_theme_dir, $target_theme_dir)
  455. {
  456. $res = true;
  457. $base_theme_dir = Tools::normalizeDirectory($base_theme_dir);
  458. $base_dir = _PS_ALL_THEMES_DIR_.$base_theme_dir;
  459. $target_theme_dir = Tools::normalizeDirectory($target_theme_dir);
  460. $target_dir = _PS_ALL_THEMES_DIR_.$target_theme_dir;
  461. $files = scandir($base_dir);
  462. foreach ($files as $file)
  463. {
  464. if (!in_array($file[0], array('.', '..', '.svn')))
  465. {
  466. if (is_dir($base_dir.$file))
  467. {
  468. if (!is_dir($target_dir.$file))
  469. mkdir($target_dir.$file, Theme::$access_rights);
  470. $res &= AdminThemesController::copyTheme($base_theme_dir.$file, $target_theme_dir.$file);
  471. }
  472. elseif (!file_exists($target_dir.$file))
  473. $res &= copy($base_dir.$file, $target_dir.$file);
  474. }
  475. }
  476. return $res;
  477. }
  478. public function downloadAddonsThemes()
  479. {
  480. if (!$this->logged_on_addons)
  481. return false;
  482. if (!$this->isFresh(Theme::CACHE_FILE_CUSTOMER_THEMES_LIST, 86400))
  483. file_put_contents(_PS_ROOT_DIR_.Theme::CACHE_FILE_CUSTOMER_THEMES_LIST, Tools::addonsRequest('customer_themes'));
  484. $customer_themes_list = file_get_contents(_PS_ROOT_DIR_.Theme::CACHE_FILE_CUSTOMER_THEMES_LIST);
  485. if (!empty($customer_themes_list) && $customer_themes_list_xml = simplexml_load_string($customer_themes_list))
  486. {
  487. foreach ($customer_themes_list_xml->theme as $addons_theme)
  488. {
  489. //get addons theme if folder does not exist
  490. $ids_themes = Tools::unSerialize(Configuration::get('PS_ADDONS_THEMES_IDS'));
  491. if (!is_array($ids_themes) || (is_array($ids_themes) && !in_array((string)$addons_theme->id, $ids_themes)))
  492. {
  493. $zip_content = Tools::addonsRequest('module', array(
  494. 'id_module' => pSQL($addons_theme->id),
  495. 'username_addons' => pSQL(trim($this->context->cookie->username_addons)),
  496. 'password_addons' => pSQL(trim($this->context->cookie->password_addons)))
  497. );
  498. $uniqid = uniqid();
  499. $sandbox = _PS_CACHE_DIR_.'sandbox'.DIRECTORY_SEPARATOR.$uniqid.DIRECTORY_SEPARATOR;
  500. mkdir($sandbox);
  501. file_put_contents($sandbox.(string)$addons_theme->name.'.zip', $zip_content);
  502. if ($this->extractTheme($sandbox.(string)$addons_theme->name.'.zip', $sandbox))
  503. if ($theme_directory = $this->installTheme(Theme::UPLOADED_THEME_DIR_NAME, $sandbox, false))
  504. $ids_themes[$theme_directory] = (string)$addons_theme->id;
  505. Tools::deleteDirectory($sandbox);
  506. }
  507. Configuration::updateValue('PS_ADDONS_THEMES_IDS', serialize($ids_themes));
  508. }
  509. }
  510. }
  511. public function processAdd()
  512. {
  513. if (Tools::getValue('directory') == '' || Tools::getValue('name') == '')
  514. {
  515. $this->errors[] = $this->l('Form invalid');
  516. $this->display = 'form';
  517. return false;
  518. }
  519. if (($new_dir = Tools::getValue('directory')) != '')
  520. {
  521. if (!Validate::isDirName($new_dir))
  522. {
  523. $this->display = 'add';
  524. return !($this->errors[] = sprintf(Tools::displayError('"%s" is not a valid directory name'), $new_dir));
  525. }
  526. if (Theme::getByDirectory($new_dir))
  527. {
  528. $this->display = 'add';
  529. return !($this->errors[] = Tools::displayError('A directory with this name already exists.'));
  530. }
  531. if (mkdir(_PS_ALL_THEMES_DIR_.$new_dir, Theme::$access_rights))
  532. $this->confirmations[] = $this->l('The directory was successfully created.');
  533. if (0 !== $id_based = (int)Tools::getValue('based_on'))
  534. {
  535. $base_theme = new Theme($id_based);
  536. $this->copyTheme($base_theme->directory, $new_dir);
  537. $base_theme = new Theme((int)Tools::getValue('based_on'));
  538. }
  539. if (isset($_FILES['image_preview']) && $_FILES['image_preview']['error'] == 0)
  540. {
  541. if (@getimagesize($_FILES['image_preview']['tmp_name']) && !ImageManager::validateUpload($_FILES['image_preview'], Tools::getMaxUploadSize()))
  542. move_uploaded_file($_FILES['image_preview']['tmp_name'], _PS_ALL_THEMES_DIR_.$new_dir.'/preview.jpg');
  543. else
  544. {
  545. $this->errors[] = $this->l('Image is not valid.');
  546. $this->display = 'form';
  547. return false;
  548. }
  549. }
  550. }
  551. $theme = parent::processAdd();
  552. if ((int)$theme->product_per_page == 0)
  553. {
  554. $theme->product_per_page = 1;
  555. $theme->save();
  556. }
  557. if (is_object($theme) && (int)$theme->id > 0)
  558. {
  559. $metas = Meta::getMetas();
  560. foreach ($metas as &$meta)
  561. {
  562. $meta['left'] = $theme->default_left_column;
  563. $meta['right'] = $theme->default_right_column;
  564. }
  565. $theme->updateMetas($metas, true);
  566. }
  567. return $theme;
  568. }
  569. public function processUpdate()
  570. {
  571. if (Tools::getIsset('id_theme') && Tools::getIsset('name') && Tools::getIsset('directory'))
  572. {
  573. $theme = New Theme((int)Tools::getValue('id_theme'));
  574. $theme->name = Tools::getValue('name');
  575. $theme->directory = Tools::getValue('directory');
  576. $theme->default_left_column = Tools::getValue('default_left_column');
  577. $theme->default_right_column = Tools::getValue('default_right_column');
  578. $nb_product_per_page = (int)Tools::getValue('product_per_page');
  579. if ($nb_product_per_page == 0)
  580. $nb_product_per_page = 1;
  581. $theme->product_per_page = $nb_product_per_page;
  582. if ($this->context->shop->id_theme == (int)Tools::getValue('id_theme'))
  583. Configuration::updateValue('PS_PRODUCTS_PER_PAGE', $nb_product_per_page);
  584. if (isset($_FILES['image_preview']) && $_FILES['image_preview']['error'] == 0)
  585. {
  586. if (@getimagesize($_FILES['image_preview']['tmp_name']) && !ImageManager::validateUpload($_FILES['image_preview'], 300000))
  587. move_uploaded_file($_FILES['image_preview']['tmp_name'], _PS_ALL_THEMES_DIR_.$theme->directory.'/preview.jpg');
  588. else
  589. {
  590. $this->errors[] = $this->l('Image is not valid.');
  591. $this->display = 'form';
  592. return false;
  593. }
  594. }
  595. $theme->update();
  596. }
  597. Tools::redirectAdmin(Context::getContext()->link->getAdminLink('AdminThemes').'&conf=29');
  598. }
  599. protected function processUpdateOptions()
  600. {
  601. parent::processUpdateOptions();
  602. if (!count($this->errors))
  603. Tools::redirectAdmin(Context::getContext()->link->getAdminLink('AdminThemes').'&conf=6');
  604. }
  605. public function processDelete()
  606. {
  607. $obj = $this->loadObject();
  608. if ($obj)
  609. {
  610. if ($obj->isUsed())
  611. {
  612. $this->errors[] = $this->l('The theme is being used by at least one shop. Please choose another theme before continuing.');
  613. return false;
  614. }
  615. $themes = array();
  616. foreach (Theme::getThemes() as $theme)
  617. {
  618. if ($theme->id != $obj->id)
  619. $themes[] = $theme->directory;
  620. }
  621. if (is_dir(_PS_ALL_THEMES_DIR_.$obj->directory) && !in_array($obj->directory, $themes))
  622. Tools::deleteDirectory(_PS_ALL_THEMES_DIR_.$obj->directory.'/');
  623. $ids_themes = Tools::unSerialize(Configuration::get('PS_ADDONS_THEMES_IDS'));
  624. if (array_key_exists($obj->directory, $ids_themes))
  625. unset($ids_themes[$obj->directory]);
  626. $obj->removeMetas();
  627. }
  628. elseif ($obj === false && $theme_dir = Tools::getValue('theme_dir'))
  629. {
  630. $theme_dir = basename($theme_dir);
  631. if (Tools::deleteDirectory(_PS_ALL_THEMES_DIR_.$theme_dir.'/'))
  632. Tools::redirectAdmin(Context::getContext()->link->getAdminLink('AdminThemes').'&conf=2');
  633. else
  634. $this->errors[] = Tools::displayError('The folder cannot be deleted');
  635. }
  636. return parent::processDelete();
  637. }
  638. public function initPageHeaderToolbar()
  639. {
  640. parent::initPageHeaderToolbar();
  641. if (empty($this->display))
  642. {
  643. $this->page_header_toolbar_btn['import_theme'] = array(
  644. 'href' => self::$currentIndex.'&action=importtheme&token='.$this->token,
  645. 'desc' => $this->l('Add new theme', null, null, false),
  646. 'icon' => 'process-icon-new'
  647. );
  648. if ($this->context->mode)
  649. unset($this->toolbar_btn['new']);
  650. $this->page_header_toolbar_btn['export_theme'] = array(
  651. 'href' => self::$currentIndex.'&action=exporttheme&token='.$this->token,
  652. 'desc' => $this->l('Export theme', null, null, false),
  653. 'icon' => 'process-icon-export'
  654. );
  655. }
  656. if ($this->display == 'importtheme')
  657. $this->toolbar_title[] = $this->l('Import theme');
  658. elseif ($this->display == 'exporttheme')
  659. $this->toolbar_title[] = $this->l('Export theme');
  660. else
  661. $this->toolbar_title[] = $this->l('Theme');
  662. $title = implode(' '.Configuration::get('PS_NAVIGATION_PIPE').' ', $this->toolbar_title);
  663. $this->page_header_toolbar_title = $title;
  664. }
  665. private function checkParentClass($name)
  666. {
  667. if (!$obj = Module::getInstanceByName($name))
  668. return false;
  669. if (is_callable(array($obj, 'validateOrder')))
  670. return false;
  671. if (is_callable(array($obj, 'getDateBetween')))
  672. return false;
  673. if (is_callable(array($obj, 'getGridEngines')))
  674. return false;
  675. if (is_callable(array($obj, 'getGraphEngines')))
  676. return false;
  677. if (is_callable(array($obj, 'hookAdminStatsModules')))
  678. return false;
  679. else
  680. return true;
  681. }
  682. private function checkNames()
  683. {
  684. $author = Tools::getValue('name');
  685. $theme_name = Tools::getValue('theme_name');
  686. if (!$author || !Validate::isGenericName($author) || strlen($author) > self::MAX_NAME_LENGTH)
  687. $this->errors[] = $this->l('Please enter a valid author name');
  688. elseif (!$theme_name || !Validate::isGenericName($theme_name) || strlen($theme_name) > self::MAX_NAME_LENGTH)
  689. $this->errors[] = $this->l('Please enter a valid theme name');
  690. if (count($this->errors) > 0)
  691. return false;
  692. return true;
  693. }
  694. private function checkDocumentation()
  695. {
  696. $extensions = array(
  697. '.pdf',
  698. '.txt'
  699. );
  700. if (isset($_FILES['documentation']) && $_FILES['documentation']['name'] != '')
  701. {
  702. $extension = strrchr($_FILES['documentation']['name'], '.');
  703. $name = Tools::getValue('documentationName');
  704. if (!in_array($extension, $extensions))
  705. $this->errors[] = $this->l('File extension must be .txt or .pdf');
  706. elseif ($_FILES['documentation']['error'] > 0 || $_FILES['documentation']['size'] > 1048576)
  707. $this->errors[] = $this->l('An error occurred during documentation upload');
  708. elseif (!$name || !Validate::isGenericName($name) || strlen($name) > self::MAX_NAME_LENGTH)
  709. $this->errors[] = $this->l('Please enter a valid documentation name');
  710. }
  711. if (count($this->errors) > 0)
  712. return false;
  713. return true;
  714. }
  715. private function checkVersionsAndCompatibility()
  716. {
  717. $exp = '#^[0-9]+[.]+[0-9.]*[0-9]$#';
  718. if (!preg_match('#^[0-9][.][0-9]$#', Tools::getValue('theme_version')) ||
  719. !preg_match($exp, Tools::getValue('compa_from')) || !preg_match($exp, Tools::getValue('compa_to')) ||
  720. version_compare(Tools::getValue('compa_from'), Tools::getValue('compa_to')) == 1
  721. )
  722. $this->errors[] = $this->l('Syntax error on version field. Only digits and periods (.) are allowed, and the compatibility version should be increasing or at least be equal to the previous version.');
  723. if (count($this->errors) > 0)
  724. return false;
  725. return true;
  726. }
  727. private function checkPostedDatas()
  728. {
  729. $mail = Tools::getValue('email');
  730. $website = Tools::getValue('website');
  731. if ($mail && !preg_match('#^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,6}$#', $mail))
  732. $this->errors[] = $this->l('There is an error in your email syntax!');
  733. elseif ($website && (!Validate::isURL($website) || !Validate::isAbsoluteUrl($website)))
  734. $this->errors[] = $this->l('There is an error in your URL syntax!');
  735. elseif (!$this->checkVersionsAndCompatibility() || !$this->checkNames() || !$this->checkDocumentation())
  736. return false;
  737. else
  738. return true;
  739. return false;
  740. }
  741. private function archiveThisFile($obj, $file, $server_path, $archive_path)
  742. {
  743. if (is_dir($server_path.$file))
  744. {
  745. $dir = scandir($server_path.$file);
  746. foreach ($dir as $row)
  747. {
  748. if ($row != '.' && $row != '..')
  749. $this->archiveThisFile($obj, $row, $server_path.$file.'/', $archive_path.$file.'/');
  750. }
  751. }
  752. elseif (!$obj->addFile($server_path.$file, $archive_path.$file))
  753. $this->error = true;
  754. }
  755. private function generateArchive()
  756. {
  757. $zip = new ZipArchive();
  758. $zip_file_name = md5(time()).'.zip';
  759. if ($zip->open(_PS_CACHE_DIR_.$zip_file_name, ZipArchive::OVERWRITE) === true)
  760. {
  761. if (!$zip->addFromString('Config.xml', $this->xml_file))
  762. $this->errors[] = $this->l('Cannot create config file.');
  763. if (isset($_FILES['documentation']))
  764. if (!empty($_FILES['documentation']['tmp_name']) &&
  765. !empty($_FILES['documentation']['name']) &&
  766. !$zip->addFile($_FILES['documentation']['tmp_name'], 'doc/'.$_FILES['documentation']['name']))
  767. $this->errors[] = $this->l('Cannot copy documentation.');
  768. $given_path = realpath(_PS_ALL_THEMES_DIR_.Tools::getValue('theme_directory'));
  769. if ($given_path !== false)
  770. {
  771. $ps_all_theme_dir_lenght = strlen(realpath(_PS_ALL_THEMES_DIR_));
  772. $to_compare_path = substr($given_path, 0, $ps_all_theme_dir_lenght);
  773. if ($to_compare_path != realpath(_PS_ALL_THEMES_DIR_))
  774. $this->errors[] = $this->l('Wrong theme directory path');
  775. else
  776. {
  777. $this->archiveThisFile($zip, Tools::getValue('theme_directory'), _PS_ALL_THEMES_DIR_, 'themes/');
  778. foreach ($this->to_export as $row)
  779. {
  780. if (!in_array($row, $this->native_modules))
  781. $this->archiveThisFile($zip, $row, _PS_ROOT_DIR_.'/modules/', 'modules/');
  782. }
  783. }
  784. }
  785. else
  786. $this->errors[] = $this->l('Wrong theme directory path');
  787. $zip->close();
  788. if (!$this->errors)
  789. {
  790. if (ob_get_length() > 0)
  791. ob_end_clean();
  792. ob_start();
  793. header('Pragma: public');
  794. header('Expires: 0');
  795. header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  796. header('Cache-Control: public');
  797. header('Content-Description: File Transfer');
  798. header('Content-type: application/octet-stream');
  799. header('Content-Disposition: attachment; filename="'.$zip_file_name.'"');
  800. header('Content-Transfer-Encoding: binary');
  801. ob_end_flush();
  802. readfile(_PS_CACHE_DIR_.$zip_file_name);
  803. @unlink(_PS_CACHE_DIR_.$zip_file_name);
  804. exit;
  805. }
  806. }
  807. $this->errors[] = $this->l('An error occurred during the archive generation');
  808. }
  809. private function generateXML($theme_to_export, $metas)
  810. {
  811. $theme = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><!-- Copyright PrestaShop --><theme></theme>');
  812. $theme->addAttribute('version', Tools::getValue('theme_version'));
  813. $theme->addAttribute('name', Tools::htmlentitiesUTF8(Tools::getValue('theme_name')));
  814. $theme->addAttribute('directory', Tools::htmlentitiesUTF8(Tools::getValue('theme_directory')));
  815. $author = $theme->addChild('author');
  816. $author->addAttribute('name', Tools::htmlentitiesUTF8(Tools::getValue('name')));
  817. $author->addAttribute('email', Tools::htmlentitiesUTF8(Tools::getValue('email')));
  818. $author->addAttribute('url', Tools::htmlentitiesUTF8(Tools::getValue('website')));
  819. $descriptions = $theme->addChild('descriptions');
  820. $languages = Language::getLanguages();
  821. foreach ($languages as $language)
  822. {
  823. $val = Tools::htmlentitiesUTF8(Tools::getValue('body_title_'.$language['id_lang']));
  824. $description = $descriptions->addChild('description', Tools::htmlentitiesUTF8($val));
  825. $description->addAttribute('iso', $language['iso_code']);
  826. }
  827. $variations = $theme->addChild('variations');
  828. $variation = $variations->addChild('variation');
  829. $variation->addAttribute('name', Tools::htmlentitiesUTF8(Tools::getValue('theme_name')));
  830. $variation->addAttribute('directory', Tools::getValue('theme_directory'));
  831. $variation->addAttribute('responsive', $theme_to_export->responsive);
  832. $variation->addAttribute('default_left_column', $theme_to_export->default_left_column);
  833. $variation->addAttribute('default_right_column', $theme_to_export->default_right_column);
  834. $variation->addAttribute('product_per_page', $theme_to_export->product_per_page);
  835. $variation->addAttribute('from', Tools::getValue('compa_from'));
  836. $variation->addAttribute('to', Tools::getValue('compa_to'));
  837. $docs = $theme->addChild('docs');
  838. if (isset($this->user_doc))
  839. foreach ($this->user_doc as $row)
  840. {
  841. $array = explode('¤', $row);
  842. $doc = $docs->addChild('doc');
  843. $doc->addAttribute('name', $array[0]);
  844. $doc->addAttribute('path', $array[1]);
  845. }
  846. $metas_xml = $theme->addChild('metas');
  847. foreach ($metas as $row)
  848. {
  849. $meta_obj = New Meta((int)$row['id_meta']);
  850. $meta_xml = $metas_xml->addChild('meta');
  851. $meta_xml->addAttribute('meta_page', $meta_obj->page);
  852. $meta_xml->addAttribute('left', $row['left_column']);
  853. $meta_xml->addAttribute('right', $row['right_column']);
  854. }
  855. $modules = $theme->addChild('modules');
  856. if (isset($this->to_export))
  857. foreach ($this->to_export as $row)
  858. {
  859. if (!in_array($row, $this->native_modules))
  860. {
  861. $module = $modules->addChild('module');
  862. $module->addAttribute('action', 'install');
  863. $module->addAttribute('name', $row);
  864. }
  865. }
  866. foreach ($this->to_enable as $row)
  867. {
  868. $module = $modules->addChild('module');
  869. $module->addAttribute('action', 'enable');
  870. $module->addAttribute('name', $row);
  871. }
  872. foreach ($this->to_disable as $row)
  873. {
  874. $module = $modules->addChild('module');
  875. $module->addAttribute('action', 'disable');
  876. $module->addAttribute('name', $row);
  877. }
  878. $hooks = $modules->addChild('hooks');
  879. foreach ($this->to_hook as $row)
  880. {
  881. $array = explode(';', $row);
  882. $hook = $hooks->addChild('hook');
  883. $hook->addAttribute('module', $array[0]);
  884. $hook->addAttribute('hook', $array[1]);
  885. $hook->addAttribute('position', $array[2]);
  886. if (!empty($array[3]))
  887. $hook->addAttribute('exceptions', $array[3]);
  888. }
  889. $images = $theme->addChild('images');
  890. foreach ($this->image_list as $row)
  891. {
  892. $array = explode(';', $row);
  893. $image = $images->addChild('image');
  894. $image->addAttribute('name', Tools::htmlentitiesUTF8($array[0]));
  895. $image->addAttribute('width', $array[1]);
  896. $image->addAttribute('height', $array[2]);
  897. $image->addAttribute('products', $array[3]);
  898. $image->addAttribute('categories', $array[4]);
  899. $image->addAttribute('manufacturers', $array[5]);
  900. $image->addAttribute('suppliers', $array[6]);
  901. $image->addAttribute('scenes', $array[7]);
  902. }
  903. $this->xml_file = $theme->asXML();
  904. }
  905. public function processExportTheme()
  906. {
  907. if (Tools::isSubmit('name'))
  908. {
  909. if ($this->checkPostedDatas())
  910. {
  911. $filename = Tools::htmlentitiesUTF8($_FILES['documentation']['name']);
  912. $name = Tools::htmlentitiesUTF8(Tools::getValue('documentationName'));
  913. $this->user_doc = array($name.'¤doc/'.$filename);
  914. $table = Db::getInstance()->executeS('
  915. SELECT name, width, height, products, categories, manufacturers, suppliers, scenes
  916. FROM `'._DB_PREFIX_.'image_type`');
  917. $this->image_list = array();
  918. foreach ($table as $row)
  919. {
  920. $this->image_list[] = $row['name'].';'.$row['width'].';'.$row['height'].';'.
  921. ($row['products'] == 1 ? 'true' : 'false').';'.
  922. ($row['categories'] == 1 ? 'true' : 'false').';'.
  923. ($row['manufacturers'] == 1 ? 'true' : 'false').';'.
  924. ($row['suppliers'] == 1 ? 'true' : 'false').';'.
  925. ($row['scenes'] == 1 ? 'true' : 'false');
  926. }
  927. $id_shop = Db::getInstance()->getValue('SELECT `id_shop` FROM `'._DB_PREFIX_.'shop` WHERE `id_theme` = '.(int)Tools::getValue('id_theme_export'));
  928. // Select the list of module for this shop
  929. $this->module_list = Db::getInstance()->executeS('
  930. SELECT m.`id_module`, m.`name`, m.`active`, ms.`id_shop`
  931. FROM `'._DB_PREFIX_.'module` m
  932. LEFT JOIN `'._DB_PREFIX_.'module_shop` ms On (m.`id_module` = ms.`id_module`)
  933. WHERE ms.`id_shop` = '.(int)$id_shop.'
  934. ');
  935. // Select the list of hook for this shop
  936. $this->hook_list = Db::getInstance()->executeS('
  937. SELECT h.`id_hook`, h.`name` as name_hook, hm.`position`, hm.`id_module`, m.`name` as name_module, GROUP_CONCAT(hme.`file_name`, ",") as exceptions
  938. FROM `'._DB_PREFIX_.'hook` h
  939. LEFT JOIN `'._DB_PREFIX_.'hook_module` hm ON hm.`id_hook` = h.`id_hook`
  940. LEFT JOIN `'._DB_PREFIX_.'module` m ON hm.`id_module` = m.`id_module`
  941. LEFT OUTER JOIN `'._DB_PREFIX_.'hook_module_exceptions` hme ON (hme.`id_module` = hm.`id_module` AND hme.`id_hook` = h.`id_hook`)
  942. WHERE hm.`id_shop` = '.(int)$id_shop.'
  943. GROUP BY `id_module`, `id_hook`
  944. ORDER BY `name_module`
  945. ');
  946. $this->native_modules = $this->getNativeModule();
  947. foreach ($this->hook_list as &$row)
  948. $row['exceptions'] = trim(preg_replace('/(,,+)/', ',', $row['exceptions']), ',');
  949. $this->to_install = array();
  950. $this->to_enable = array();
  951. $this->to_hook = array();
  952. foreach ($this->module_list as $array)
  953. {
  954. if (!self::checkParentClass($array['name']))
  955. continue;
  956. if (in_array($array['name'], $this->native_modules))
  957. {
  958. if ($array['active'] == 1)
  959. $this->to_enable[] = $array['name'];
  960. else
  961. $this->to_disable[] = $array['name'];
  962. }
  963. elseif ($array['active'] == 1)
  964. $this->to_install[] = $array['name'];
  965. }
  966. foreach ($this->native_modules as $str)
  967. {
  968. $flag = 0;
  969. if (!self::checkParentClass($str))
  970. continue;
  971. foreach ($this->module_list as $tmp)
  972. {
  973. if (in_array($str, $tmp))
  974. {
  975. $flag = 1;
  976. break;
  977. }
  978. }
  979. if ($flag == 0)
  980. $this->to_disable[] = $str;
  981. }
  982. foreach ($_POST as $key => $value)
  983. if (strncmp($key, 'modulesToExport_module', strlen('modulesToExport_module')) == 0)
  984. $this->to_export[] = $value;
  985. if ($this->to_install)
  986. foreach ($this->to_install as $string)
  987. {
  988. foreach ($this->hook_list as $tmp)
  989. {
  990. if ($tmp['name_module'] == $string)
  991. $this->to_hook[] = $string.';'.$tmp['name_hook'].';'.$tmp['position'].';'.$tmp['exceptions'];
  992. }
  993. }
  994. if ($this->to_enable)
  995. foreach ($this->to_enable as $string)
  996. {
  997. foreach ($this->hook_list as $tmp)
  998. {
  999. if ($tmp['name_module'] == $string)
  1000. $this->to_hook[] = $string.';'.$tmp['name_hook'].';'.$tmp['position'].';'.$tmp['exceptions'];
  1001. }
  1002. }
  1003. $theme_to_export = New Theme((int)Tools::getValue('id_theme_export'));
  1004. $metas = $theme_to_export->getMetas();
  1005. $this->generateXML($theme_to_export, $metas);
  1006. $this->generateArchive();
  1007. }
  1008. else
  1009. $this->display = 'exporttheme';
  1010. }
  1011. else
  1012. $this->display = 'exporttheme';
  1013. }
  1014. private function renderExportTheme1()
  1015. {
  1016. $to_install = array();
  1017. $module_list = Db::getInstance()->executeS('
  1018. SELECT m.`id_module`, m.`name`, m.`active`, ms.`id_shop`
  1019. FROM `'._DB_PREFIX_.'module` m
  1020. LEFT JOIN `'._DB_PREFIX_.'module_shop` ms On (m.`id_module` = ms.`id_module`)
  1021. WHERE ms.`id_shop` = '.(int)$this->context->shop->id.'
  1022. ');
  1023. // Select the list of hook for this shop
  1024. $hook_list = Db::getInstance()->executeS('
  1025. SELECT h.`id_hook`, h.`name` as name_hook, hm.`position`, hm.`id_module`, m.`name` as name_module, GROUP_CONCAT(hme.`file_name`, ",") as exceptions
  1026. FROM `'._DB_PREFIX_.'hook` h
  1027. LEFT JOIN `'._DB_PREFIX_.'hook_module` hm ON hm.`id_hook` = h.`id_hook`
  1028. LEFT JOIN `'._DB_PREFIX_.'module` m ON hm.`id_module` = m.`id_module`
  1029. LEFT OUTER JOIN `'._DB_PREFIX_.'hook_module_exceptions` hme ON (hme.`id_module` = hm.`id_module` AND hme.`id_hook` = h.`id_hook`)
  1030. WHERE hm.`id_shop` = '.(int)$this->context->shop->id.'
  1031. GROUP BY `id_module`, `id_hook`
  1032. ORDER BY `name_module`
  1033. ');
  1034. foreach ($hook_list as &$row)
  1035. $row['exceptions'] = trim(preg_replace('/(,,+)/', ',', $row['exceptions']), ',');
  1036. $native_modules = $this->getNativeModule();
  1037. foreach ($module_list as $array)
  1038. {
  1039. if (!self::checkParentClass($array['name']))
  1040. continue;
  1041. if (in_array($array['name'], $native_modules))
  1042. {
  1043. if ($array['active'] == 1)
  1044. $to_enable[] = $array['name'];
  1045. else
  1046. $to_disable[] = $array['name'];
  1047. }
  1048. elseif ($array['active'] == 1)
  1049. $to_install[] = $array['name'];
  1050. }
  1051. foreach ($native_modules as $str)
  1052. {
  1053. $flag = 0;
  1054. if (!$this->checkParentClass($str))
  1055. continue;
  1056. foreach ($module_list as $tmp)
  1057. {
  1058. if (in_array($str, $tmp))
  1059. {
  1060. $flag = 1;
  1061. break;
  1062. }
  1063. }
  1064. if ($flag == 0)
  1065. $to_disable[] = $str;
  1066. }
  1067. $employee = $this->context->employee;
  1068. $mail = Tools::getValue('email') ? Tools::getValue('email') : $employee->email;
  1069. $author = Tools::getValue('author_name') ? Tools::getValue('author_name') : $employee->firstname.' '.$employee->lastname;
  1070. $website = Tools::getValue('website') ? Tools::getValue('website') : Tools::getHttpHost(true);
  1071. $this->formatHelperArray($to_install);
  1072. $theme = New Theme(Tools::getValue('id_theme_export'));
  1073. $fields_form = array(
  1074. 'form' => array(
  1075. 'tinymce' => false,
  1076. 'legend' => array(
  1077. 'title' => $this->l('Theme configuration'),
  1078. 'icon' => 'icon-picture'
  1079. ),
  1080. 'input' => array(
  1081. array(
  1082. 'type' => 'hidden',
  1083. 'name' => 'id_theme_export'
  1084. ),
  1085. array(
  1086. 'type' => 'text',
  1087. 'name' => 'name',
  1088. 'label' => $this->l('Name'),
  1089. ),
  1090. array(
  1091. 'type' => 'text',
  1092. 'name' => 'email',
  1093. 'label' => $this->l('Email'),
  1094. ),
  1095. array(
  1096. 'type' => 'text',
  1097. 'name' => 'website',
  1098. 'label' => $this->l('Website'),
  1099. ),
  1100. array(
  1101. 'type' => 'text',
  1102. 'name' => 'theme_name',
  1103. 'label' => $this->l('Theme name'),
  1104. ),
  1105. array(
  1106. 'type' => 'text',
  1107. 'name' => 'theme_directory',
  1108. 'label' => $this->l('Theme directory'),
  1109. ),
  1110. array(
  1111. 'type' => 'text',
  1112. 'name' => 'body_title',
  1113. 'lang' => true,
  1114. 'label' => $this->l('Description'),
  1115. ),
  1116. array(
  1117. 'type' => 'text',
  1118. 'name' => 'theme_version',
  1119. 'label' => $this->l('Theme version'),
  1120. ),
  1121. array(
  1122. 'type' => 'text',
  1123. 'name' => 'compa_from',
  1124. 'label' => $this->l('Compatible from'),
  1125. ),
  1126. array(
  1127. 'type' => 'text',
  1128. 'name' => 'compa_to',
  1129. 'label' => $this->l('Compatible to'),
  1130. ),
  1131. array(
  1132. 'type' => 'file',
  1133. 'name' => 'documentation',
  1134. 'label' => $this->l('Documentation'),
  1135. ),
  1136. array(
  1137. 'type' => 'text',
  1138. 'name' => 'documentationName',
  1139. 'label' => $this->l('Documentation name'),
  1140. ),
  1141. ),
  1142. 'submit' => array(
  1143. 'title' => $this->l('Save'),
  1144. )
  1145. )
  1146. );
  1147. if (count($to_install) > 0)
  1148. {
  1149. $fields_form['form']['input'][] = array(
  1150. 'type' => 'checkbox',
  1151. 'label' => $this->l('Select the theme\'s modules that you wish to export'),
  1152. 'values' => array(
  1153. 'query' => $this->formatHelperArray($to_install),
  1154. 'id' => 'id',
  1155. 'name' => 'name'
  1156. ),
  1157. 'name' => 'modulesToExport',
  1158. );
  1159. }
  1160. $default_language = (int)$this->context->language->id;
  1161. $languages = $this->getLanguages();
  1162. foreach ($languages as $language)
  1163. $fields_value['body_title'][$language['id_lang']] = '';
  1164. $helper = new HelperForm();
  1165. $helper->languages = $languages;
  1166. $helper->default_form_language = $default_language;
  1167. $fields_value['name'] = $author;
  1168. $fields_value['email'] = $mail;
  1169. $fields_value['website'] = $website;
  1170. $fields_value['theme_name'] = $theme->name;
  1171. $fields_value['theme_directory'] = $theme->directory;
  1172. $fields_value['theme_version'] = '1.0';
  1173. $fields_value['compa_from'] = _PS_VERSION_;
  1174. $fields_value['compa_to'] = _PS_VERSION_;
  1175. $fields_value['id_theme_export'] = Tools::getValue('id_theme_export');
  1176. $fields_value['documentationName'] = $this->l('documentation');
  1177. $toolbar_btn['save'] = array(
  1178. 'href' => '',
  1179. 'desc' => $this->l('Save')
  1180. );
  1181. $helper->currentIndex = $this->context->link->getAdminLink('AdminThemes', false).'&action=exporttheme';
  1182. $helper->token = Tools::getAdminTokenLite('AdminThemes');
  1183. $helper->show_toolbar = true;
  1184. $helper->fields_value = $fields_value;
  1185. $helper->toolbar_btn = $toolbar_btn;
  1186. $helper->override_folder = $this->tpl_folder;
  1187. return $helper->generateForm(array($fields_form));
  1188. }
  1189. public function renderExportTheme()
  1190. {
  1191. if (Tools::getIsset('id_theme_export') && (int)Tools::getValue('id_theme_export') > 0)
  1192. return $this->renderExportTheme1();
  1193. $theme_list = Theme::getThemes();
  1194. $fields_form = array(
  1195. 'form' => array(
  1196. 'tinymce' => false,
  1197. 'legend' => array(
  1198. 'title' => $this->l('Theme'),
  1199. 'icon' => 'icon-picture'
  1200. ),
  1201. 'input' => array(
  1202. array(
  1203. 'type' => 'select',
  1204. 'name' => 'id_theme_export',
  1205. 'label' => $this->l('Choose the theme that you want to export'),
  1206. 'options' => array(
  1207. 'id' => 'id',
  1208. 'name' => 'name',
  1209. 'query' => $theme_list,
  1210. )
  1211. ),
  1212. ),
  1213. 'submit' => array(
  1214. 'title' => $this->l('Save'),
  1215. )
  1216. )
  1217. );
  1218. $toolbar_btn['save'] = array(
  1219. 'href' => '#',
  1220. 'desc' => $this->l('Export')
  1221. );
  1222. $fields_value['id_theme_export'] = array();
  1223. $helper = new HelperForm();
  1224. $helper->currentIndex = $this->context->link->getAdminLink('AdminThemes', false).'&action=exporttheme';
  1225. $helper->token = Tools::getAdminTokenLite('AdminThemes');
  1226. $helper->show_toolbar = true;
  1227. $helper->fields_value = $fields_value;
  1228. $helper->toolbar_btn = $toolbar_btn;
  1229. $helper->override_folder = $this->tpl_folder;
  1230. return $helper->generateForm(array($fields_form));
  1231. }
  1232. private function checkXmlFields($xml_file)
  1233. {
  1234. if (!file_exists($xml_file) || !$xml = simplexml_load_file($xml_file))
  1235. return false;
  1236. if (!$xml['version'] || !$xml['name'])
  1237. return false;
  1238. foreach ($xml->variations->variation as $val)
  1239. {
  1240. if (!$val['name'] || !$val['directory'] || !$val['from'] || !$val['to'])
  1241. return false;
  1242. }
  1243. foreach ($xml->modules->module as $val)
  1244. {
  1245. if (!$val['action'] || !$val['name'])
  1246. return false;
  1247. }
  1248. foreach ($xml->modules->hooks->hook as $val)
  1249. {
  1250. if (!$val['module'] || !$val['hook'] || !$val['position'])
  1251. return false;
  1252. }
  1253. return true;
  1254. }
  1255. private function recurseCopy($src, $dst)
  1256. {
  1257. if (!$dir = opendir($src))
  1258. return;
  1259. if (!file_exists($dst))
  1260. mkdir($dst);
  1261. while (($file = readdir($dir)) !== false)
  1262. {
  1263. if (strncmp($file, '.', 1) != 0)
  1264. {
  1265. if (is_dir($src.'/'.$file))
  1266. self::recurseCopy($src.'/'.$file, $dst.'/'.$file);
  1267. elseif (is_readable($src.'/'.$file) && $file != 'Thumbs.db' && $file != '.DS_Store' && substr($file, -1) != '~')
  1268. copy($src.'/'.$file, $dst.'/'.$file);
  1269. }
  1270. }
  1271. closedir($dir);
  1272. }
  1273. public function processImportTheme()
  1274. {
  1275. $this->display = 'importtheme';
  1276. if ($this->context->mode == Context::MODE_HOST)
  1277. return true;
  1278. if (isset($_FILES['themearchive']) && isset($_POST['filename']) && Tools::isSubmit('theme_archive_server'))
  1279. {
  1280. $uniqid = uniqid();
  1281. $sandbox = _PS_CACHE_DIR_.'sandbox'.DIRECTORY_SEPARATOR.$uniqid.DIRECTORY_SEPARATOR;
  1282. mkdir($sandbox);
  1283. $archive_uploaded = false;
  1284. if (Tools::getValue('filename') != '')
  1285. {
  1286. $uploader = new Uploader('themearchive');
  1287. $uploader->setCheckFileSize(false);
  1288. $uploader->setAcceptTypes(array('zip'));
  1289. $uploader->setSavePath($sandbox);
  1290. $file = $uploader->process(Theme::UPLOADED_THEME_DIR_NAME.'.zip');
  1291. if ($file[0]['error'] === 0)
  1292. {
  1293. if (Tools::ZipTest($sandbox.Theme::UPLOADED_THEME_DIR_NAME.'.zip'))
  1294. $archive_uploaded = true;
  1295. else
  1296. $this->errors[] = $this->l('Zip file seems to be broken');
  1297. }
  1298. else
  1299. $this->errors[] = $file[0]['error'];
  1300. }
  1301. elseif (Tools::getValue('themearchiveUrl') != '')
  1302. {
  1303. if (!Validate::isModuleUrl($url = Tools::getValue('themearchiveUrl'), $this->errors))
  1304. $this->errors[] = $this->l('Only zip files are allowed');
  1305. elseif (!Tools::copy($url, $sandbox.Theme::UPLOADED_THEME_DIR_NAME.'.zip'))
  1306. $this->errors[] = $this->l('Error during the file download');
  1307. elseif (Tools::ZipTest($sandbox.Theme::UPLOADED_THEME_DIR_NAME.'.zip'))
  1308. $archive_uploaded = true;
  1309. else
  1310. $this->errors[] = $this->l('Zip file seems to be broken');
  1311. }
  1312. elseif (Tools::getValue('theme_archive_server') != '')
  1313. {
  1314. $filename = _PS_ALL_THEMES_DIR_.Tools::getValue('theme_archive_server');
  1315. if (substr($filename, -4) != '.zip')
  1316. $this->errors[] = $this->l('Only zip files are allowed');
  1317. elseif (!copy($filename, $sandbox.Theme::UPLOADED_THEME_DIR_NAME.'.zip'))
  1318. $this->errors[] = $this->l('An error has occurred during the file copy.');
  1319. elseif (Tools::ZipTest($sandbox.Theme::UPLOADED_THEME_DIR_NAME.'.zip'))
  1320. $archive_uploaded = true;
  1321. else
  1322. $this->errors[] = $this->l('Zip file seems to be broken');
  1323. }
  1324. else
  1325. $this->errors[] = $this->l('You must upload or enter a location of your zip');
  1326. if ($archive_uploaded)
  1327. if ($this->extractTheme($sandbox.Theme::UPLOADED_THEME_DIR_NAME.'.zip', $sandbox))
  1328. $this->installTheme(Theme::UPLOADED_THEME_DIR_NAME, $sandbox);
  1329. Tools::deleteDirectory($sandbox);
  1330. if (count($this->errors) > 0)
  1331. $this->display = 'importtheme';
  1332. else
  1333. Tools::redirectAdmin(Context::getContext()->link->getAdminLink('AdminThemes').'&conf=18');
  1334. }
  1335. }
  1336. protected function extractTheme($theme_zip_file, $sandbox)
  1337. {
  1338. if (Tools::ZipExtract($theme_zip_file, $sandbox.Theme::UPLOADED_THEME_DIR_NAME.'/'))
  1339. ret

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