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