1<?php 2/** 3 * Copyright (C) 2017-2019 thirty bees 4 * Copyright (C) 2007-2016 PrestaShop SA 5 * 6 * thirty bees is an extension to the PrestaShop software by PrestaShop SA. 7 * 8 * NOTICE OF LICENSE 9 * 10 * This source file is subject to the Academic Free License (AFL 3.0) 11 * that is bundled with this package in the file LICENSE.md. 12 * It is also available through the world-wide-web at this URL: 13 * https://opensource.org/licenses/afl-3.0.php 14 * If you did not receive a copy of the license and are unable to 15 * obtain it through the world-wide-web, please send an email 16 * to license@thirtybees.com so we can send you a copy immediately. 17 * 18 * @author thirty bees <modules@thirtybees.com> 19 * @author PrestaShop SA <contact@prestashop.com> 20 * @copyright 2017-2019 thirty bees 21 * @copyright 2007-2016 PrestaShop SA 22 * @license Academic Free License (AFL 3.0) 23 * PrestaShop is an internationally registered trademark of PrestaShop SA. 24 */ 25 26if (!defined('_TB_VERSION_')) { 27 exit; 28} 29 30/** 31 * Class ThemeConfigurator 32 */ 33class ThemeConfigurator extends Module 34{ 35 // @codingStandardsIgnoreStart 36 /** @var int $max_image_size */ 37 protected $max_image_size = 1048576; 38 /** @var mixed $default_language */ 39 protected $default_language; 40 /** @var array $languages */ 41 protected $languages; 42 /** @var string $admin_tpl_path */ 43 public $admin_tpl_path; 44 /** @var string $hooks_tpl_path */ 45 public $hooks_tpl_path; 46 /** @var string $uploads_path */ 47 public $uploads_path; 48 /** @var string $secure_key */ 49 public $secure_key; 50 /** @var string $module_path */ 51 public $module_path; 52 /** @var string $module_url */ 53 public $module_url = ''; 54 /** @var array $fields_form */ 55 public $fields_form; 56 // @codingStandardsIgnoreEnd 57 58 /** 59 * ThemeConfigurator constructor. 60 */ 61 public function __construct() 62 { 63 $this->name = 'themeconfigurator'; 64 $this->tab = 'front_office_features'; 65 $this->version = '3.0.9'; 66 $this->author = 'thirty bees'; 67 $this->need_instance = 0; 68 $this->bootstrap = true; 69 $this->secure_key = Tools::encrypt($this->name); 70 $this->default_language = Language::getLanguage(Configuration::get('PS_LANG_DEFAULT')); 71 $this->languages = Language::getLanguages(); 72 parent::__construct(); 73 $this->displayName = $this->l('Theme Configurator'); 74 $this->description = $this->l('Configure the main elements of your theme.'); 75 $this->tb_versions_compliancy = '> 1.0.0'; 76 $this->tb_min_version = '1.0.0'; 77 $this->module_path = _PS_MODULE_DIR_.$this->name.'/'; 78 if (isset(Context::getContext()->employee->id) && Context::getContext()->employee->id && Context::getContext()->link instanceof Link) { 79 $this->module_url = Context::getContext()->link->getAdminLink('AdminModules', true).'&'.http_build_query([ 80 'configure' => $this->name, 81 'tab_module' => $this->tab, 82 'module_name' => $this->name, 83 ]); 84 } 85 $this->uploads_path = _PS_MODULE_DIR_.$this->name.'/img/'; 86 $this->admin_tpl_path = _PS_MODULE_DIR_.$this->name.'/views/templates/admin/'; 87 $this->hooks_tpl_path = _PS_MODULE_DIR_.$this->name.'/views/templates/hooks/'; 88 } 89 90 /** 91 * Install the module 92 * 93 * @return bool 94 */ 95 public function install() 96 { 97 $themesColors = [ 98 'theme1', 99 'theme2', 100 'theme3', 101 'theme4', 102 'theme5', 103 'theme6', 104 'theme7', 105 'theme8', 106 'theme9', 107 ]; 108 $themesFonts = [ 109 'font1' => 'Open Sans', 110 'font2' => 'Josefin Slab', 111 'font3' => 'Arvo', 112 'font4' => 'Lato', 113 'font5' => 'Volkorn', 114 'font6' => 'Abril Fatface', 115 'font7' => 'Ubuntu', 116 'font8' => 'PT Sans', 117 'font9' => 'Old Standard TT', 118 'font10' => 'Droid Sans', 119 ]; 120 121 if (!parent::install() 122 || !$this->installDB() 123 ) { 124 return false; 125 } 126 127 $this->registerHook('displayHeader'); 128 $this->registerHook('displayTopColumn'); 129 $this->registerHook('displayLeftColumn'); 130 $this->registerHook('displayRightColumn'); 131 $this->registerHook('displayHome'); 132 $this->registerHook('displayFooter'); 133 $this->registerHook('displayBackOfficeHeader'); 134 $this->registerHook('actionObjectLanguageAddAfter'); 135 // TODO: these two shouldn't be stored in the database, but provided 136 // as instance variables. 137 Configuration::updateValue('PS_TC_THEMES', json_encode($themesColors)); 138 Configuration::updateValue('PS_TC_FONTS', json_encode($themesFonts)); 139 140 Configuration::updateValue('PS_TC_THEME', ''); 141 Configuration::updateValue('PS_TC_FONT', ''); 142 Configuration::updateValue('PS_TC_ACTIVE', 1); 143 Configuration::updateValue('PS_SET_DISPLAY_SUBCATEGORIES', 1); 144 145 $this->installFixtures(Language::getLanguages(true)); 146 147 return true; 148 } 149 150 /** 151 * Install the module's fixtures 152 * 153 * @param array|null $languages 154 * 155 * @return bool 156 */ 157 public function installFixtures($languages = null) 158 { 159 $result = true; 160 161 if ($languages === null) { 162 $languages = Language::getLanguages(true); 163 } 164 165 foreach ($languages as $language) { 166 $result &= $this->installFixture('top', 1, $this->context->shop->id, $language['id_lang']); 167 } 168 169 return $result; 170 } 171 172 /** 173 * Uninstall the module 174 * 175 * @return bool 176 */ 177 public function uninstall() 178 { 179 $images = []; 180 if (count(Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('SHOW TABLES LIKE \''._DB_PREFIX_.'themeconfigurator\''))) { 181 $images = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS( 182 (new DbQuery()) 183 ->select('`image`') 184 ->from('themeconfigurator') 185 ); 186 } 187 foreach ($images as $image) { 188 $this->deleteImage($image['image']); 189 } 190 191 if (!$this->runQueries('uninstall') || !parent::uninstall()) { 192 return false; 193 } 194 195 return true; 196 } 197 198 /** 199 * Display back office header 200 */ 201 public function hookDisplayBackOfficeHeader() 202 { 203 if (Tools::getValue('configure') != $this->name) { 204 return; 205 } 206 207 $this->context->controller->addCSS($this->_path.'css/admin.css'); 208 $this->context->controller->addJquery(); 209 $this->context->controller->addJS($this->_path.'js/admin.js'); 210 } 211 212 /** 213 * Display header 214 * 215 * @return string 216 */ 217 public function hookdisplayHeader() 218 { 219 $this->context->controller->addCss($this->_path.'css/hooks.css', 'all'); 220 221 if ((int) Configuration::get('PS_TC_ACTIVE') == 1 && Tools::getValue('live_configurator_token') && Tools::getValue('live_configurator_token') == $this->getLiveConfiguratorToken() && $this->checkEnvironment()) { 222 $this->context->controller->addCSS($this->_path.'css/live_configurator.css'); 223 $this->context->controller->addJS($this->_path.'js/live_configurator.js'); 224 225 if (Tools::getValue('theme')) { 226 $this->context->controller->addCss($this->_path.'css/'.Tools::getValue('theme').'.css', 'all'); 227 } 228 229 if (Tools::getValue('theme_font')) { 230 $this->context->controller->addCss($this->_path.'css/'.Tools::getValue('theme_font').'.css', 'all'); 231 } 232 } else { 233 if (Configuration::get('PS_TC_THEME') != '') { 234 $this->context->controller->addCss($this->_path.'css/'.Configuration::get('PS_TC_THEME').'.css', 'all'); 235 } 236 237 if (Configuration::get('PS_TC_FONT') != '') { 238 $this->context->controller->addCss($this->_path.'css/'.Configuration::get('PS_TC_FONT').'.css', 'all'); 239 } 240 } 241 242 if (isset($this->context->controller->php_self) && $this->context->controller->php_self == 'category') { 243 $this->context->smarty->assign( 244 [ 245 'display_subcategories' => (int) Configuration::get('PS_SET_DISPLAY_SUBCATEGORIES'), 246 ] 247 ); 248 249 return $this->display(__FILE__, 'hook.tpl'); 250 } 251 252 return ''; 253 } 254 255 /** 256 * Live configurator token 257 * 258 * @return bool|string 259 */ 260 public function getLiveConfiguratorToken() 261 { 262 return Tools::getAdminToken($this->name.(int) Tab::getIdFromClassName($this->name).((Context::getContext()->employee instanceof Employee) ? (int) Context::getContext()->employee->id : Tools::getValue('id_employee'))); 263 } 264 265 /** 266 * Hook to adding a language object 267 * 268 * @param array $params 269 * 270 * @return bool 271 */ 272 public function hookActionObjectLanguageAddAfter($params) 273 { 274 return $this->installFixtures([['id_lang' => (int) $params['object']->id]]); 275 } 276 277 /** 278 * @return string 279 */ 280 public function hookdisplayTopColumn() 281 { 282 return $this->hookdisplayTop(); 283 } 284 285 /** 286 * @return string 287 */ 288 public function hookdisplayTop() 289 { 290 if (!isset($this->context->controller->php_self) || $this->context->controller->php_self != 'index') { 291 return ''; 292 } 293 $this->context->smarty->assign( 294 [ 295 'htmlitems' => $this->getItemsFromHook('top'), 296 'hook' => 'top', 297 ] 298 ); 299 300 return $this->display(__FILE__, 'hook.tpl'); 301 } 302 303 /** 304 * Hook display home 305 * 306 * @return string 307 */ 308 public function hookDisplayHome() 309 { 310 $this->context->smarty->assign( 311 [ 312 'htmlitems' => $this->getItemsFromHook('home'), 313 'hook' => 'home', 314 ] 315 ); 316 317 return $this->display(__FILE__, 'hook.tpl'); 318 } 319 320 /** 321 * Display left column 322 * 323 * @return string 324 */ 325 public function hookDisplayLeftColumn() 326 { 327 $this->context->smarty->assign( 328 [ 329 'htmlitems' => $this->getItemsFromHook('left'), 330 'hook' => 'left', 331 ] 332 ); 333 334 return $this->display(__FILE__, 'hook.tpl'); 335 } 336 337 /** 338 * Display right column 339 * 340 * @return string 341 */ 342 public function hookDisplayRightColumn() 343 { 344 $this->context->smarty->assign( 345 [ 346 'htmlitems' => $this->getItemsFromHook('right'), 347 'hook' => 'right', 348 ] 349 ); 350 351 return $this->display(__FILE__, 'hook.tpl'); 352 } 353 354 /** 355 * Display footer 356 * 357 * @return string 358 */ 359 public function hookDisplayFooter() 360 { 361 $html = ''; 362 363 if ((int) Configuration::get('PS_TC_ACTIVE') == 1 && Tools::getValue('live_configurator_token') && Tools::getValue('live_configurator_token') == $this->getLiveConfiguratorToken() && Tools::getIsset('id_employee') && $this->checkEnvironment()) { 364 if (Tools::isSubmit('submitLiveConfigurator')) { 365 Configuration::updateValue('PS_TC_THEME', Tools::getValue('theme')); 366 Configuration::updateValue('PS_TC_FONT', Tools::getValue('theme_font')); 367 } 368 369 $adImage = $this->_path.'img/'.$this->context->language->iso_code.'/advertisement.png'; 370 371 if (!file_exists($adImage)) { 372 $adImage = $this->_path.'img/en/advertisement.png'; 373 } 374 375 $themes = json_decode(Configuration::get('PS_TC_THEMES'), true); 376 $fonts = json_decode(Configuration::get('PS_TC_FONTS'), true); 377 378 // Retrocompatibility for module versions <= 3.0.7, which happened 379 // to use serialize() rather than json_encode(). 380 if (!$themes) { 381 $themes = Tools::unSerialize(Configuration::get('PS_TC_THEMES')); 382 } 383 if (!$fonts) { 384 $fonts = Tools::unSerialize(Configuration::get('PS_TC_FONTS')); 385 } 386 387 $this->smarty->assign( 388 [ 389 'themes' => $themes, 390 'fonts' => $fonts, 391 'theme_font' => Tools::getValue('theme_font', Configuration::get('PS_TC_FONT')), 392 'live_configurator_token' => $this->getLiveConfiguratorToken(), 393 'id_shop' => (int) $this->context->shop->id, 394 'id_employee' => is_object($this->context->employee) ? (int) $this->context->employee->id : Tools::getValue('id_employee'), 395 'advertisement_image' => $adImage, 396 'advertisement_url' => '', 397 'advertisement_text' => '', 398 ] 399 ); 400 401 $html .= $this->display(__FILE__, 'live_configurator.tpl'); 402 } 403 404 $this->context->smarty->assign( 405 [ 406 'htmlitems' => $this->getItemsFromHook('footer'), 407 'hook' => 'footer', 408 ] 409 ); 410 411 return $html.$this->display(__FILE__, 'hook.tpl'); 412 } 413 414 /** 415 * Get module configuration page 416 * 417 * @return string 418 */ 419 public function getContent() 420 { 421 if (Tools::isSubmit('submitModule')) { 422 Configuration::updateValue('PS_QUICK_VIEW', (int) Tools::getValue('quick_view')); 423 Configuration::updateValue('PS_TC_ACTIVE', (int) Tools::getValue('live_conf')); 424 Configuration::updateValue('PS_GRID_PRODUCT', (int) Tools::getValue('grid_list')); 425 Configuration::updateValue('PS_SET_DISPLAY_SUBCATEGORIES', (int) Tools::getValue('sub_cat')); 426 foreach ($this->getConfigurableModules() as $module) { 427 if (!isset($module['is_module']) || !$module['is_module'] || !Validate::isModuleName($module['name']) || !Tools::isSubmit($module['name'])) { 428 continue; 429 } 430 431 $moduleInstance = Module::getInstanceByName($module['name']); 432 if ($moduleInstance === false || !is_object($moduleInstance)) { 433 continue; 434 } 435 436 $isInstalled = (int) Validate::isLoadedObject($moduleInstance); 437 if ($isInstalled) { 438 if (($active = (int) Tools::getValue($module['name'])) == $moduleInstance->active) { 439 continue; 440 } 441 442 if ($active) { 443 $moduleInstance->enable(); 444 } else { 445 $moduleInstance->disable(); 446 } 447 } else { 448 if ((int) Tools::getValue($module['name'])) { 449 $moduleInstance->install(); 450 } 451 } 452 } 453 } 454 455 if (Tools::isSubmit('newItem')) { 456 $this->addItem(); 457 } elseif (Tools::isSubmit('updateItem')) { 458 $this->updateItem(); 459 } elseif (Tools::isSubmit('removeItem')) { 460 $this->removeItem(); 461 } 462 463 $html = $this->renderConfigurationForm(); 464 $html .= $this->renderThemeConfiguratorForm(); 465 466 return $html; 467 } 468 469 /** 470 * Ajax position update 471 */ 472 public function ajaxProcessUpdatePosition() 473 { 474 $items = Tools::getValue('item'); 475 $total = count($items); 476 $success = true; 477 for ($i = 1; $i <= $total; $i++) { 478 $success &= Db::getInstance()->update( 479 'themeconfigurator', 480 ['item_order' => $i], 481 '`id_item` = '.preg_replace('/(item-)([0-9]+)/', '${2}', $items[$i - 1]) 482 ); 483 } 484 if (!$success) { 485 die(json_encode(['error' => 'Update Fail'])); 486 } 487 die(json_encode(['success' => 'Update Success !', 'error' => false])); 488 } 489 490 protected function getConfigurableModules() 491 { 492 // Construct the description for the 'Enable Live Configurator' switch 493 if ($this->context->shop->getBaseURL()) { 494 $request = 'live_configurator_token='.$this->getLiveConfiguratorToken().'&id_employee='.(int) $this->context->employee->id.'&id_shop='.(int) $this->context->shop->id.(Configuration::get('PS_TC_THEME') != '' ? '&theme='.Configuration::get('PS_TC_THEME') : '').(Configuration::get('PS_TC_FONT') != '' ? '&theme_font='.Configuration::get('PS_TC_FONT') : ''); 495 $url = $this->context->link->getPageLink('index', null, $idLang = null, $request); 496 $desc = '<a class="btn btn-default" href="'.$url.'" onclick="return !window.open($(this).attr(\'href\'));" id="live_conf_button">'.$this->l('View').' <i class="icon-external-link"></i></a><br />'.$this->l('Only you can see this on your front office - your visitors will not see this tool.'); 497 } else { 498 $desc = $this->l('Only you can see this on your front office - your visitors will not see this tool.'); 499 } 500 501 $ret = [ 502 [ 503 'label' => $this->l('Display links to your store\'s social accounts (Twitter, Facebook, etc.)'), 504 'name' => 'blocksocial', 505 'value' => (int) Validate::isLoadedObject($module = Module::getInstanceByName('blocksocial')) && $module->isEnabledForShopContext(), 506 'is_module' => true, 507 ], 508 [ 509 'label' => $this->l('Display your contact information'), 510 'name' => 'blockcontactinfos', 511 'value' => (int) Validate::isLoadedObject($module = Module::getInstanceByName('blockcontactinfos')) && $module->isEnabledForShopContext(), 512 'is_module' => true, 513 ], 514 [ 515 'label' => $this->l('Display social sharing buttons on the product\'s page'), 516 'name' => 'socialsharing', 517 'value' => (int) Validate::isLoadedObject($module = Module::getInstanceByName('socialsharing')) && $module->isEnabledForShopContext(), 518 'is_module' => true, 519 ], 520 [ 521 'label' => $this->l('Display the Facebook block on the home page'), 522 'name' => 'blockfacebook', 523 'value' => (int) Validate::isLoadedObject($module = Module::getInstanceByName('blockfacebook')) && $module->isEnabledForShopContext(), 524 'is_module' => true, 525 ], 526 [ 527 'label' => $this->l('Display the custom CMS information block'), 528 'name' => 'blockcmsinfo', 529 'value' => (int) Validate::isLoadedObject($module = Module::getInstanceByName('blockcmsinfo')) && $module->isEnabledForShopContext(), 530 'is_module' => true, 531 ], 532 [ 533 'label' => $this->l('Display quick view window on homepage and category pages'), 534 'name' => 'quick_view', 535 'value' => (int) Tools::getValue('PS_QUICK_VIEW', Configuration::get('PS_QUICK_VIEW')), 536 ], 537 [ 538 'label' => $this->l('Display categories as a list of products instead of the default grid-based display'), 539 'name' => 'grid_list', 540 'value' => (int) Configuration::get('PS_GRID_PRODUCT'), 541 'desc' => $this->l('Works only for first-time users. This setting is overridden by the user\'s choice as soon as the user cookie is set.'), 542 ], 543 [ 544 'label' => $this->l('Display top banner'), 545 'name' => 'blockbanner', 546 'value' => (int) Validate::isLoadedObject($module = Module::getInstanceByName('blockbanner')) && $module->isEnabledForShopContext(), 547 'is_module' => true, 548 ], 549 [ 550 'label' => $this->l('Display logos of available payment methods'), 551 'name' => 'productpaymentlogos', 552 'value' => (int) Validate::isLoadedObject($module = Module::getInstanceByName('productpaymentlogos')) && $module->isEnabledForShopContext(), 553 'is_module' => true, 554 ], 555 ]; 556 557 $ret[] = [ 558 'label' => $this->l('Display Live Configurator'), 559 'name' => 'live_conf', 560 'value' => (int) Tools::getValue('PS_TC_ACTIVE', Configuration::get('PS_TC_ACTIVE')), 561 'hint' => $this->l('This customization tool allows you to make color and font changes in your theme.'), 562 'desc' => $desc, 563 ]; 564 565 $ret[] = [ 566 'label' => $this->l('Display subcategories'), 567 'name' => 'sub_cat', 568 'value' => (int) Tools::getValue('PS_SET_DISPLAY_SUBCATEGORIES', Configuration::get('PS_SET_DISPLAY_SUBCATEGORIES')), 569 ]; 570 571 return $ret; 572 } 573 574 protected function addItem() 575 { 576 $title = Tools::getValue('item_title'); 577 $content = Tools::getValue('item_html'); 578 579 if (!Validate::isCleanHtml($title, (int) Configuration::get('PS_ALLOW_HTML_IFRAME')) 580 || !Validate::isCleanHtml($content, (int) Configuration::get('PS_ALLOW_HTML_IFRAME')) 581 ) { 582 $this->context->smarty->assign('error', $this->l('Invalid content')); 583 584 return false; 585 } 586 587 if (!$currentOrder = (int) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue( 588 (new DbQuery()) 589 ->select('`item_order` + 1') 590 ->from('themeconfigurator') 591 ->where('`id_shop` = '.(int) $this->context->shop->id) 592 ->where('`id_lang` = '.(int) Tools::getValue('id_lang')) 593 ->where('`hook` = \''.pSQL(Tools::getValue('item_hook')).'\'') 594 ->orderBy('`item_order` DESC') 595 )) { 596 $currentOrder = 1; 597 } 598 599 $imageW = is_numeric(Tools::getValue('item_img_w')) ? (int) Tools::getValue('item_img_w') : ''; 600 $imageH = is_numeric(Tools::getValue('item_img_h')) ? (int) Tools::getValue('item_img_h') : ''; 601 602 if (!empty($_FILES['item_img']['name'])) { 603 if (!$image = $this->uploadImage($_FILES['item_img'], $imageW, $imageH)) { 604 return false; 605 } 606 } else { 607 $image = ''; 608 $imageW = ''; 609 $imageH = ''; 610 } 611 612 if (!Db::getInstance()->insert( 613 'themeconfigurator', 614 [ 615 'id_shop' => (int) $this->context->shop->id, 616 'id_lang' => (int) Tools::getValue('id_lang'), 617 'item_order' => (int) $currentOrder, 618 'title' => pSQL($title), 619 'title_use' => (int) Tools::getValue('item_title_use'), 620 'hook' => pSQL(Tools::getValue('item_hook')), 621 'url' => pSQL(Tools::getValue('item_url')), 622 'target' => (int) Tools::getValue('item_target'), 623 'image' => pSQL($image), 624 'image_w' => pSQL($imageW), 625 'image_h' => pSQL($imageH), 626 'html' => pSQL($this->filterVar($content), true), 627 'active' => 1, 628 ] 629 )) { 630 if (!Tools::isEmpty($image)) { 631 $this->deleteImage($image); 632 } 633 634 $this->context->smarty->assign('error', $this->l('An error occurred while saving data.')); 635 636 return false; 637 } 638 639 $this->context->smarty->assign('confirmation', $this->l('New item successfully added.')); 640 641 return true; 642 } 643 644 /** 645 * @param array $image 646 * @param string $imageW 647 * @param string $imageH 648 * 649 * @return bool|string 650 */ 651 protected function uploadImage($image, $imageW = '', $imageH = '') 652 { 653 $res = false; 654 if (is_array($image) && (ImageManager::validateUpload($image, $this->max_image_size) === false) && ($tmpName = tempnam(_PS_TMP_IMG_DIR_, 'PS')) && move_uploaded_file($image['tmp_name'], $tmpName)) { 655 $salt = sha1(microtime()); 656 $pathinfo = pathinfo($image['name']); 657 $imgName = $salt.'_'.Tools::str2url(str_replace('%', '', urlencode($pathinfo['filename']))).'.'.$pathinfo['extension']; 658 659 if (ImageManager::resize($tmpName, dirname(__FILE__).'/img/'.$imgName, $imageW, $imageH)) { 660 $res = true; 661 } 662 } 663 664 if (!$res) { 665 $this->context->smarty->assign('error', $this->l('An error occurred during the image upload.')); 666 667 return false; 668 } 669 670 return isset($imgName) ? $imgName : false; 671 } 672 673 /** 674 * @param string $value 675 * 676 * @return string 677 */ 678 protected function filterVar($value) 679 { 680 return Tools::purifyHTML($value); 681 } 682 683 /** 684 * @return bool 685 */ 686 protected function updateItem() 687 { 688 $idItem = (int) Tools::getValue('item_id'); 689 $title = Tools::getValue('item_title'); 690 $content = Tools::getValue('item_html'); 691 692 if (!Validate::isCleanHtml($title, (int) Configuration::get('PS_ALLOW_HTML_IFRAME')) || !Validate::isCleanHtml($content, (int) Configuration::get('PS_ALLOW_HTML_IFRAME'))) { 693 $this->context->smarty->assign('error', $this->l('Invalid content')); 694 695 return false; 696 } 697 698 $imageW = (is_numeric(Tools::getValue('item_img_w'))) ? (int) Tools::getValue('item_img_w') : ''; 699 $imageH = (is_numeric(Tools::getValue('item_img_h'))) ? (int) Tools::getValue('item_img_h') : ''; 700 701 $oldImage = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue( 702 (new DbQuery()) 703 ->select('`image`') 704 ->from('themeconfigurator') 705 ->where('`id_item` = '.(int) $idItem) 706 ); 707 708 if (!empty($_FILES['item_img']['name'])) { 709 if ($oldImage) { 710 if (file_exists(dirname(__FILE__).'/img/'.$oldImage)) { 711 @unlink(dirname(__FILE__).'/img/'.$oldImage); 712 } 713 } 714 715 if (!$image = $this->uploadImage($_FILES['item_img'], $imageW, $imageH)) { 716 return false; 717 } 718 } 719 720 if (!Db::getInstance()->update( 721 'themeconfigurator', 722 [ 723 'title' => pSQL($title), 724 'title_use' => (int) Tools::getValue('item_title_use'), 725 'hook' => pSQL(Tools::getValue('item_hook')), 726 'url' => pSQL(Tools::getValue('item_url')), 727 'target' => (int) Tools::getValue('item_target'), 728 'image' => isset($image) ? pSQL($image) : $oldImage, 729 'image_w' => (int) $imageW, 730 'image_h' => (int) $imageH, 731 'active' => (int) Tools::getValue('item_active'), 732 'html' => pSQL($this->filterVar($content), true), 733 ], 734 '`id_item` = '.(int) Tools::getValue('item_id') 735 )) { 736 if ($image = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue( 737 (new DbQuery()) 738 ->select('`image`') 739 ->from('themeconfigurator') 740 ->where('`id_item` = '.(int) Tools::getValue('item_id')) 741 )) { 742 $this->deleteImage($image); 743 } 744 745 $this->context->smarty->assign('error', $this->l('An error occurred while saving data.')); 746 747 return false; 748 } 749 750 $this->context->smarty->assign('confirmation', $this->l('Successfully updated.')); 751 752 return true; 753 } 754 755 protected function removeItem() 756 { 757 $idItem = (int) Tools::getValue('item_id'); 758 759 if ($image = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue( 760 (new DbQuery()) 761 ->select('`image`') 762 ->from('themeconfigurator') 763 ->where('`id_item` = '.(int) $idItem) 764 )) { 765 $this->deleteImage($image); 766 } 767 768 Db::getInstance()->delete(_DB_PREFIX_.'themeconfigurator', 'id_item = '.(int) $idItem); 769 770 if (Db::getInstance()->Affected_Rows() == 1) { 771 Db::getInstance()->execute( 772 ' 773 UPDATE `'._DB_PREFIX_.'themeconfigurator` 774 SET item_order = item_order-1 775 WHERE ( 776 item_order > '.(int) Tools::getValue('item_order').' AND 777 id_shop = '.(int) $this->context->shop->id.' AND 778 hook = \''.pSQL(Tools::getValue('item_hook')).'\')' 779 ); 780 Tools::redirectAdmin('index.php?tab=AdminModules&configure='.$this->name.'&conf=6&token='.Tools::getAdminTokenLite('AdminModules')); 781 } else { 782 $this->context->smarty->assign('error', $this->l('Can\'t delete the slide.')); 783 } 784 } 785 786 /** 787 * @return string 788 */ 789 protected function renderConfigurationForm() 790 { 791 $inputs = []; 792 793 foreach ($this->getConfigurableModules() as $module) { 794 $desc = ''; 795 796 if (isset($module['is_module']) && $module['is_module']) { 797 $moduleInstance = Module::getInstanceByName($module['name']); 798 if (Validate::isLoadedObject($moduleInstance) && method_exists($moduleInstance, 'getContent')) { 799 $moduleLink = $this->context->link->getAdminLink('AdminModules', true).'&configure='.urlencode($moduleInstance->name).'&tab_module='.$moduleInstance->tab.'&module_name='.urlencode($moduleInstance->name); 800 $desc = '<a class="btn btn-default" href="'.$moduleLink.'">'.$this->l('Configure').' <i class="icon-external-link"></i></a>'; 801 } 802 } 803 if (!$desc && isset($module['desc']) && $module['desc']) { 804 $desc = $module['desc']; 805 } 806 807 $inputs[] = [ 808 'type' => 'switch', 809 'label' => $module['label'], 810 'name' => $module['name'], 811 'desc' => $desc, 812 'values' => [ 813 [ 814 'id' => 'active_on', 815 'value' => 1, 816 'label' => $this->l('Enabled'), 817 ], 818 [ 819 'id' => 'active_off', 820 'value' => 0, 821 'label' => $this->l('Disabled'), 822 ], 823 ], 824 ]; 825 } 826 827 $fieldsForm = [ 828 'form' => [ 829 'legend' => [ 830 'title' => $this->l('Settings'), 831 'icon' => 'icon-cogs', 832 ], 833 'input' => $inputs, 834 'submit' => [ 835 'title' => $this->l('Save'), 836 'class' => 'btn btn-default pull-right', 837 ], 838 ], 839 ]; 840 841 $helper = new HelperForm(); 842 $helper->show_toolbar = false; 843 $helper->table = $this->table; 844 $lang = new Language((int) Configuration::get('PS_LANG_DEFAULT')); 845 $helper->default_form_language = $lang->id; 846 $helper->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') ? Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') : 0; 847 $this->fields_form = []; 848 849 $helper->identifier = $this->identifier; 850 $helper->submit_action = 'submitModule'; 851 $helper->currentIndex = $this->context->link->getAdminLink('AdminModules', false).'&configure='.$this->name.'&tab_module='.$this->tab.'&module_name='.$this->name; 852 $helper->token = Tools::getAdminTokenLite('AdminModules'); 853 $helper->tpl_vars = [ 854 'fields_value' => $this->getConfigFieldsValues(), 855 'languages' => $this->context->controller->getLanguages(), 856 'id_language' => $this->context->language->id, 857 ]; 858 859 return $helper->generateForm([$fieldsForm]); 860 } 861 862 /** 863 * @return array 864 */ 865 protected function getConfigFieldsValues() 866 { 867 $values = []; 868 foreach ($this->getConfigurableModules() as $module) { 869 $values[$module['name']] = $module['value']; 870 } 871 872 return $values; 873 } 874 875 /** 876 * @return string 877 */ 878 protected function renderThemeConfiguratorForm() 879 { 880 $idShop = (int) $this->context->shop->id; 881 $items = []; 882 $hooks = []; 883 884 $this->context->smarty->assign( 885 'htmlcontent', 886 [ 887 'admin_tpl_path' => $this->admin_tpl_path, 888 'hooks_tpl_path' => $this->hooks_tpl_path, 889 890 'info' => [ 891 'module' => $this->name, 892 'name' => $this->displayName, 893 'version' => $this->version, 894 'context' => (Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE') == 0) ? 1 : ($this->context->shop->getTotalShops() != 1) ? $this->context->shop->getContext() : 1, 895 ], 896 ] 897 ); 898 899 foreach ($this->languages as $language) { 900 $hooks[$language['id_lang']] = [ 901 'home', 902 'top', 903 'left', 904 'right', 905 'footer', 906 ]; 907 908 foreach ($hooks[$language['id_lang']] as $hook) { 909 $items[$language['id_lang']][$hook] = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS( 910 (new DbQuery()) 911 ->select('*') 912 ->from('themeconfigurator') 913 ->where('`id_shop` = '.(int) $idShop) 914 ->where('`id_lang` = '.(int) $language['id_lang']) 915 ->where('`hook` = \''.pSQL($hook).'\'') 916 ->orderBy('`item_order` ASC') 917 ); 918 } 919 } 920 921 $this->context->smarty->assign( 922 'htmlitems', 923 [ 924 'items' => $items, 925 'theme_url' => $this->module_url, 926 'lang' => [ 927 'default' => $this->default_language, 928 'all' => $this->languages, 929 'lang_dir' => _THEME_LANG_DIR_, 930 'user' => $this->context->language->id, 931 ], 932 'postAction' => 'index.php?tab=AdminModules&configure='.$this->name.'&token='.Tools::getAdminTokenLite('AdminModules').'&tab_module=other&module_name='.$this->name.'', 933 'id_shop' => $idShop, 934 ] 935 ); 936 937 $this->context->controller->addJqueryUI('ui.sortable'); 938 939 return $this->display(__FILE__, 'views/templates/admin/admin.tpl'); 940 } 941 942 /** 943 * @param string $file 944 * 945 * @return bool 946 */ 947 protected function runQueries($file) 948 { 949 if (!file_exists(__DIR__.DIRECTORY_SEPARATOR.'sql'.DIRECTORY_SEPARATOR.$file.'.sql')) { 950 return false; 951 } elseif (!$sql = file_get_contents(__DIR__.DIRECTORY_SEPARATOR.'sql'.DIRECTORY_SEPARATOR.$file.'.sql')) { 952 return false; 953 } 954 $sql = str_replace(['PREFIX_', 'ENGINE_TYPE', 'DB_NAME'], [_DB_PREFIX_, _MYSQL_ENGINE_, _DB_NAME_], $sql); 955 $sql = preg_split("/;\s*[\r\n]+/", trim($sql)); 956 957 foreach ($sql as $query) { 958 if (!Db::getInstance()->execute(trim($query))) { 959 return false; 960 } 961 } 962 963 return true; 964 } 965 966 /** 967 * Install the module's DB table(s) 968 * 969 * @return bool 970 */ 971 protected function installDB() 972 { 973 return $this->runQueries('install'); 974 } 975 976 /** 977 * Install a fixture 978 * 979 * @param string $hook 980 * @param int $idImage 981 * @param int $idShop 982 * @param int $idLang 983 * 984 * @return bool 985 */ 986 protected function installFixture($hook, $idImage, $idShop, $idLang) 987 { 988 $result = true; 989 990 $sizes = @getimagesize((dirname(__FILE__).DIRECTORY_SEPARATOR.'img'.DIRECTORY_SEPARATOR.'banner-img'.(int) $idImage.'.jpg')); 991 $width = (isset($sizes[0]) && $sizes[0]) ? (int) $sizes[0] : 0; 992 $height = (isset($sizes[1]) && $sizes[1]) ? (int) $sizes[1] : 0; 993 994 $result &= Db::getInstance()->insert( 995 'themeconfigurator', 996 [ 997 'id_shop' => (int) $idShop, 998 'id_lang' => (int) $idLang, 999 'item_order' => (int) $idImage, 1000 'title' => '', 1001 'title_use' => '0', 1002 'hook' => pSQL($hook), 1003 'url' => 'https://www.thirtybees.com/', 1004 'target' => '0', 1005 'image' => 'banner-img'.(int) $idImage.'.jpg', 1006 'image_w' => (int) $width, 1007 'image_h' => (int) $height, 1008 'html' => '', 1009 'active' => 1, 1010 ] 1011 ); 1012 1013 return $result; 1014 } 1015 1016 /** 1017 * Delete an image 1018 * 1019 * @param $image 1020 */ 1021 protected function deleteImage($image) 1022 { 1023 $fileName = $this->uploads_path.$image; 1024 1025 if (realpath(dirname($fileName)) != realpath($this->uploads_path)) { 1026 Logger::addLog(sprintf('Could not find upload directory'), 2); 1027 } 1028 1029 if ($image != '' && is_file($fileName) && !strpos($fileName, 'banner-img') && !strpos($fileName, 'bg-theme') && !strpos($fileName, 'footer-bg')) { 1030 unlink($fileName); 1031 } 1032 } 1033 1034 /** 1035 * Check environment 1036 * 1037 * @return bool 1038 */ 1039 protected function checkEnvironment() 1040 { 1041 $cookie = new Cookie('psAdmin', '', (int) Configuration::get('PS_COOKIE_LIFETIME_BO')); 1042 1043 return isset($cookie->id_employee) && isset($cookie->passwd) && Employee::checkPassword($cookie->id_employee, $cookie->passwd); 1044 } 1045 1046 /** 1047 * Get items from hook 1048 * 1049 * @param string $hook 1050 * 1051 * @return array|bool|false|null|PDOStatement 1052 */ 1053 protected function getItemsFromHook($hook) 1054 { 1055 if (!$hook) { 1056 return false; 1057 } 1058 1059 return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS( 1060 (new DbQuery()) 1061 ->select('*') 1062 ->from('themeconfigurator') 1063 ->where('`id_shop` = '.(int) $this->context->shop->id) 1064 ->where('`id_lang` = '.(int) $this->context->language->id) 1065 ->where('`hook` = \''.pSQL($hook).'\'') 1066 ->where('`active` = 1') 1067 ->orderBY('`item_order` ASC') 1068 ); 1069 } 1070} 1071