<?php
/**
* Since 2013 Ovidiu Cimpean.
*
* Ovidiu Cimpean - Newsletter Pro © All rights reserved.
*
* DISCLAIMER
*
* Do not edit, modify or copy this file.
* If you wish to customize it, contact us at addons4prestashop@gmail.com.
*
* @author    Ovidiu Cimpean <addons4prestashop@gmail.com>
* @copyright Since 2013 Ovidiu Cimpean
* @license   Do not edit, modify or copy this file
*
* @version   Release: 4
*/

use PQNP\Config;
use PQNP\Version;

if (!defined('_PS_VERSION_')) {
    exit;
}

require_once dirname(__FILE__).'/config/config.inc.php';

class NewsletterPro extends Module
{
    public $url_location = '';
    public $uri_location = '';
    public $dir_location = '';
    public $tpl_location = '';
    public $lang_img_path = '';
    public $lang_img_dir = '';
    public $class_name = 'AdminNewsletterPro';
    public $menu_name = 'Newsletter Pro';
    public $ajax_file_name = 'ajax_newsletterpro.php';
    public $id_parent = 11;
    public $token = '';
    public $link = '';
    public $default_campaign_params = '';
    public $request;
    public $response;
    /**
     * @var NewsletterProUpgrade
     */
    public $upgrade = null;

    /**
     * The value needs to be changed from the file config.ini.
     *
     * @var int
     */
    public $step;

    public $my_account_url = '';
    public $ajax_destination = '';
    // public $subscribe_hooks         = array('displayFooterBefore', 'displayFooter', 'displayRightColumn', 'displayLeftColumn');

    /**
     * @var NewsletterProMailChimpController
     */
    public $chimp;

    /* ini configuration */
    public $uninstall_all_tables = false;

    public $ini_config;

    public $demo_mode;

    public static $replace_vars;
    public static $instance;

    public $embed_images_message;

    public $embed_images_attachments = [];

    public $context;

    /**
     * @var NewsletterProSubscriptionTpl
     */
    private $subscription_tpl;

    private $dev_subscription_tpl;

    const SEND_BOOSTER = true;

    const BACKUP_TYPE = 'xml';

    const BACKUP_TYPE_XML = 1;

    const BACKUP_TYPE_SQL = 2;

    /**
     * This constants need to be changed also in the file init.js.
     */
    const SEND_METHOD_DEFAULT = 0;

    const SEND_METHOD_ANTIFLOOD = 1;

    const SEND_THROTTLER_TYPE_EMAILS = 0;

    const SEND_THROTTLER_TYPE_MB = 1;

    /* the value is md5(newsletterpro) */
    const NEWSLETTER_PRO_KEY = '5a112de88bead4ef8eeb07825d5af6e1';

    /*
    * if the opton is false, you can import emails that already exists into ps_customer into the table ps_newsletter_pro_email, when you are importing a csv file
    */
    const CSV_IMPORT_STRICT = true;

    const LIST_CUSTOMERS = 101;

    const LIST_VISITORS = 102;

    const LIST_VISITORS_NP = 103;

    const LIST_ADDED = 104;

    const SEARCH_CONDITION_CONTAINS = 100;

    const SEARCH_CONDITION_IS = 101;

    const SEARCH_CONDITION_IS_NOT = 102;

    const SEARCH_CONDITION_GREATER = 103;

    const SEARCH_CONDITION_LESS = 104;

    const REPLACE_ADMIN_PATH = true;

    /**
     * Test function.
     */
    public function dev()
    {
    }

    public function devLoadSubscriptionTemplate($install_filename)
    {
        $dirname = _NEWSLETTER_PRO_DIR_.'/install/tables/subscription_tpl/'.$install_filename.'/';
        $this->dev_subscription_tpl = NewsletterProSubscriptionTpl::loadFile($dirname, true, true);
    }

    /**
     * Init.
     */
    public function __construct()
    {
        self::$instance = &$this;

        $this->name = 'newsletterpro';
        $this->tab = 'advertising_marketing';
        $this->version = '5.0.3';
        $this->author = 'ProQuality';
        $this->module_key = 'cd3f5ac1a07c62f20ba41465a387a8de';
        $this->author_address = '0x79A346Cb657578a98e464200DFF789eE447A3e2a';
        $this->ps_versions_compliancy = ['min' => '1.5', 'max' => _PS_VERSION_];

        if (version_compare(_PS_VERSION_, '1.6.0.1', '>=')) {
            $this->bootstrap = true;
        } else {
            $this->bootstrap = false;
        }

        parent::__construct();

        $this->displayName = $this->l('Newsletter Pro');
        $this->description = $this->l('Advertise selected products from your store via newsletter.');
        $this->confirmUninstall = $this->l('Are you sure you want to uninstall this module? All data will be lost. If you desire you can create a backup from the module "Settings" tab.');

        if (defined('_PS_ADMIN_DIR_')) {
            $this->admin_name = basename(_PS_ADMIN_DIR_);
        } else {
            $this->admin_name = '';
        }

        $this->displayCompatibilityError();

        try {
            // this will protect the front office
            $module_dir = $this->relplaceAdminLink(_MODULE_DIR_);
            $ps_img = $this->relplaceAdminLink(_PS_IMG_);
            $this->_path = $this->relplaceAdminLink($this->_path);

            $this->request = new NewsletterProRequest();
            $this->response = new NewsletterProResponse();
            $this->upgrade = new NewsletterProUpgrade();

            $this->initContext();
            $this->initInitConfiguration();
            $this->initConfiguration();

            $this->url_location = Tools::getHttpHost(true).$module_dir.$this->name.'/';
            $this->uri_location = $module_dir.$this->name.'/';

            $this->dir_location = _PS_MODULE_DIR_.$this->name.'/';
            $this->tpl_location = _PS_MODULE_DIR_.$this->name.'/mail_templates/';
            $this->lang_img_path = Tools::getHttpHost(true).$ps_img.'l/';
            $this->lang_img_dir = _PS_IMG_DIR_.'l/';

            if (defined('_PS_ADMIN_DIR_')) {
                $this->ajax_destination = Version::isHigher('1.6.0.4') ? _PS_ADMIN_DIR_.'/filemanager/' : _PS_ADMIN_DIR_.'/ajaxfilemanager/';
            }

            $this->my_account_url = $this->context->link->getModuleLink($this->name, 'myaccount');

            $this->default_campaign_params = ['UTM_SOURCE' => 'Newsletter',
                                                    'UTM_MEDIUM' => 'email',
                                                    'UTM_CAMPAIGN' => '{newsletter_title}',
                                                    'UTM_CONTENT' => '{product_name}',
                                                ];

            $this->token = Tools::getAdminToken($this->class_name.(int) Tab::getIdFromClassName($this->class_name).(int) $this->context->cookie->id_employee);
            $this->link = 'index.php?controller='.$this->class_name.'&token='.$this->token;
            $this->chimp = new NewsletterProMailChimpController(pqnp_config_get('CHIMP.API_KYE', ''));
        } catch (Exception $e) {
            NewsletterProLog::writeStrip($e->getMessage(), NewsletterProLog::ERROR_FILE);
            $this->exception_message = $e->getMessage();
        }

        $this->dev();
    }

    public static function getNewsletterProToken()
    {
        return Tools::encrypt(NewsletterPro::NEWSLETTER_PRO_KEY);
    }

    /**
     * Display the compatibility problems for older versions of prestashop.
     */
    private function displayCompatibilityError()
    {
        if (Tools::isSubmit('controller') && Tools::strtolower(Tools::getValue('controller')) == Tools::strtolower('AdminNewsletterPro')) {
            if (version_compare(_PS_VERSION_, '1.5.1.0', '<=') && (!method_exists('Db', 'update') || !method_exists('Db', 'insert') || !method_exists('Db', 'delete'))) {
                exit(Tools::displayError(
                    sprintf($this->l('You have to override the class "%s" from the folder "%s" into the folder "%s" in order to work properly. If you have experience with PHP please ask the developer to do this.'), 'Db.php', '/newsletterpro/compatibility/', '/override/classes/db/')
                ));
            }

            if (version_compare(_PS_VERSION_, '1.5.1.0', '<=')) {
                $reflection_error = sprintf($this->l('You need to change the property "%s" from private to protected variable, from the file "%s".'), '$asso_tables', 'Shop.php');

                $reflection_class = new ReflectionClass('Shop');

                try {
                    $property = $reflection_class->getProperty('asso_tables');

                    if ($property->isPrivate()) {
                        exit(Tools::displayError($reflection_error));
                    }
                } catch (Exception $e) {
                    exit(Tools::displayError($reflection_error));
                }
            }
        }
    }

    /**
     * Module installation.
     *
     * @return bool
     */
    public function install()
    {
        if (Shop::isFeatureActive()) {
            Shop::setContext(Shop::CONTEXT_ALL);
        }

        @ini_set('max_execution_time', 600);

        $errors = [];
        $success = [];
        $success[] = parent::install()
                    && $this->installConfiguration()
                    && $this->installModuleTab('AdminNewsletterPro', 'Newsletter Pro')
                    && $this->registerHook('header')
                    && $this->registerHook('customerAccount')
                    && $this->registerHook('createAccount')
                    && $this->registerHook('actionShopDataDuplication')
                    && $this->registerHook('backOfficeHeader')
                    && $this->registerHook('actionAdminControllerSetMedia')
                    && $this->registerHook('registerGDPRConsent')
                    && $this->registerHook('actionDeleteGDPRCustomer')
                    && $this->registerHook('actionExportGDPRData')
                    && $this->registerHook('displayCustomerIdentityForm')
                    && $this->registerHook('displayCustomerAccountForm')
                    && $this->registerHook('actionCustomerAccountAdd')
                    && $this->registerHook('actionCustomerAccountUpdate')
                    && (Version::isLower('1.5.4.1') ? $this->installLang('id_lang') : true)
                    && (Version::isLower('1.5.0.4') ? $this->updateShopName((string) Configuration::get('PS_SHOP_NAME')) : true)
                    && $this->installFiles()
                    && $this->installTemplates();

        if (!Tools::isSubmit('reset')) {
            $success[] = $this->installDb();
        }

        if (!($install_data = NewsletterProInstallData::newInstance($this)->execute($errors))) {
            $this->_errors = array_merge($this->_errors, $errors);
        }

        return !in_array(false, $success) && $install_data;
    }

    /**
     * Module desinstallation.
     *
     * @return bool
     */
    public function uninstall()
    {
        // 10 minutes if need regards the prestashop uninstallation time bug, in some cases
        @ini_set('max_execution_time', 600);

        $success = [];
        $success[] = parent::uninstall()
                    && $this->unregisterHook('backOfficeHeader')
                    && $this->unregisterHook('actionAdminControllerSetMedia')
                    && $this->unregisterHook('registerGDPRConsent')
                    && $this->unregisterHook('actionDeleteGDPRCustomer')
                    && $this->unregisterHook('actionExportGDPRData')
                    && $this->unregisterHook('displayCustomerIdentityForm')
                    && $this->unregisterHook('displayCustomerAccountForm')
                    && $this->unregisterHook('actionCustomerAccountAdd')
                    && $this->unregisterHook('actionCustomerAccountUpdate')
                    && $this->uninstallConfiguration()
                    && $this->uninstallAttachments()
                    && $this->uninstallModuleTab('AdminNewsletterPro');

        if (!Tools::isSubmit('reset')) {
            $success[] = $this->uninstallDb();
        }

        return !in_array(false, $success);
    }

    public function installDemo()
    {
        NewsletterProTerminalCommand::setConfig('demo_mode', 0);

        // install users
        NewsletterProGenerateCustomers::newInstance()->generate(0, 20, 10, 20);

        // enable the templates
        pqnp_config('NEWSLETTER_TEMPLATE', 'responsive_template_0003.html');
        pqnp_config('PRODUCT_TEMPLATE', 'responsive_template_0003_layout_3_products_blue.html');

        // add mail connectin
        $smtp = new NewsletterProMail();
        $smtp->method = NewsletterProMail::METHOD_MAIL;
        $smtp->name = 'MAIL - '.uniqid();
        $smtp->from_name = '';
        $smtp->from_email = Configuration::get('PS_SHOP_EMAIL');
        $smtp->reply_to = '';
        $smtp->domain = '';
        $smtp->server = '';
        $smtp->user = '';
        $smtp->passwd = '';
        $smtp->encryption = 'off';
        $smtp->port = '';
        $smtp->list_unsubscribe_active = false;
        $smtp->list_unsubscribe_email = '';

        $smtp->add();

        pqnp_config('SMTP_ACTIVE', true);
        pqnp_config('SMTP', (int) $smtp->id);

        // activate the subscription popup
        $this->newsletterproSubscriptionActive(true, ['displayFooter']);

        // setup the front subscription
        $subscription = new NewsletterProSubscriptionTpl(3);
        if (Validate::isLoadedObject($subscription)) {
            $subscription->show_on_pages = 'index';
            $subscription->when_to_show = NewsletterProSubscriptionTpl::WHEN_TO_SHOW_POPUP_COOKIE;
            $subscription->cookie_lifetime = 1 / 24 / 60 / 60 * 60;
            $subscription->save();
        }

        NewsletterProTerminalCommand::setConfig('demo_mode', 1);
    }

    /**
     * Initialize module configuration.
     */
    public function initInitConfiguration()
    {
        $file = _PS_MODULE_DIR_.$this->name.'/config.ini';
        if (file_exists($file)) {
            try {
                $config = @parse_ini_file($file);

                if (!$config) {
                    $config = [];

                    $init_file = Tools::file_get_contents($file);
                    if (preg_match_all('/^\w+\s+?=\s+?\w+/m', $init_file, $matches)) {
                        $matches = $matches[0];

                        foreach ($matches as $line) {
                            $line_arr = explode('=', $line);
                            $key = trim($line_arr[0]);
                            $value = trim($line_arr[1]);

                            if ('true' === $value) {
                                $value = true;
                            } elseif ('false' === $value) {
                                $value = false;
                            } else {
                                $value = (int) $value;
                            }

                            $config[$key] = $value;
                        }
                    }
                }

                $this->ini_config = $config;

                if (isset($config['uninstall_all_tables'])) {
                    $this->uninstall_all_tables = $config['uninstall_all_tables'];
                }

                if (isset($config['step'])) {
                    $this->step = (int) $config['step'];
                }
            } catch (Exception $e) {
                NewsletterProLog::writeStrip($e->getMessage(), NewsletterProLog::ERROR_FILE);
            }
        }

        $file = _PS_MODULE_DIR_.$this->name.'/demo_mode.json';

        if (file_exists($file)) {
            if ((int) pqnp_ini_config('demo_mode')) {
                $this->demo_mode = Tools::jsonDecode(Tools::file_get_contents($file), true);
            }
        }
    }

    /**
     * Install prestashop files for the older versions of prestashop.
     *
     * @return bool
     */
    public function installFiles()
    {
        if (version_compare(_PS_VERSION_, '1.5.2.0', '<')) {
            $to_file = _PS_ROOT_DIR_.'/js/tiny_mce/plugins/';
            $destination = $to_file.'fullpage/';

            $zip_path = $this->dir_location.'install/tiny_mce/';
            $zip_file = $zip_path.'fullpage.zip';
            if (file_exists($zip_file)) {
                $zip = new ZipArchive();
                if (true === $zip->open($zip_file)) {
                    // create all folders
                    for ($i = 0; $i < $zip->numFiles; ++$i) {
                        $only_file_name = $zip->getNameIndex($i);
                        $full_file_name = $zip->statIndex($i);
                        if ('/' == $full_file_name['name'][Tools::strlen($full_file_name['name']) - 1]) {
                            @NewsletterProTools::createFolder($zip_path.$full_file_name['name']);
                        }
                    }

                    // unzip into the folders
                    for ($i = 0; $i < $zip->numFiles; ++$i) {
                        $only_file_name = $zip->getNameIndex($i);
                        $full_file_name = $zip->statIndex($i);

                        if (!('/' == $full_file_name['name'][Tools::strlen($full_file_name['name']) - 1])) {
                            copy('zip://'.$zip_file.'#'.$only_file_name, $zip_path.$full_file_name['name']);
                        }
                    }
                    $zip->close();

                    // unzip done
                    // copy the folder only the folder does not exists
                    if (file_exists($to_file) && !file_exists($destination)) {
                        $move_file = $this->dir_location.'install/tiny_mce/fullpage/';

                        if (!file_exists($move_file)) {
                            $this->addError(sprintf($this->l('The tiny mce plugin "%s" not exists for instalation.'), 'fullpage'));

                            return false;
                        }
                        @NewsletterProTools::recurseCopy($move_file, $destination);
                    }
                } else {
                    $this->addError(sprintf($this->l('Failed to open file "%s"'), $zip_file));
                }
            }
        }

        return true;
    }

    public function installTemplates()
    {
        $path = $this->tpl_location.'newsletter/';
        $result = NewsletterProTools::getDirectoryIterator($path, '/^[a-zA-Z0-9_-]+$/');
        $languages = Language::getLanguages(false);

        $index_file = $path.'index.php';

        try {
            foreach ($result as $file) {
                if ($file->isDir()) {
                    $folder_path = $file->getPathName();

                    if (!$file->isWritable()) {
                        throw new Exception(sprintf($this->l('The file "%s" is not writable, please check the CHMOD permissions.'), $folder_path));
                    }

                    $file_name = $file->getFileName().'.html';
                    $file_path = $folder_path.'/en/'.$file_name;

                    foreach ($languages as $lang) {
                        if ('en' != $lang['iso_code']) {
                            $new_iso_path = $folder_path.'/'.$lang['iso_code'];

                            if (!file_exists($new_iso_path)) {
                                if (!mkdir($new_iso_path, 0777)) {
                                    throw new Exception(sprintf($this->l('Cannot create the directory "%s", please check the CHMOD permissions.'), $new_iso_path));
                                }
                            }

                            $copy_message = $this->l('Cannot copy the file "%s", please checked the CHMOD permissions.');
                            $new_index = $new_iso_path.'/index.php';

                            if (!copy($index_file, $new_index)) {
                                throw new Exception(sprintf($copy_message, $new_index));
                            }

                            $new_file_name = $new_iso_path.'/'.$file_name;

                            if (!copy($file_path, $new_file_name)) {
                                throw new Exception(sprintf($copy_message, $new_file_name));
                            }
                        }
                    }
                }
            }
        } catch (Exception $e) {
            $this->addError($e->getMessage());

            return false;
        }

        return true;
    }

    /**
     * Get an instance of the module.
     *
     * @return NewsletterPro
     */
    public static function getInstance()
    {
        return self::$instance;
    }

    /**
     * For Prestashop 1.6, for 1.7 read the dev/Readme.md file.
     *
     * Allow authentication with postman
     *
     * This function will be placed in the file admin/init.php
     *
     * $newsletterpro = Module::getInstanceByName('newsletterpro');
     * if (Validate::isLoadedObject($newsletterpro))
     *     $newsletterpro->allowAuthorization();
     *
     * You need to place in .htaccess file the lines
     *
     * RewriteEngine on
     *
     * RewriteCond %{HTTP:Authorization} ^(.*)
     * RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1]
     */
    public function allowAuthorization()
    {
        if (function_exists('getallheaders')) {
            $headers = call_user_func('getallheaders');

            if (isset($headers['Authorization']) && !empty($headers['Authorization'])) {
                $context = Context::getContext();

                $authorization = explode(' ', $headers['Authorization']);
                $auth = call_user_func_array('base64_decode', [$authorization[1]]);
                $user = explode(':', $auth);

                if (isset($user[0], $user[1])) {
                    $context->employee = new Employee();
                    $context->employee = $context->employee->getByEmail($user[0], $user[1]);
                    if (Validate::isLoadedObject($context->employee)) {
                        $context->employee->remote_addr = (int) ip2long(Tools::getRemoteAddr());
                        // Update cookie
                        $cookie = Context::getContext()->cookie;
                        $cookie->id_employee = $context->employee->id;
                        $cookie->email = $context->employee->email;
                        $cookie->profile = $context->employee->id_profile;
                        $cookie->passwd = $context->employee->passwd;
                        $cookie->remote_addr = $context->employee->remote_addr;

                        if (!Tools::getValue('stay_logged_in')) {
                            $cookie->last_activity = time();
                        }

                        $cookie->write();
                    }
                }
            }
        }
    }

    /**
     * Get the MailChimp object.
     *
     * @return object
     */
    public function getChimp()
    {
        return $this->chimp;
    }

    /**
     * Hook customer account.
     *
     * @return string
     */
    public function hookCustomerAccount()
    {
        if ((bool) pqnp_config('DISPLYA_MY_ACCOUNT_NP_SETTINGS')) {
            $this->context->smarty->assign([
                'url_location' => $this->url_location,
                'my_account_url' => $this->my_account_url,
            ]);

            if (NewsletterProTools::is17()) {
                return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/front/1.7/my_account_button.tpl'));
            } elseif ($this->isPS16()) {
                return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/front/1.6/my_account_button.tpl'));
            } else {
                return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/front/1.5/my_account_button.tpl'));
            }
        }

        return '';
    }

    public function hookDisplayCustomerAccount()
    {
        return $this->hookCustomerAccount();
    }

    /**
     * Get the module css path, according to the prestashop version.
     *
     * @return string
     */
    public function getCssPath($default = false)
    {
        $css_path = $this->uri_location.'views/css/';

        if ($default) {
            return $css_path;
        }

        if (Version::isLower('1.6.0.0')) {
            $css_path = $this->uri_location.'views/css/1.5/';
        } else {
            $css_path = $this->uri_location.'views/css/1.6/';
        }

        return $css_path;
    }

    /**
     * Get css dir path.
     *
     * @return string
     */
    public function getCssDirPath()
    {
        $css_path = $this->dir_location.'views/css/';

        if (Version::isLower('1.6.0.0')) {
            $css_path = $this->dir_location.'views/css/1.5/';
        } else {
            $css_path = $this->dir_location.'views/css/1.6/';
        }

        return $css_path;
    }

    /**
     * Id language exists.
     *
     * @param int $id_lang
     *
     * @return bool
     */
    private function idLangExists($id_lang)
    {
        $languages = Language::getLanguages(true, $this->context->shop->id);
        foreach ($languages as $lang) {
            if ($id_lang == $lang['id_lang']) {
                return true;
            }
        }

        return false;
    }

    /**
     * Id gender exists.
     *
     * @param int $id_gender
     *
     * @return bool
     */
    private function idGenderExists($id_gender)
    {
        $genders = Gender::getGenders();
        foreach ($genders as $gender) {
            if ($gender->id_gender == $id_gender) {
                return true;
            }
        }

        return false;
    }

    /**
     * Close front subscription window popup, according to the cookie lifetime.
     *
     * @return json
     */
    public function submitNewsletterProSubscribeCloseForever()
    {
        $response = new NewsletterProAjaxResponse();

        $subscrbtion_template = NewsletterProSubscriptionTpl::getActiveTemplateInstance();

        $one_day = 60 * 60 * 24;
        $life_time_days = round($one_day * (float) $subscrbtion_template->cookie_lifetime);

        $cookie = new NewsletterProCookie('subscription_template_front', time() + $life_time_days);

        $cookie->set('popup_show', '0');

        if (true == (bool) $cookie->get('popup_show')) {
            $response->addError($this->l('An error occurred to proceed this action.'));
        }

        return $response->display();
    }

    /**
     * Submit the front subscription forum.
     *
     * @return json
     */
    public function submitNewsletterProSubscribe()
    {
        $response = new NewsletterProAjaxResponse([
            'msg' => '',
            'displaySubscribeMessage' => false,
        ]);

        try {
            $subscribe = new NewsletterProSubscribers();

            $subscribe->active = 1;
            $subscribe->firstname = Tools::getValue('firstname');
            $subscribe->lastname = Tools::getValue('lastname');

            $email = trim(Tools::getValue('email'));

            if (!empty($email)) {
                $subscribe->email = $email;
            } else {
                $response->addError($this->l('The email address cannot be empty.'));
            }

            $custom_variabels_hidden_keys = preg_grep('/^hidden_custom_variable_.*$/', array_keys($_POST));

            if (!empty($custom_variabels_hidden_keys)) {
                $required_message = $this->l('You must fill all the required fields.');

                foreach ($custom_variabels_hidden_keys as $variable_name) {
                    $variable_name = str_replace('hidden_', '', $variable_name);
                    $name = str_replace('custom_variable_', '', $variable_name);

                    if (Tools::isSubmit($variable_name)) {
                        $value = Tools::getValue($variable_name);
                        $field = NewsletterProSubscribersCustomField::getInstanceByVariableName($name);

                        if ($field) {
                            if (is_array($value) && NewsletterProSubscribersCustomField::TYPE_CHECKBOX == $field->type) {
                                if (empty($value)) {
                                    $response->addError($required_message);
                                }

                                $subscribe->{$name} = Tools::jsonEncode($value);
                            } elseif (is_string($value)) {
                                if ($field->isRequired()) {
                                    $tr = Tools::strlen(trim($value));
                                    if (0 == $tr) {
                                        $response->addError($required_message);
                                    }
                                }

                                $subscribe->{$name} = $value;
                            }
                        }
                    } else {
                        $field = NewsletterProSubscribersCustomField::getInstanceByVariableName($name);

                        if ($field) {
                            if ($field->isRequired()) {
                                $response->addError($required_message);
                            }
                        }
                    }
                }
            }

            $id_lang = $this->context->language->id;
            if (Tools::isSubmit('id_lang')) {
                $get_id_lang = Tools::getValue('id_lang');
                if ($this->idLangExists($get_id_lang)) {
                    $id_lang = $get_id_lang;
                }
            }

            if (Tools::isSubmit('id_gender')) {
                $id_gender = Tools::getValue('id_gender');
                if ($this->idGenderExists($id_gender)) {
                    $subscribe->id_gender = (int) $id_gender;
                }
            }

            $subscribe->id_lang = (int) $id_lang;

            $days = trim(Tools::getValue('days'));
            $months = trim(Tools::getValue('months'));
            $years = trim(Tools::getValue('years'));

            if (!empty($days) && !empty($months) && !empty($years)) {
                $date = new DateTime();
                $date->setDate((int) $years, (int) $months, (int) $days);
                $subscribe->birthday = $date->format('Y-m-d');
            }

            if (Tools::isSubmit('list_of_interest')) {
                $list_of_interest = (int) Tools::getValue('list_of_interest');

                if ($list_of_interest) {
                    $subscribe->list_of_interest = $list_of_interest;
                }
            } else {
                $loi_array = [];
                $post_keys = array_keys($_POST);
                $loi_grep = preg_grep('/^list_of_interest_\d+$/', $post_keys);

                foreach ($loi_grep as $key) {
                    if (Tools::isSubmit($key) && ($value = (int) Tools::getValue($key))) {
                        $loi_array[] = $value;
                    }
                }

                $subscribe->list_of_interest = rtrim(implode(',', $loi_array), ',');
            }

            $subscrbtion_template = NewsletterProSubscriptionTpl::getActiveTemplateInstance();

            // check if the user is already registered to the newsletter
            if ($subscrbtion_template->allow_multiple_time_subscription) {
                $tables = [
                    'customer' => ['primary' => 'id_customer', 'active' => 'newsletter'],
                    'newsletter_pro_subscribers' => ['primary' => 'id_newsletter_pro_subscribers', 'active' => 'active'],
                ];

                if ((bool) pqnp_config('SUBSCRIPTION_ACTIVE')) {
                    $tables['newsletter_pro_email'] = ['primary' => 'id_newsletter_pro_email', 'active' => 'active'];
                }

                if (NewsletterProTools::tableExists('newsletter')) {
                    $tables['newsletter'] = ['primary' => 'id', 'active' => 'active'];
                }

                foreach ($tables as $table_name => $data) {
                    $sql = 'SELECT `'.pSQL($data['primary']).'` FROM `'._DB_PREFIX_.pSQL($table_name).'` WHERE `email` = "'.pSQL($subscribe->email).'" AND `'.pSQL($data['active']).'` = 1';
                    if (Db::getInstance()->getValue($sql)) {
                        $response->addError($this->l('The email address is already subscribed at our newsletter.'));
                    }
                }
            }

            if (null != $subscrbtion_template->mandatory_fields) {
                $mandatory_fields = NewsletterProTools::unSerialize($subscrbtion_template->mandatory_fields);

                if (!empty($mandatory_fields)) {
                    if (in_array('firstname', $mandatory_fields)) {
                        $fn = trim($subscribe->firstname);
                        if (empty($fn) || !Validate::isName($fn)) {
                            $response->addError($this->l('The First Name field is required.'));
                        }
                    }

                    if (in_array('lastname', $mandatory_fields)) {
                        $ln = trim($subscribe->lastname);
                        if (empty($ln) || !Validate::isName($ln)) {
                            $response->addError($this->l('The Last Name field is required.'));
                        }
                    }
                }
            }

            $voucher = $subscrbtion_template->getVoucher();

            if ($response->success()) {
                if (pqnp_config('SUBSCRIPTION_SECURE_SUBSCRIBE')) {
                    $subscribe_temp = new NewsletterProSubscribersTemp();
                    $subscribe_temp->id_newsletter_pro_subscription_tpl = $subscrbtion_template->id;

                    if (!$subscribe_temp->saveTemp($subscribe)) {
                        $response->mergeErrors($subscribe_temp->getErrors());
                    } else {
                        $link = $this->context->link->getModuleLink('newsletterpro', 'subscribeconfirmation', ['token' => $subscribe_temp->token]);

                        $subscrbtion_template->extendVars([
                            'firstname' => htmlentities(Tools::getValue('firstname')),
                            'lastname' => htmlentities(Tools::getValue('lastname')),
                            'email_confirmation_link' => $link,
                            'email_confirmation' => '<a href="'.$link.'" style="color: blue">'.$this->l('here').'</a>',
                        ]);

                        $secure_subscribe_msg = trim($subscrbtion_template->renderEmailSubscribeConfirmationMessage((int) $this->context->language->id));
                        $secure_subscribe_msg_strip = trim(strip_tags($secure_subscribe_msg));

                        $email_title = $this->l('Newsletter Subscription Confirmation');
                        $email_body = '';

                        if (!empty($secure_subscribe_msg_strip)) {
                            $email_body = $secure_subscribe_msg;
                        } else {
                            $file_tpl = dirname(__FILE__).'/views/templates/front/newsletter_subscribe.tpl';
                            $this->context->smarty->assign([
                                'confirmation_link' => $link,
                            ]);
                            $content = $this->context->smarty->fetch($file_tpl);

                            $template = NewsletterProTemplate::newString(['', $content], $subscribe->email)->load();
                            $message = $template->message();

                            $email_title = $message['title'];
                            $email_body = $message['body'];
                        }

                        if (pqnp_config('DEBUG_MODE')) {
                            $send = NewsletterProSendManager::getInstance()->sendNewsletter($email_title, $email_body, $subscribe_temp->email, [], [], false);
                        } else {
                            $send = @NewsletterProSendManager::getInstance()->sendNewsletter($email_title, $email_body, $subscribe_temp->email, [], [], false);
                        }

                        if (is_array($send)) {
                            $response->addError($this->l('An error occurred when sending the confirmation email.'));
                        }
                    }
                } else {
                    $id_duplicate = (int) $subscribe->isDuplicateEmail();
                    if ($id_duplicate) {
                        $subscribe->id = $id_duplicate;
                        if (!$subscribe->update()) {
                            $response->mergeErrors($subscribe->getErrors());
                        } else {
                            // if the customer is not logged it the consent_date will be null
                            NewsletterProSubscriptionConsent::newInstance()->set($subscribe->email, (bool) $subscribe->active, $this->context->customer->isLogged())->add();
                        }
                    } else {
                        if (!$subscribe->add()) {
                            $response->mergeErrors($subscribe->getErrors());
                        } else {
                            // if the customer is not logged it the consent_date will be null
                            NewsletterProSubscriptionConsent::newInstance()->set($subscribe->email, (bool) $subscribe->active, $this->context->customer->isLogged())->add();
                        }
                    }
                }
            }

            if ($response->success()) {
                if (!pqnp_config('SUBSCRIPTION_SECURE_SUBSCRIBE')) {
                    $subscribe_msg = trim($subscrbtion_template->renderSubscribeMessage((int) $this->context->language->id));
                    $subscribe_msg_strip = trim(strip_tags($subscribe_msg));

                    if ($subscrbtion_template->display_subscribe_message && !empty($subscribe_msg_strip)) {
                        $response->set('displaySubscribeMessage', true);
                        $response->set('msg', $subscribe_msg);
                    } elseif (!empty($subscribe_msg_strip)) {
                        $response->set('msg', $subscribe_msg);
                    } else {
                        $success_msg = $this->l('You have been successfully subscribed to our newsletter.');
                        if ($voucher) {
                            $response->set('msg', $success_msg.sprintf($this->l('You can use this voucher %s.'), $voucher));
                        } else {
                            $response->set('msg', $success_msg);
                        }
                    }

                    // send the email with the voucher it the voucher exists
                    if ($voucher) {
                        $subscribe_voucher_msg = trim($subscrbtion_template->renderEmailSubscribeVoucherMessage((int) $this->context->language->id));
                        $subscribe_voucher_msg_strip = trim(strip_tags($subscribe_voucher_msg));

                        if (!empty($subscribe_voucher_msg_strip)) {
                            if (pqnp_config('DEBUG_MODE')) {
                                $send = NewsletterProSendManager::getInstance()->sendNewsletter($this->l('Newsletter Subscription Voucher'), $subscribe_voucher_msg, $subscribe->email, [], [], false);
                            } else {
                                $send = @NewsletterProSendManager::getInstance()->sendNewsletter($this->l('Newsletter Subscription Voucher'), $subscribe_voucher_msg, $subscribe->email, [], [], false);
                            }
                        }
                    }
                } else {
                    if ($subscrbtion_template->display_subscribe_message) {
                        $response->set('displaySubscribeMessage', true);
                    } else {
                        $response->set('displaySubscribeMessage', false);
                    }

                    $response->set('msg', sprintf($this->l('A configuration email has been sent to the email address "%s". To subscribe please click on the link from your email address.'), $subscribe->email));
                }
            }
        } catch (Exception $e) {
            NewsletterProLog::writeStrip($e->getMessage(), NewsletterProLog::ERROR_FILE);
            $response->addError($this->l('An error occurred.'));
        }

        $response->errors = array_unique($response->errors);

        return $response->display();
    }

    /**
     * Get shop logo.
     *
     * @param int $id_shop
     *
     * @return string
     */
    public function getShopLogoUrl($id_shop = null)
    {
        if (!isset($id_shop)) {
            $id_shop = (int) $this->context->shop->id;
        }

        $theme_logo = Configuration::get('PS_LOGO');

        if (isset($theme_logo) && Tools::strlen($theme_logo) > 0 && file_exists(_PS_IMG_DIR_.$theme_logo)) {
            $shop_logo_url = Tools::getHttpHost(true)._PS_IMG_.$theme_logo;
            $shop_logo_url = $this->relplaceAdminLink($shop_logo_url);

            return $shop_logo_url;
        }

        $row = Db::getInstance()->getRow('
			SELECT `value` FROM `'._DB_PREFIX_.'configuration`
			WHERE `name`="PS_LOGO_MAIL"
			AND `id_shop` = '.(int) $id_shop.'
		');

        $ps_logo_mail = isset($row['value']) ? $row['value'] : pqnp_config('PS_LOGO_MAIL');

        $shop_logo_url = '';
        if (false !== $ps_logo_mail && file_exists(_PS_IMG_DIR_.$ps_logo_mail)) {
            $shop_logo_url = Tools::getHttpHost(true)._PS_IMG_.$ps_logo_mail;
        } else {
            if (file_exists(_PS_IMG_DIR_.'logo.jpg')) {
                $shop_logo_url = Tools::getHttpHost(true)._PS_IMG_.'logo.jpg';
            }
        }

        $shop_logo_url = $this->relplaceAdminLink($shop_logo_url);

        return $shop_logo_url;
    }

    /**
     * Process the ajax requests.
     *
     * @return die
     */
    public function ajaxProcess()
    {
        $ajax = new NewsletterProAjaxController();
        $ajax->processFront(Tools::getValue('submit'));
    }

    /**
     * Check of the availability of the uniform js library.
     *
     * @return bool
     */
    public static function isUniformRequired()
    {
        return version_compare(_PS_VERSION_, '1.6.0.1', '<');
    }

    /**
     * Include the uniform js library.
     *
     * @param object $controller
     */
    public static function includeUniform($controller)
    {
        $module = NewsletterPro::getInstance();
        $controller->addCSS($module->uri_location.'views/css/uniform.default.css');
        $controller->addJS([
            $module->uri_location.'views/js/jquery.uniform.min.js',
        ]);
    }

    /**
     * Check of the availabillity of the fontawoseme css library.
     *
     * @return bool
     */
    public static function isFontAwesomeRequired()
    {
        // some of the icons are not displaied on prestashop 1.6.0.6, and I've updated to 1.6.1
        return version_compare(_PS_VERSION_, '1.6.1', '<');
    }

    /**
     * Include the fontawesome js library.
     *
     * @param object $controller
     */
    public static function includeFontAwesome($controller)
    {
        $module = NewsletterPro::getInstance();
        $controller->addCSS($module->uri_location.'views/css/font-awesome.css');
    }

    private function initPopup()
    {
        if ((int) pqnp_config('SUBSCRIPTION_ACTIVE')) {
            if (isset($this->dev_subscription_tpl)) {
                // for development
                $this->subscription_tpl = $this->dev_subscription_tpl;
            } else {
                $this->subscription_tpl = NewsletterProSubscriptionTpl::getActiveTemplateInstance();
            }

            $this->context->controller->addCSS(NewsletterProTools::getCSS('newsletter_subscribe_block.css'), 'all');

            $css_path = $this->getCssPath();

            if (self::isUniformRequired()) {
                self::includeUniform($this->context->controller);
            }

            if (self::isFontAwesomeRequired()) {
                self::includeFontAwesome($this->context->controller);
            }

            $this->context->controller->addCSS($this->uri_location.'views/css/'.NewsletterProTools::getVersion().'/'.NewsletterProSubscriptionTpl::CSS_STYLE_GLOBAL_PATH, 'all');

            $css_details = $this->subscription_tpl->getSubscriptionCSSLinkWithDetails();

            if ($css_details['file_exists']) {
                $this->context->controller->addCSS($css_details['link']);
            } else {
                // cannot add the file with the regular function addCSS
                $this->context->controller->css_files[$css_details['link']] = 'all';
            }

            $this->context->controller->addCSS($css_path.'front_window.css', 'all');

            $this->loadSubscriptionControllerTemplate();

            NewsletterProAppStorage::extend([
                'subscription_tpl_loader_better' => (bool) $this->subscription_tpl->render_loader,
                // 'subscription_tpl_loader_better' => true,
                'subscription_tpl' => $this->subscription_tpl->getJSData(),
                'subscription_tpl_render' => $this->subscription_tpl->render((int) $this->context->language->id),
            ]);
        }
    }

    private function loadSubscriptionControllerTemplate()
    {
        $templateId = (int) pqnp_config_get('SUBSCRIPTION_CONTROLLER_TEMPLATE_ID', 0);
        $template = new NewsletterProSubscriptionTpl($templateId);

        if (Validate::isLoadedObject($template)) {
            $css_details = $template->getSubscriptionCSSLinkWithDetails();

            if ($css_details['file_exists']) {
                $this->context->controller->addCSS($css_details['link']);
            } else {
                // cannot add the file with the regular function addCSS
                $this->context->controller->css_files[$css_details['link']] = 'all';
            }

            NewsletterProAppStorage::extend([
                'subscription_controller_tpl_render' => $template->render((int) $this->context->language->id),
            ]);
        }
    }

    /**
     * Hook display top.
     *
     * @param array $params
     *
     * @return string
     */
    public function hookDisplayTop($params)
    {
        $output = [];

        try {
            if (pqnp_config('SUBSCRIPTION_ACTIVE') && isset($this->subscription_tpl)) {
                if (NewsletterProSubscriptionTpl::RENDER_LOADER_BETTER === (int) $this->subscription_tpl->render_loader) {
                    return '';
                }

                $this->context->smarty->assign([
                    'display_in_footer' => true,
                    'tpl_location' => $this->dir_location.'views/',
                    'render_template' => $this->subscription_tpl->render((int) $this->context->language->id),
                    'subscription_template_front_info' => pqnp_addcslashes(Tools::jsonEncode($this->subscription_tpl->getJSData())),
                ]);

                $output[] = $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/hook/newsletter_subscribe.tpl'));
            }
        } catch (Exception $e) {
            NewsletterProLog::writeStrip($e->getMessage(), NewsletterProLog::ERROR_FILE);
        }

        // $this->restoreJqueryIfNeeded('displayTop');

        return implode('', $output);
    }

    /**
     * Get languages.
     *
     * @return array
     */
    public function getLanguages()
    {
        $languages = Language::getLanguages(true, $this->context->shop->id);
        foreach ($languages as &$lang) {
            if ($lang['id_lang'] == $this->context->language->id) {
                $lang['selected'] = true;
            } else {
                $lang['selected'] = false;
            }
        }

        return $languages;
    }

    /**
     * Hook display left column.
     *
     * @param array $params
     *
     * @return string
     */
    public function hookDisplayLeftColumn($params)
    {
        return (new NewsletterProSubscriptionHook())->display('displayLeftColumn');
    }

    /**
     * Hook display right column.
     *
     * @param array $params
     *
     * @return string
     */
    public function hookDisplayRightColumn($params)
    {
        return (new NewsletterProSubscriptionHook())->display('displayRightColumn');
    }

    /**
     * Hook display footer column.
     *
     * @param array $params
     *
     * @return string
     */
    public function hookFooter($params)
    {
        return (new NewsletterProSubscriptionHook())->display('displayFooter');
    }

    public function hookDisplayFooter($params)
    {
        return (new NewsletterProSubscriptionHook())->display('displayFooter');
    }

    public function hookDisplayFooterBefore($params)
    {
        return (new NewsletterProSubscriptionHook())->display('displayFooterBefore');
    }

    /**
     * Hook action shop data duplicate.
     *
     * @param array $params
     *
     * @return string
     */
    public function hookActionShopDataDuplication($params)
    {
        try {
            $old_id_shop = (int) $params['old_id_shop'];
            $new_id_shop = (int) $params['new_id_shop'];

            if (!$old_id_shop) {
                $old_id_shop = Configuration::get('PS_SHOP_DEFAULT');
            }

            $deleted = false;

            $asso_tables = array_merge(
                NewsletterProSubscriptionTpl::getAssoTables(),
                NewsletterProListOfInterest::getAssoTables()
            );
            foreach ($asso_tables as $table_name => $row) {
                $id = 'id_'.$row['type'];
                if ('fk_shop' == $row['type'] || preg_match('/_lang$/', $table_name)) {
                    $id = 'id_shop';
                } else {
                    $table_name .= '_'.$row['type'];
                }

                if (!$deleted) {
                    $res = Db::getInstance()->getRow('SELECT * FROM `'._DB_PREFIX_.$table_name.'` WHERE `'.$id.'` = '.(int) $old_id_shop);

                    if ($res) {
                        unset($res[$id]);
                        if (isset($row['primary'])) {
                            unset($res[$row['primary']]);
                        }

                        $keys = implode('`, `', array_keys($res));

                        $sql = 'INSERT IGNORE INTO `'._DB_PREFIX_.$table_name.'` (`'.$keys.'`, '.$id.')
								(SELECT `'.$keys.'`, '.(int) $new_id_shop.' FROM '._DB_PREFIX_.$table_name.'
								WHERE `'.$id.'` = '.(int) $old_id_shop.')';
                    }
                    Db::getInstance()->execute($sql);
                }
            }
        } catch (Exception $e) {
            NewsletterProLog::writeStrip($e->getMessage(), NewsletterProLog::ERROR_FILE);

            if (_PS_MODE_DEV_) {
                exit(Tools::displayError($e->getMessage()));
            }
        }
    }

    public function hookBackOfficeHeader()
    {
        if ('AdminNewsletterPro' == $this->context->controller->controller_name) {
            // for prestashop 1.5
            if (!method_exists('Media', 'addJsDef') || version_compare('1.6.0.9', _PS_VERSION_, '==')) {
                return NewsletterProTools::getJsDefScript([
                    'NewsletterProAppStorage' => NewsletterProAppStorage::get('app'),
                    'NewsletterProAppTranslate' => NewsletterProAppTranslate::get('app'),
                ]);
            }
        }
    }

    public function hookActionAdminControllerSetMedia()
    {
        if ('AdminNewsletterPro' == $this->context->controller->controller_name) {
        }
    }

    private function getJSData()
    {
        $data = [
            'ajax_url' => Context::getContext()->link->getModuleLink($this->name, 'ajax', []),
            'isPS17' => NewsletterProTools::is17(),
            'psVersion' => NewsletterProTools::getVersion(),
            'configuration' => [
                'CROSS_TYPE_CLASS' => pqnp_config('CROSS_TYPE_CLASS'),
            ],
        ];

        if ($this->context->controller && $this->context->controller instanceof NewsletterProMyAccountModuleFrontController) {
            $tree = NewsletterProCategoryTree::newInstance((int) $this->context->customer->id);
            $data['home_category'] = $tree->home($this->context->language->id, $this->context->shop->id);
        }

        $js_data = [
            'NewsletterProAppStorage' => NewsletterProAppStorage::get('app_front'),
            'NewsletterProAppTranslate' => NewsletterProAppTranslate::get('app_front'),
            'NewsletterPro_Data' => $data,
            'NPRO_AJAX_URL' => [
                'ajax_url' => $data['ajax_url'],
            ],
        ];

        return $js_data;
    }

    private function initJSData(&$output)
    {
        $js_data = $this->getJSData();

        if (method_exists('Media', 'addJsDef')) {
            Media::addJsDef($js_data);
            $output .= NewsletterProTools::getJsDefScript(null, '
				'.Tools::file_get_contents($this->dir_location.'views/js/ready.js').'
			');
        } else {
            // for prestashop 1.5
            $output .= NewsletterProTools::getJsDefScript($js_data, '
				NewsletterPro.dataStorage.addObject(NewsletterPro_Data);
				'.Tools::file_get_contents($this->dir_location.'views/js/ready.js').'
			');
        }
    }

    /**
     * Hook display header.
     *
     * @return string
     */
    public function hookHeader()
    {
        $output = '';

        $css_path = $this->getCssPath();

        $this->context->controller->addCSS($css_path.'newsletterpro_front.css', 'all');
        // $this->context->controller->addCSS($this->uri_location.'views/css/newsletterpro_cross.css', 'all');

        $this->restore_jquery = false;
        if (version_compare(_PS_JQUERY_VERSION_, '1.7.2', '<')) {
            $this->restore_jquery = true;
        }

        $this->context->controller->addCSS($this->uri_location.'views/css/'.NewsletterProTools::getMin('app_front.css'), 'all');

        $this->context->controller->addJS([
            $this->uri_location.'views/js/'.NewsletterProTools::getMin('app_front.js'),
        ]);

        if ((int) pqnp_config('SUBSCRIPTION_ACTIVE')) {
            $this->initPopup();
        }

        $this->context->controller->addCSS(NewsletterProTools::getCSS('display_customer_account_form.css'), 'all');

        $this->initJSData($output);

        if (Tools::isSubmit('newsletterpro_source') && 'newsletter' == Tools::getValue('newsletterpro_source') && Tools::isSubmit('id_product')) {
            try {
                $id_product = (int) Tools::getValue('id_product');

                $product_exists = 'SELECT COUNT(*) FROM `'._DB_PREFIX_.'product` WHERE `id_product` = '.(int) $id_product.'';

                if (Db::getInstance()->getValue($product_exists) > 0) {
                    $cookie = new NewsletterProCookie('statistics_products', time() + 259200);

                    if (!$cookie->get('products_id')) {
                        $cookie->set('products_id', []);
                    }

                    $products_id = $cookie->get('products_id');

                    if (!in_array($id_product, $products_id)) {
                        $cookie->append('products_id', $id_product);

                        if (Tools::isSubmit('id_newsletter')) {
                            $id_history = (int) Tools::getValue('id_newsletter');
                            $history_sql = 'SELECT `clicks` FROM `'._DB_PREFIX_.'newsletter_pro_tpl_history` WHERE `id_newsletter_pro_tpl_history` = '.(int) $id_history.'';

                            $history = Db::getInstance()->getRow($history_sql);

                            if ($history && isset($history['clicks'])) {
                                $h_clicks = (int) $history['clicks'];
                                ++$h_clicks;

                                Db::getInstance()->update('newsletter_pro_tpl_history', [
                                    'clicks' => (int) $h_clicks,
                                ], '`id_newsletter_pro_tpl_history`='.(int) $id_history);
                            }
                        }

                        $need_update = 'SELECT `clicks` FROM `'._DB_PREFIX_.'newsletter_pro_statistics` WHERE `id_product` = '.(int) $id_product.'';
                        $clicks = Db::getInstance()->getValue($need_update);
                        if (false !== $clicks) {
                            ++$clicks;
                            Db::getInstance()->update('newsletter_pro_statistics', [
                                'clicks' => (int) $clicks,
                            ], '`id_product`='.(int) $id_product);
                        } else {
                            Db::getInstance()->insert('newsletter_pro_statistics', [
                                'id_product' => (int) $id_product,
                                'clicks' => 1,
                            ]);
                        }
                    }
                }
            } catch (Exception $e) {
                NewsletterProLog::writeStrip($e->getMessage(), NewsletterProLog::ERROR_FILE);
            }
        }

        if (Version::isLower('1.5.2.0') && isset($this->context->cookie->id_customer) && isset($this->context->cookie->id_lang)) {
            $id_lang = (int) $this->context->cookie->id_lang;
            $this->updateCustomerLanguage($id_lang);
        }

        if ((bool) pqnp_config('GOOGLE_ANALYTICS_ACTIVE')) {
            if ((bool) pqnp_config('GOOGLE_UNIVERSAL_ANALYTICS_ACTIVE')) {
                $google_analytics = pqnp_template_path($this->dir_location.'views/templates/admin/analyticstracking_new.tpl');
            } else {
                $google_analytics = pqnp_template_path($this->dir_location.'views/templates/admin/analyticstracking_old.tpl');
            }

            if (file_exists($google_analytics)) {
                $c_params = $this->getCampaignSubmition();
                $this->context->smarty->assign($c_params);

                $this->context->smarty->assign([
                        'CAMPAIGN_ACTIVE' => (bool) pqnp_config('CAMPAIGN_ACTIVE'),
                        'GOOGLE_ANALYTICS_ID' => pqnp_config('GOOGLE_ANALYTICS_ID'),
                        'HOST' => Tools::getHttpHost(false),
                    ]);

                $output .= $this->context->smarty->fetch($google_analytics);
            }
        } elseif ((bool) pqnp_config('CAMPAIGN_ACTIVE')) {
            $this->context->smarty->assign(['NEWSLETTER_CAMPAIGN' => self::getCampaignScript()]);
        }

        // $this->restoreJqueryIfNeeded('displayHeader');
        return $output;
    }

    public static function getNewsletterCampaign()
    {
        $output = '';

        if (Module::isInstalled('newsletterpro')) {
            $module = NewsletterPro::getInstance();
            if (!(bool) pqnp_config('GOOGLE_ANALYTICS_ACTIVE') && (bool) pqnp_config('CAMPAIGN_ACTIVE')) {
                $output = self::getCampaignScript();
            }
        }

        return $output;
    }

    /**
     * Get google campaign script.
     *
     * @return string
     */
    public static function getCampaignScript()
    {
        $module = NewsletterPro::getInstance();
        $c_params = $module->getCampaignSubmition();

        $script = '';
        if ((bool) pqnp_config('GOOGLE_UNIVERSAL_ANALYTICS_ACTIVE')) {
            // The new version of the Google Analytics API
            if (isset($c_params['utm_source'])) {
                $script .= 'ga(\'set\', \'campaignSource\', \'('.$c_params['utm_source'].')\');'."\n";
            }
            if (isset($c_params['utm_medium'])) {
                $script .= 'ga(\'set\', \'campaignMedium\', \'('.$c_params['utm_medium'].')\');'."\n";
            }
            if (isset($c_params['utm_campaign'])) {
                $script .= 'ga(\'set\', \'campaignName\', \'('.$c_params['utm_campaign'].')\');'."\n";
            }
            if (isset($c_params['utm_content'])) {
                $script .= 'ga(\'set\', \'campaignContent\', \'('.$c_params['utm_content'].')\');'."\n";
            }
        } else {
            // The old version of the Google Analytics API
            $script .= '_gaq.push([\'_setCampaignTrack\', true]);'."\n";
            if (isset($c_params['utm_source'])) {
                $script .= '_gaq.push([\'_setCampSourceKey\', \''.$c_params['utm_source'].'\']);'."\n";
            }
            if (isset($c_params['utm_medium'])) {
                $script .= '_gaq.push([\'_setCampMediumKey\', \''.$c_params['utm_medium'].'\']);'."\n";
            }
            if (isset($c_params['utm_campaign'])) {
                $script .= '_gaq.push([\'_setCampNameKey\', \''.$c_params['utm_campaign'].'\']);'."\n";
            }
            if (isset($c_params['utm_content'])) {
                $script .= '_gaq.push([\'_setCampContentKey\', \''.$c_params['utm_content'].'\']);'."\n";
            }
        }

        if ('testCampaign' == Tools::getValue('utm_source')) {
            http_response_code(205);
        }

        if (Tools::isSubmit('testCampaign')) {
            if ('script' == Tools::getValue('testCampaign')) {
                ddd($script);
            }
        }

        return $script;
    }

    /**
     * Get campaign post fields.
     *
     * @return array
     */
    public function getCampaignSubmition()
    {
        $params = [];
        $params_values = array_keys($this->getCampaignParamsArray());

        foreach ($params_values as $param) {
            if (Tools::isSubmit($param)) {
                // can replace addcslashes with Tools::safeOutput( from prestashop but first I need to check that
                $params[$param] = addcslashes(Tools::getValue($param), "'");
            }
        }

        if ('testCampaign' == Tools::getValue('utm_source')) {
            http_response_code(205);
        }

        if (Tools::isSubmit('testCampaign')) {
            if ('params' == Tools::getValue('testCampaign')) {
                ddd($params);
            }
        }

        return $params;
    }

    /**
     * Add a restriction on id_shop for multishop lang table.
     *
     * @param string  $alias
     * @param Context $context
     *
     * @return string
     */
    public static function addSqlRestrictionOnLang($alias = null, $id_shop = null)
    {
        if (version_compare(_PS_VERSION_, '1.5.2.0', '>=')) {
            return Shop::addSqlRestrictionOnLang($alias, $id_shop);
        }

        if (is_null($id_shop)) {
            $id_shop = Context::getContext()->shop->id;
        }

        return ' AND '.(($alias) ? $alias.'.' : '').'id_shop = '.$id_shop.' ';
    }

    /**
     * This method allow to return children categories with the number of sub children selected for a product.
     *
     * @param int $id_parent
     * @param int $id_product
     * @param int $id_lang
     *
     * @return array
     */
    public static function getChildrenWithNbSelectedSubCat($id_parent, $selected_cat, $id_lang, Shop $shop = null, $use_shop_context = true)
    {
        if (!$shop) {
            $shop = Context::getContext()->shop;
        }

        $id_shop = $shop->id ? $shop->id : Configuration::get('PS_SHOP_DEFAULT');
        $selected_cat = explode(',', str_replace(' ', '', $selected_cat));
        $sql = '
		SELECT c.`id_category`, c.`level_depth`, cl.`name`,
		IF((
			SELECT COUNT(*)
			FROM `'._DB_PREFIX_.'category` c2
			WHERE c2.`id_parent` = c.`id_category`
		) > 0, 1, 0) AS has_children,
		'.($selected_cat ? '(
			SELECT count(c3.`id_category`)
			FROM `'._DB_PREFIX_.'category` c3
			WHERE c3.`nleft` > c.`nleft`
			AND c3.`nright` < c.`nright`
			AND c3.`id_category`  IN ('.implode(',', array_map('intval', $selected_cat)).')
		)' : '0').' AS nbSelectedSubCat
		FROM `'._DB_PREFIX_.'category` c
		LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON (c.`id_category` = cl.`id_category` '.NewsletterPro::addSqlRestrictionOnLang('cl', (int) $id_shop).')
		LEFT JOIN `'._DB_PREFIX_.'category_shop` cs ON (c.`id_category` = cs.`id_category` AND cs.`id_shop` = '.(int) $id_shop.')
		WHERE `id_lang` = '.(int) $id_lang.'
		AND c.`active` = 1
		AND c.`id_parent` = '.(int) $id_parent;

        if (Shop::CONTEXT_SHOP == Shop::getContext() && $use_shop_context) {
            $sql .= ' AND cs.`id_shop` = '.(int) $shop->id;
        }

        if (!Shop::isFeatureActive() || Shop::CONTEXT_SHOP == Shop::getContext() && $use_shop_context) {
            $sql .= ' ORDER BY cs.`position` ASC';
        }

        return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
    }

    /**
     * Explode the campaign params into an array.
     *
     * @return array
     */
    public function getCampaignParamsArray()
    {
        $params_str = Configuration::get('NEWSLETTER_PRO_CAMPAIGN');
        $params_exp = explode("\n", $params_str);

        $params = [];
        foreach ($params_exp as $key => $param) {
            $param_exp = explode('=', $param);

            if (isset($param_exp[0], $param_exp[1])) {
                $params[trim($param_exp[0])] = trim($param_exp[1]);
            }
        }
        $utm = [];
        $utm_db = pqnp_config('CAMPAIGN');
        foreach ($utm_db as $key => $value) {
            $utm[Tools::strtolower($key)] = $value;
        }

        $return = array_merge($params, $utm);

        return $return;
    }

    /**
     * Hook create account.
     *
     * @param array $params
     *
     * @return string
     */
    public function hookCreateAccount($params)
    {
        if (isset($params['newCustomer']) && Validate::isLoadedObject($params['newCustomer'])) {
            /** @var Customer */
            $customer = $params['newCustomer'];

            if (Tools::isSubmit('pqnp_newsletter')) {
                $customer->newsletter = true;
                $customer->update();
            }
        }

        if ((bool) pqnp_config('SEND_NEWSLETTER_ON_SUBSCRIBE')) {
            if (isset($params['newCustomer']) && Validate::isLoadedObject($params['newCustomer'])) {
                $customer = $params['newCustomer'];
                try {
                    $this->sendLastNewsletter((string) $customer->email);
                } catch (Exception $e) {
                    NewsletterProLog::writeStrip($e->getMessage(), NewsletterProLog::ERROR_FILE);
                }
            }
        }

        if (Version::isLower('1.5.4.0')) {
            if (isset($this->context->cookie->id_customer) && isset($this->context->cookie->id_lang)) {
                $id_lang = (int) $this->context->cookie->id_lang;
                $this->updateCustomerLanguage($id_lang);
            }
        }

        // fix the bug for some shops
        if (!isset($this->context->cart)) {
            $this->context->cart = new Cart();
        }
    }

    /**
     * Send the last newsletter to the new customer subscribed.
     *
     * @param string $email
     */
    public function sendLastNewsletter($email)
    {
        $id_history = $this->getLastSendNewsletterIdHisotry();

        if ($id_history > 0) {
            $template = NewsletterProTemplate::newHistory((int) $id_history, $email)->load();
            $message = $template->message();

            $send_manager = NewsletterProSendManager::getInstance();
            $send_manager->setTemplateNameForAttachment($template->name);
            $send_manager->sendNewsletter($message['title'], $message['body'], $template->user->to(), [
                'user' => $template->user,
            ]);
        }
    }

    /**
     * Get the last newsletter id sent from the hisotry.
     *
     * @return int
     */
    public function getLastSendNewsletterIdHisotry()
    {
        $send_count = 10;

        $sql = 'SELECT MAX(`id_newsletter_pro_tpl_history`) AS `id_newsletter_pro_tpl_history`, `date`
				FROM `'._DB_PREFIX_.'newsletter_pro_send`
				WHERE `emails_completed` >= '.(int) $send_count;
        $send = Db::getInstance()->getRow($sql);
        $send_date = $send['date'];

        $sql = 'SELECT MAX(`id_newsletter_pro_tpl_history`) AS `id_newsletter_pro_tpl_history`, `date_start`
				FROM `'._DB_PREFIX_.'newsletter_pro_task`
				WHERE `emails_completed` >= '.(int) $send_count;

        $task = Db::getInstance()->getRow($sql);
        $task_date = $task['date_start'];

        $id_newsletter_pro_tpl_history = 0;

        if (!isset($send_date) && isset($task_date)) {
            $id_newsletter_pro_tpl_history = (int) $task['id_newsletter_pro_tpl_history'];
        } elseif (isset($send_date) && !isset($task_date)) {
            $id_newsletter_pro_tpl_history = (int) $send['id_newsletter_pro_tpl_history'];
        } elseif (isset($send_date) && isset($task_date)) {
            if (strtotime($send_date) < strtotime($task_date)) {
                $id_newsletter_pro_tpl_history = (int) $task['id_newsletter_pro_tpl_history'];
            } elseif (strtotime($send_date) > strtotime($task_date)) {
                $id_newsletter_pro_tpl_history = (int) $send['id_newsletter_pro_tpl_history'];
            } elseif ((int) $send['id_newsletter_pro_tpl_history'] < (int) $task['id_newsletter_pro_tpl_history']) {
                $id_newsletter_pro_tpl_history = (int) $task['id_newsletter_pro_tpl_history'];
            } else {
                $id_newsletter_pro_tpl_history = (int) $send['id_newsletter_pro_tpl_history'];
            }
        } else {
            $id_newsletter_pro_tpl_history = 0;
        }

        return $id_newsletter_pro_tpl_history;
    }

    /**
     * Update customer language.
     *
     * @param int $id_lang
     */
    public function updateCustomerLanguage($id_lang)
    {
        $sql = 'SELECT count(*) AS count FROM `'._DB_PREFIX_.'lang` where `id_lang` = '.(int) $id_lang.' AND `active` = 1';
        $count = Db::getInstance()->executeS($sql);

        if (!empty($count) && isset($count[0]['count']) && $count[0]['count'] >= 1) {
            Db::getInstance()->execute('
				UPDATE `'._DB_PREFIX_.'customer` SET `id_lang` = '.(int) $id_lang.'
				WHERE `id_customer` = '.(int) $this->context->cookie->id_customer.' LIMIT 1;
			');
        }
    }

    /**
     * Install the database field id_lang for the oder versions of prestashop.
     *
     * @param string $column_name
     *
     * @return bool
     */
    public function installLang($column_name = 'id_lang')
    {
        $sql = "SELECT COUNT(*) AS `count` FROM INFORMATION_SCHEMA.COLUMNS
				WHERE TABLE_SCHEMA = '"._DB_NAME_."'
				AND TABLE_NAME='"._DB_PREFIX_."customer'
				AND COLUMN_NAME = '".pSQL($column_name)."';";

        $count = Db::getInstance()->executeS($sql);

        if (!empty($count) && isset($count[0]['count']) && 0 == $count[0]['count']) {
            $sql = 'ALTER TABLE `'._DB_PREFIX_.'customer` ADD COLUMN `'.pSQL($column_name).'` INT(10) UNSIGNED NULL DEFAULT NULL;';
            if (!Db::getInstance()->execute($sql)) {
                $this->_errors[] = $this->l('Cannot create the field "id_lang" into the table "customer".');

                return false;
            }

            $id_default_lang = (int) Configuration::get('PS_LANG_DEFAULT');
            $sql = 'UPDATE `'._DB_PREFIX_.'customer` SET `'.pSQL($column_name).'` = '.(int) $id_default_lang.';';

            if (!Db::getInstance()->execute($sql)) {
                $this->_errors[] = $this->l('Cannot update the language for the existing customers into the table "customer".');

                return false;
            }
        }

        if (!$this->registerHook('createAccount')) {
            $this->_errors[] = $this->l('The module cannot be registered to hook "createAccount".');

            return false;
        }

        return true;
    }

    /**
     * Fix the prestashop problem shop name in single store.
     *
     * @param string $shop_name
     *
     * @return bool
     */
    public function updateShopName($shop_name)
    {
        $sql = 'UPDATE `'._DB_PREFIX_.'shop` SET `name` = "'.pSQL($shop_name).'" WHERE `id_shop` = '.(int) $this->context->shop->id.' LIMIT 1;';
        if (!Db::getInstance()->execute($sql)) {
            $this->_errors[] = $this->l('Cannot update the shop name.');
        }

        return true;
    }

    /**
     * Is prestashop 1.6 version.
     *
     * @return bool
     */
    public function isPS16()
    {
        return Version::isHigher('1.5.9.9');
    }

    /**
     * Install the module configuration.
     *
     * @return bool
     */
    public function installConfiguration()
    {
        if (!Configuration::updateValue('NEWSLETTER_PRO_CAMPAIGN', '', false, 0, 0)) {
            $this->_errors[] = sprintf($this->l('The configuration "%s" cannot be installed into "%s" table.'), 'NEWSLETTER_PRO_CAMPAIGN', _DB_PREFIX_.'configuration');

            return false;
        }

        if (!Config::install()) {
            $this->_errors[] = sprintf($this->l('The configuration "%s" cannot be installed into "%s" table.'), Config::NAME, _DB_PREFIX_.'configuration');

            return false;
        }

        if (!NewsletterProConfigurationShop::install()) {
            $this->_errors[] = array_merge($this->_errors, NewsletterProConfigurationShop::getErrors());

            return false;
        }

        return true;
    }

    /**
     * Uninstall the module configuraion.
     *
     * @return bool
     */
    public function uninstallConfiguration()
    {
        return Configuration::deleteByName(Config::NAME)
                && Configuration::deleteByName('NEWSLETTER_PRO_CAMPAIGN')
                && NewsletterProConfigurationShop::uninstall();
    }

    /**
     * Initialize configuration.
     */
    public function initConfiguration()
    {
        Config::init();
        NewsletterProConfigurationShop::init();
    }

    /**
     * Redirect to the module dashboard if the employee will click on the module config anchor.
     */
    public function getContent()
    {
        Tools::redirectAdmin($this->link);
    }

    /**
     * Install module database tables, fields and data.
     *
     * @return bool
     */
    public function installDb()
    {
        /** @var NewsletterProInstall */
        $install = null;
        require_once _NEWSLETTER_PRO_DIR_.'/sql/install.php';

        if (!$install->execute()) {
            $this->mergeErrors($install->getErrors());

            return false;
        }

        // add records into the  database
        require_once _NEWSLETTER_PRO_DIR_.'/install/Install-3.1.1.php';

        // install the "newsletter_pro_config" table
        if (!NewsletterProConfig::install()) {
            $this->mergeErrors(NewsletterProConfig::getErrors());

            return false;
        }

        return $this->success();
    }

    /**
     * Add an error to the module.
     */
    public function addError($error)
    {
        if (is_array($error)) {
            foreach ($error as $err) {
                $this->_errors[] = $err;
            }
        } else {
            $this->_errors[] = $error;
        }
    }

    /**
     * Check if the module has errors.
     *
     * @return bool
     */
    public function success()
    {
        return empty($this->_errors);
    }

    /**
     * Add errors as array.
     *
     * @param array $errors
     */
    public function mergeErrors($errors)
    {
        $this->_errors = array_merge($this->_errors, $errors);
    }

    /**
     * Install the module to the customers menu.
     *
     * @param string $tab_class
     * @param string $tab_name
     * @param string $tab_parent_name
     *
     * @return bool
     */
    public function installModuleTab($tab_class, $tab_name, $tab_parent_name = 'AdminCustomers')
    {
        if (NewsletterProTools::is17()) {
            $tab_parent_name = 'AdminParentCustomer';
        }

        $logo_file = _NEWSLETTER_PRO_DIR_.'/logo.png';
        $logo_destination = _PS_IMG_DIR_.'t/'.$tab_class.'.png';
        copy($logo_file, $logo_destination);

        $id = Tab::getIdFromClassName($tab_class);

        $tab = new Tab((int) $id);

        foreach (Language::getLanguages(false) as $lang) {
            $tab->name[$lang['id_lang']] = $tab_name;
        }

        $tab->class_name = $tab_class;
        $tab->module = $this->name;
        $tab->id_parent = (int) Tab::getIdFromClassName($tab_parent_name);
        $tab->active = 1;

        if (version_compare(_PS_VERSION_, '1.5.1.0', '<=')) {
            if (!$tab->save()) {
                return (bool) $tab->id;
            }

            return true;
        } else {
            if (!$tab->save()) {
                return false;
            }

            return true;
        }
    }

    /**
     * Uninstall the module from the menu.
     *
     * @param string $tab_class
     *
     * @return bool
     */
    public function uninstallModuleTab($tab_class)
    {
        $id_tab = Tab::getIdFromClassName($tab_class);
        if (0 != $id_tab) {
            $tab = new Tab($id_tab);
            $tab->delete();

            return true;
        }

        return false;
    }

    /**
     * Copy data from the $_POST variable.
     *
     * @param object $object
     * @param string $table
     */
    protected function copyFromPost(&$object, $table)
    {
        $post = &$_POST;

        foreach ($post as $key => $value) {
            if (array_key_exists($key, $object) && $key != 'id_'.$table) {
                if ('passwd' == $key && Tools::getValue('id_'.$table) && empty($value)) {
                    continue;
                }

                if ('passwd' == $key && !empty($value)) {
                    $value = Tools::encrypt($value);
                }
                $object->{$key} = $value;
            }
        }

        $rules = call_user_func([get_class($object), 'getValidationRules'], get_class($object));
        if (count($rules['validateLang'])) {
            $languages = Language::getLanguages(false);
            foreach ($languages as $language) {
                foreach (array_keys($rules['validateLang']) as $field) {
                    if (isset($post[$field.'_'.(int) $language['id_lang']])) {
                        $object->{$field}[(int) $language['id_lang']] = $post[$field.'_'.(int) $language['id_lang']];
                    }
                }
            }
        }
    }

    /**
     * Update asso shop.
     *
     * @param int    $id_object
     * @param string $table
     *
     * @return int/bool
     */
    public function updateAssoShop($id_object, $table)
    {
        if (!Shop::isFeatureActive()) {
            return;
        }

        if (!NewsletterPro::isTableAssociated($table)) {
            return;
        }

        $assos_data = $this->getSelectedAssoShop($table, $id_object);

        $exclude_ids = $assos_data;
        foreach (Db::getInstance()->executeS('SELECT id_shop FROM '._DB_PREFIX_.'shop') as $row) {
            if (!$this->context->employee->hasAuthOnShop($row['id_shop'])) {
                $exclude_ids[] = $row['id_shop'];
            }
        }

        Db::getInstance()->delete($table.'_shop', '`'.$id_object.'` = '.(int) $id_object.($exclude_ids ? ' AND id_shop NOT IN ('.implode(', ', $exclude_ids).')' : ''));

        $insert = [];
        foreach ($assos_data as $id_shop) {
            $insert[] = [
                $id_object => $id_object,
                'id_shop' => (int) $id_shop,
            ];
        }

        return Db::getInstance()->insert($table.'_shop', $insert, false, true, Db::INSERT_IGNORE);
    }

    /**
     * Get asso shop.
     *
     * @param string $table
     *
     * @return array
     */
    public function getSelectedAssoShop($table)
    {
        if (!Shop::isFeatureActive() || !NewsletterPro::isTableAssociated($table)) {
            return [];
        }

        $shops = Shop::getShops(true, null, true);

        if (1 == count($shops) && isset($shops[0])) {
            return [$shops[0], 'shop'];
        }

        $assos = [];
        if (Tools::isSubmit('checkBoxShopAsso_'.$table)) {
            $check = Tools::getValue('checkBoxShopAsso_'.$table);

            foreach (array_keys($check) as $id_shop) {
                $assos[] = (int) $id_shop;
            }
        } elseif (1 == Shop::getTotalShops(false)) {
            $assos[] = (int) NewsletterPro::getContextShopID();
        }

        return $assos;
    }

    /**
     * Uninstall the module from the menu.
     *
     * @return bool
     */
    public function uninstallFromMenu()
    {
        $id_tab = Tab::getIdFromClassName($this->class_name);
        $tab = new Tab($id_tab);

        return $tab->delete();
    }

    /**
     * Uninstall the database.
     *
     * @return bool
     */
    private function uninstallDb()
    {
        // this option will always be uninstalled
        $full_uninstall = [
            'newsletter_pro_send',
            'newsletter_pro_send_step',
            'newsletter_pro_smtp',
            'newsletter_pro_task',
            'newsletter_pro_task_step',
            // 3.2.8
            'newsletter_pro_email_exclusion',
            // in 4.0.0
            'newsletter_pro_filters_selection',
            'newsletter_pro_send_connection',
            // in 4.0.1
            'newsletter_pro_attachment',
            'newsletter_pro_subscribers_custom_field',
            'newsletter_pro_subscribers_custom_field_lang',
            'newsletter_pro_mailchimp_token',
        ];

        // this tables will uninstall only if the option uninstall_all_tables is true
        $protected_uninstall = [
            'newsletter_pro_email',
            'newsletter_pro_tpl_history',
            'newsletter_pro_tpl_history_lang',
            'newsletter_pro_customer_category',
            'newsletter_pro_customer_list_of_interests',
            'newsletter_pro_statistics',
            'newsletter_pro_unsibscribed',
            'newsletter_pro_fwd_unsibscribed',
            'newsletter_pro_forward',
            // 3.1.1
            'newsletter_pro_list_of_interest',
            'newsletter_pro_list_of_interest_lang',
            'newsletter_pro_list_of_interest_shop',
            'newsletter_pro_subscribers',
            'newsletter_pro_subscribers_temp',
            'newsletter_pro_subscription_tpl',
            'newsletter_pro_subscription_tpl_lang',
            'newsletter_pro_subscription_tpl_shop',
            // 4.5.5
            'newsletter_pro_subscription_consent',
        ];

        foreach ($full_uninstall as $table_name) {
            if (!Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.$table_name.'`;')) {
                return false;
            }
        }

        if ($this->uninstall_all_tables) {
            foreach ($protected_uninstall as $table_name) {
                if (!Db::getInstance()->execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.$table_name.'`;')) {
                    return false;
                }
            }

            if (!NewsletterProConfig::uninstall()) {
                return false;
            }
        }

        return true;
    }

    private function uninstallAttachments()
    {
        $files = NewsletterProTools::getDirectoryIterator($this->tpl_location.'attachments/', '/.*/');

        foreach ($files as $file) {
            if ($file->isFile() && !in_array($file->getFileName(), ['.htaccess', 'index.php'])) {
                @unlink($file->getPathName());
            }
        }

        return true;
    }

    /**
     * Get the context shop id.
     *
     * @param  bool
     *
     * @return bool
     */
    public static function getContextShopID($null_value_without_multishop = false)
    {
        if (method_exists('Shop', 'getContextShopID')) {
            return Shop::getContextShopID($null_value_without_multishop);
        }

        return Shop::getContextShopIDNewsletterPro($null_value_without_multishop);
    }

    /**
     * Get the context shop group id.
     *
     * @param bool $null_value_without_multishop
     *
     * @return bool
     */
    public static function getContextShopGroupID($null_value_without_multishop = false)
    {
        if (method_exists('Shop', 'getContextShopGroupID')) {
            return Shop::getContextShopGroupID($null_value_without_multishop);
        }

        return Shop::getContextShopGroupIDNewsletterPro($null_value_without_multishop);
    }

    /**
     * Check if the database table is associated.
     *
     * @param string $table
     *
     * @return bool
     */
    public static function isTableAssociated($table)
    {
        if (method_exists('Shop', 'isTableAssociated')) {
            return Shop::isTableAssociated($table);
        }

        return Shop::isTableAssociatedNewsletterPro($table);
    }

    /**
     * Get shops groups.
     *
     * @param bool $active
     *
     * @return array
     */
    public static function getShopGroups($active = true)
    {
        if (class_exists('ShopGroup')) {
            return ShopGroup::getShopGroups($active);
        }

        return GroupShop::getGroupShops($active);
    }

    /**
     * Init content.
     */
    private function initContext()
    {
        $this->context = Context::getContext();
        // Set Front Office Link
        $protocol_link = (Configuration::get('PS_SSL_ENABLED') || Tools::usingSecureMode()) ? 'https://' : 'http://';
        $use_ssl = ((isset($this->ssl) && $this->ssl && Configuration::get('PS_SSL_ENABLED')) || Tools::usingSecureMode()) ? true : false;
        $protocol_content = ($use_ssl) ? 'https://' : 'http://';
        $link = new Link($protocol_link, $protocol_content);
        $link->protocol_link = $this->relplaceAdminLink($link->protocol_link);
        $link->protocol_content = $this->relplaceAdminLink($link->protocol_content);

        $this->context->link = &$link;

        if (!isset($this->context->shop->id_shop_group)) {
            $this->context->shop->id_shop_group = &$this->context->shop->id_group_shop;
        }

        if (!Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE')) {
            $ps_shop_name = Configuration::get('PS_SHOP_NAME');
            $ps_shop_default = (int) Configuration::get('PS_SHOP_DEFAULT');

            if ($ps_shop_name && $ps_shop_default && ($this->context->shop->name != $ps_shop_name)) {
                Db::getInstance()->update('shop', [
                    'name' => pSQL($ps_shop_name),
                ], '`id_shop` = '.(int) $ps_shop_default, 1);
            }
        }
    }

    /**
     * Save products number per row.
     *
     * @param int $number
     *
     * @return json
     */
    public function saveProductNumberPerRow($number)
    {
        $errors = [];
        $response = ['status' => false, 'errors' => &$errors];

        $template = pqnp_config('PRODUCT_TEMPLATE');
        $path = $this->tpl_location.'product/'.$template;

        if ('default.html' == $template) {
            $errors[] = $this->l('Save a copy of the default template to change the number of products on row.');
        } else {
            if (file_exists($path)) {
                $content = Tools::file_get_contents($path);
                $full_content = preg_replace('/\{columns=\d+\}(\n)?/', '{columns='.$number.'}', $content);

                if ($handle = fopen($path, 'w')) {
                    fwrite($handle, $full_content);
                    fclose($handle);
                } else {
                    $errors[] = $this->l('Template cannot be written');
                }
            } else {
                $errors[] = $this->l('Missing template file');
            }
        }

        if (empty($errors)) {
            $response['status'] = true;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Save product template.
     *
     * @param string $content
     * @param int    $product_nr
     *
     * @return json
     */
    public function saveProductTemplate($content, $product_nr)
    {
        $data = [];

        $template = pqnp_config('PRODUCT_TEMPLATE');
        $path = $this->tpl_location.'product/'.$template;

        if ('default.html' == $template) {
            $data['message'] = $this->l('You cannot override the default templates. Please save a copy.');
            $data['type'] = false;

            return Tools::jsonEncode($data);
        }

        if (file_exists($path)) {
            if (isset($content) && false != $content) {
                if (preg_match('/<!-- \{columns=\d+\} -->/', $content, $match)) {
                    $content = str_replace($match[1], '<!-- {columns='.$product_nr.'} -->', $content);
                }

                $header = '';
                // $header = '<!-- {columns='.$product_nr."} -->\n";
                // $header = '<!-- {columns='.$product_nr."} -->\n";
                $full_content = $header.$content;

                if (preg_match('/<(?=\/?img(?=>|\s.*>))\/?.*?>/', $full_content, $img_match)) {
                    $img_match = (string) $img_match[0];

                    $img_re_match = preg_replace('/\ssrc\="[^"]*"/', ' src="{image_path}"', $img_match);
                    $img_re_match = preg_replace('/\swidth\="[^"]*"/', ' width="{image_width}"', $img_re_match);
                    $img_re_match = preg_replace('/\sheight\="[^"]*"/', ' height="{image_height}"', $img_re_match);

                    $full_content = str_replace($img_match, $img_re_match, $full_content);
                }

                if ($handle = fopen($path, 'w')) {
                    fwrite($handle, $full_content);
                    fclose($handle);
                    $data['message'] = $this->l('Update successfully');
                    $data['type'] = true;
                    $data['content'] = $full_content;
                } else {
                    $data['message'] = $this->l('Template cannot be written');
                    $data['type'] = false;
                }
            } else {
                $data['message'] = $this->l('Template not saved');
                $data['type'] = false;
            }
        } else {
            $data['message'] = $this->l('Missing template file');
            $data['type'] = false;
        }

        return Tools::jsonEncode($data);
    }

    /**
     * Save as the product template.
     *
     * @param string $name
     * @param string $content
     * @param int    $product_nr
     *
     * @return json
     */
    public function saveAsProductTemplate($name, $content, $product_nr)
    {
        $name = preg_replace('/\s+/i', '_', $name);
        $validate_name = $this->verifyName($name);

        if (true !== $validate_name) {
            return '{"status":"'.false.'", "msg":"'.$validate_name.'"}';
        }

        $name = $name.'.html';
        $path = $this->tpl_location.'product/'.$name;

        if (!file_exists($path)) {
            if (isset($content) && false != $content) {
                if (preg_match('/<!-- \{columns=\d+\} -->/', $content, $match)) {
                    $content = str_replace($match[1], '<!-- {columns='.$product_nr.'} -->', $content);
                }

                $header = '';
                // $header = '<!-- {columns='.$product_nr."} -->\n";
                $full_content = $header.$content;

                $header_content = '';
                if (preg_match('/<!-- start header -->\s*?<!--(?P<header>[\s\S]*)-->\s*?<!-- end header -->/', $content, $match)) {
                    $header_content = trim($match['header']);
                }

                if (!preg_match('/content\s+=\s+template/', $header_content) && preg_match('/<(?=\/?img(?=>|\s.*>))\/?.*?>/', $full_content, $img_match)) {
                    $img_match = (string) $img_match[0];

                    $img_re_match = preg_replace('/\ssrc\="[^"]*"/', ' src="{image_path}"', $img_match);
                    $img_re_match = preg_replace('/\swidth\="[^"]*"/', ' width="{image_width}"', $img_re_match);
                    $img_re_match = preg_replace('/\sheight\="[^"]*"/', ' height="{image_height}"', $img_re_match);

                    $full_content = str_replace($img_match, $img_re_match, $full_content);
                }

                if ($handle = fopen($path, 'w')) {
                    fwrite($handle, $full_content);
                    fclose($handle);
                    pqnp_config('PRODUCT_TEMPLATE', $name);

                    return Tools::jsonEncode([
                        'status' => true,
                        'msg' => '',
                        'full_content' => $full_content,
                    ]);
                } else {
                    return '{"status":"'.false.'", "msg":"'.$this->l('Cannot write the template').'"}';
                }
            } else {
                return '{"status":"'.false.'", "msg":"'.$this->l('Template was not saved').'"}';
            }
        } else {
            return '{"status":"'.false.'", "msg":"'.$this->l('This file name already exists').'"}';
        }

        return '{"status":"'.false.'", "msg":"'.$this->l('Saved unsuccessfully').'"}';
    }

    /**
     * Get all products from category by id category.
     *
     * @param int $id_category
     *
     * @return json
     */
    public function getProducts($id_category)
    {
        $data = [];
        $id_lang = (int) pqnp_config('LANG');
        $order_by_prefix = null;
        $front = false;
        $order_by = 'name';
        $order_way = 'ASC';
        $limit = 1000;
        $id_supplier = false;
        $start = 0;
        $only_active = pqnp_config('ONLY_ACTIVE_PRODUCTS');

        if (Version::isBetween('1.5.0.0', '1.5.0.6')) {
            $sql = 'SELECT p.*, pl.* , t.`rate` AS tax_rate, m.`name` AS manufacturer_name, s.`name` AS supplier_name, i.`id_image`, il.`legend`
				FROM `'._DB_PREFIX_.'product` p
				'.$this->context->shop->addSqlAssociation('product', 'p').'
				LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` '.$this->context->shop->addSqlRestrictionOnLang('pl').')
				LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr ON (p.`id_tax_rules_group` = tr.`id_tax_rules_group`
				AND tr.`id_country` = '.(int) $this->context->country->id.'
				AND tr.`id_state` = 0)
				LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.`id_tax` = tr.`id_tax`)
				LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product`)  AND i.cover = 1
				LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON ( i.`id_image` = il.`id_image`) AND il.`id_lang` = '.(int) $id_lang.'
				LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (m.`id_manufacturer` = p.`id_manufacturer`)
				LEFT JOIN `'._DB_PREFIX_.'supplier` s ON (s.`id_supplier` = p.`id_supplier`)'.
                ($id_category ? 'LEFT JOIN `'._DB_PREFIX_.'category_product` c ON (c.`id_product` = p.`id_product`)' : '').'
				WHERE pl.`id_lang` = '.(int) $id_lang.
                    ($id_category ? ' AND c.`id_category` = '.(int) $id_category : '').
                    ($only_active ? ' AND p.`active` = 1' : '').'
				GROUP BY p.`id_product`
				ORDER BY '.(isset($order_by_prefix) ? pSQL($order_by_prefix).'.' : '').'`'.pSQL($order_by).'` '.pSQL($order_way).
                ($limit > 0 ? ' LIMIT '.(int) $start.','.(int) $limit : '');
        } elseif (Version::isBetween('1.5.0.4', '1.5.0.18')) {
            $sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, product_attribute_shop.`id_product_attribute`,
						pl.`description`, pl.`description_short`, pl.`available_now`,
						pl.`available_later`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, image_shop.`id_image`,
						il.`legend`, m.`name` AS manufacturer_name, tl.`name` AS tax_name, t.`rate`, cl.`name` AS category_default,
						DATEDIFF(product_shop.`date_add`, DATE_SUB(NOW(),
						INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).'
							DAY)) > 0 AS new,
						(product_shop.`price` * IF(t.`rate`,((100 + (t.`rate`))/100),1)) AS orderprice
					FROM `'._DB_PREFIX_.'category_product` cp
					LEFT JOIN `'._DB_PREFIX_.'product` p
						ON p.`id_product` = cp.`id_product`
					'.Shop::addSqlAssociation('product', 'p').'
					LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa
					ON (p.`id_product` = pa.`id_product`)
					'.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.`default_on` = 1').'
					'.Product::sqlStock('p', 'product_attribute_shop', false, $this->context->shop).'
					LEFT JOIN `'._DB_PREFIX_.'category_lang` cl
						ON (product_shop.`id_category_default` = cl.`id_category`
						AND cl.`id_lang` = '.(int) $id_lang.Shop::addSqlRestrictionOnLang('cl').')
					LEFT JOIN `'._DB_PREFIX_.'product_lang` pl
						ON (p.`id_product` = pl.`id_product`
						AND pl.`id_lang` = '.(int) $id_lang.Shop::addSqlRestrictionOnLang('pl').')
					LEFT JOIN `'._DB_PREFIX_.'image` i
					ON (i.`id_product` = p.`id_product`)'.
                    Shop::addSqlAssociation('image', 'i', false, 'i.cover=1').'
					LEFT JOIN `'._DB_PREFIX_.'image_lang` il
						ON (image_shop.`id_image` = il.`id_image`
						AND il.`id_lang` = '.(int) $id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr
						ON (product_shop.`id_tax_rules_group` = tr.`id_tax_rules_group`
						AND tr.`id_country` = '.(int) $this->context->country->id.'
						AND tr.`id_state` = 0
						AND tr.`zipcode_from` = 0)
					LEFT JOIN `'._DB_PREFIX_.'tax` t
						ON (t.`id_tax` = tr.`id_tax`)
					LEFT JOIN `'._DB_PREFIX_.'tax_lang` tl
						ON (t.`id_tax` = tl.`id_tax`
						AND tl.`id_lang` = '.(int) $id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'manufacturer` m
						ON m.`id_manufacturer` = p.`id_manufacturer`
					WHERE product_shop.`id_shop` = '.(int) $this->context->shop->id.'
					AND (pa.id_product_attribute IS NULL OR product_attribute_shop.id_shop='.(int) $this->context->shop->id.')
					AND (i.id_image IS NULL OR image_shop.id_shop='.(int) $this->context->shop->id.')
						AND cp.`id_category` = '.(int) $id_category
                        .($only_active ? ' AND product_shop.`active` = 1' : '')
                        .($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '')
                        .($id_supplier ? ' AND p.id_supplier = '.(int) $id_supplier : '')
                        .'
					GROUP BY p.`id_product`
					ORDER BY `'.pSQL($order_by).'` '.pSQL($order_way).' LIMIT 0,'.(int) $limit.';';
        } else {
            $sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, product_attribute_shop.`id_product_attribute`,
						pl.`description`, pl.`description_short`, pl.`available_now`,
						pl.`available_later`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, image_shop.`id_image`,
						il.`legend`, m.`name` AS manufacturer_name, tl.`name` AS tax_name, t.`rate`, cl.`name` AS category_default,
						DATEDIFF(product_shop.`date_add`, DATE_SUB(NOW(),
						INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).'
							DAY)) > 0 AS new,
						(product_shop.`price` * IF(t.`rate`,((100 + (t.`rate`))/100),1)) AS orderprice
					FROM `'._DB_PREFIX_.'category_product` cp
					LEFT JOIN `'._DB_PREFIX_.'product` p
						ON p.`id_product` = cp.`id_product`
					'.Shop::addSqlAssociation('product', 'p').'
					LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa
					ON (p.`id_product` = pa.`id_product` )
					'.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.`default_on` = 1').'
					'.Product::sqlStock('p', 'product_attribute_shop', false, $this->context->shop).'
					LEFT JOIN `'._DB_PREFIX_.'category_lang` cl
						ON (product_shop.`id_category_default` = cl.`id_category`
						AND cl.`id_lang` = '.(int) $id_lang.Shop::addSqlRestrictionOnLang('cl').')
					LEFT JOIN `'._DB_PREFIX_.'product_lang` pl
						ON (p.`id_product` = pl.`id_product`
						AND pl.`id_lang` = '.(int) $id_lang.Shop::addSqlRestrictionOnLang('pl').')
					LEFT JOIN `'._DB_PREFIX_.'image` i
						ON (i.`id_product` = p.`id_product`)'.
                    Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').'
					LEFT JOIN `'._DB_PREFIX_.'image_lang` il
						ON (image_shop.`id_image` = il.`id_image`
						AND il.`id_lang` = '.(int) $id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr
						ON (product_shop.`id_tax_rules_group` = tr.`id_tax_rules_group`
						AND tr.`id_country` = '.(int) $this->context->country->id.'
						AND tr.`id_state` = 0
						AND tr.`zipcode_from` = 0)
					LEFT JOIN `'._DB_PREFIX_.'tax` t
						ON (t.`id_tax` = tr.`id_tax`)
					LEFT JOIN `'._DB_PREFIX_.'tax_lang` tl
						ON (t.`id_tax` = tl.`id_tax`
						AND tl.`id_lang` = '.(int) $id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'manufacturer` m
						ON m.`id_manufacturer` = p.`id_manufacturer`
					WHERE product_shop.`id_shop` = '.(int) $this->context->shop->id.'
					AND (pa.id_product_attribute IS NULL OR product_attribute_shop.id_shop='.(int) $this->context->shop->id.')
					AND (i.id_image IS NULL OR image_shop.id_shop='.(int) $this->context->shop->id.')
						AND cp.`id_category` = '.(int) $id_category
                        .($only_active ? ' AND product_shop.`active` = 1' : '')
                        .($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '')
                        .($id_supplier ? ' AND p.id_supplier = '.(int) $id_supplier : '')
                        .'
					GROUP BY p.`id_product`
					ORDER BY `'.pSQL($order_by).'` '.pSQL($order_way).' LIMIT 0,'.(int) $limit.';';
        }

        if ((int) pqnp_ini_config('get_category_products_prestashop')) {
            $category = new Category($id_category);
            $products = $category->getProducts($id_lang, 1, 999999, $order_by, $order_way, false, false);
        } else {
            $products = Db::getInstance()->executeS($sql);
        }

        if (empty($products)) {
            $data['products'] = $products;

            return Tools::jsonEncode($data);
        }

        $data['products'] = $this->getProductsAttributes($id_lang, $products);

        return Tools::jsonEncode($data);
    }

    /**
     * Setup a new context.
     */
    public function setRelatedContext()
    {
        $this->context->currency = new Currency((int) pqnp_config('CURRENCY'));
        $this->context->language = new Language((int) pqnp_config('LANG'));
    }

    /**
     * Get products attributes.
     *
     * @param int   $id_lang
     * @param array $pr
     * @param int   $id_currency
     *
     * @return array
     */
    public function getProductsAttributes($id_lang, $pr, $id_currency = null, $productProperties = false)
    {
        $this->setRelatedContext();
        $prop = $this->getNewProperties(null, $id_currency);

        if ($productProperties) {
            // this instance will create the prestashop cache
            $products = NewsletterProProductProperties::getProductsProperties($id_lang, $pr);
        } else {
            $products = Product::getProductsProperties($id_lang, $pr);
        }

        foreach ($products as &$product) {
            $this->createProductTemplateVars($id_lang, $product, $prop);
        }

        return $products;
    }

    /**
     * Get product attributes.
     *
     * @param int   $id_lang
     * @param array $pr
     *
     * @return array
     */
    public function getProductAttributes($id_lang, $pr)
    {
        $this->setRelatedContext();
        $prop = $this->getNewProperties();
        $product = Product::getProductProperties($id_lang, $pr);

        $this->createProductTemplateVars($id_lang, $product, $prop);

        return $product;
    }

    /**
     * The the products new properties.
     *
     * @param string $image_type
     * @param int    $id_currency
     *
     * @return array
     */
    public function getNewProperties($image_type = null, $id_currency = null)
    {
        $prop = [];

        if (!isset($image_type)) {
            $image_type = (string) pqnp_config('IMAGE_TYPE');
        }

        $image = Image::getSize($image_type);

        if (false == $image) {
            $sql = 'SELECT `id_image_type` as `id`, `name`, `width`, `height` FROM `'._DB_PREFIX_.'image_type`
					WHERE `products` = 1 AND width > 150 ORDER BY `width` ASC LIMIT 1;';
            $image = Db::getInstance()->executeS($sql);
            $image = isset($image[0]) ? $image[0] : null;
        }

        $image_thumb_type = '';
        $type_cart = 'cart';
        $type_small = 'small';
        if ($this->isPS16()) {
            $image_thumb_type = $type_cart.'_default';
        } else {
            $image_thumb_type = (Version::isLower('1.5.1.0') ? 'small' : $type_small.'_default');
        }

        if ($images_types = ImageType::getImagesTypes('products')) {
            $smallest = [];
            foreach ($images_types as $img) {
                if (empty($smallest)) {
                    $smallest = $img;
                } elseif ($img['width'] < $smallest['width']) {
                    $smallest = $img;
                }
            }

            if (isset($smallest['name'])) {
                $image_thumb_type = $smallest['name'];
            }
        }

        $image_thumb = Image::getSize($image_thumb_type);

        if (false == $image_thumb) {
            $sql = 'SELECT `id_image_type` as `id`, `name`, `width`, `height` FROM `'._DB_PREFIX_.'image_type`
					WHERE `products` = 1 AND width > 1 ORDER BY `width` ASC LIMIT 1;';
            $image_thumb = Db::getInstance()->executeS($sql);
            $image_thumb = isset($image_thumb[0]) ? $image_thumb[0] : null;
        }

        if (null == $image || null == $image_thumb) {
            return false;
        }

        $prop['image_type'] = $image_type;
        $prop['image_thumb_type'] = $image_thumb_type;
        $prop['width'] = $image['width'];
        $prop['height'] = $image['height'];
        $prop['thumb_width'] = $image_thumb['width'];
        $prop['thumb_height'] = $image_thumb['height'];
        $prop['current_currency'] = isset($id_currency) ? Currency::getCurrency((int) $id_currency) : Currency::getCurrency(pqnp_config('CURRENCY'));
        $prop['mod_rewrite'] = pqnp_config('PS_REWRITING_SETTINGS');

        return $prop;
    }

    /**
     * Convert the url's to SSL protocol.
     *
     * @param string $url
     *
     * @return string
     */
    public function convertToSSL($url)
    {
        return $url;
    }

    /**
     * Returns a link to a product image for display
     * Note: the new image filesystem stores product images in subdirectories of img/p/.
     *
     * @param string $name rewrite link of the image
     * @param string $ids  id part of the image filename - can be "id_product-id_image" (legacy support, recommended) or "id_image" (new)
     * @param string $type
     */
    public function getImageLink($name, $ids, $type = null)
    {
        $link = $this->context->link->getImageLink($name, $ids, $type);
        $link = $this->relplaceAdminLink($link);
        $link = $this->convertToSSL($link);

        return $link;
    }

    /**
     * Create a link to a product.
     *
     * @param mixed  $product  Product object (can be an ID product, but deprecated)
     * @param string $alias
     * @param string $category
     * @param string $ean13
     * @param int    $id_lang
     * @param int    $id_shop  (since 1.5.0) ID shop need to be used when we generate a product link for a product in a cart
     * @param int    $ipa      ID product attribute
     *
     * @return string
     */
    public function getProductLink($product, $alias = null, $category = null, $ean13 = null, $id_lang = null, $id_shop = null, $ipa = 0, $force_routes = false)
    {
        $link = $this->context->link->getProductLink($product, $alias, $category, $ean13, $id_lang, $id_shop, $ipa, $force_routes);
        $link = $this->relplaceAdminLink($link);

        return $link;
    }

    /**
     * Generate a product link.
     *
     * @param int $id_product
     * @param int $id_lang
     *
     * @return string
     */
    public function makeProductLink($id_product, $id_lang)
    {
        $link = $this->getShopUrl().'index.php?id_product='.$id_product.'&controller=product&id_lang='.$id_lang;
        $link = $this->relplaceAdminLink($link);

        return $link;
    }

    /**
     * Create and sanitize products template variables.
     *
     * @param int   $id_lang
     * @param array $product
     * @param array $prop
     */
    public function createProductTemplateVars($id_lang, &$product, $prop)
    {
        $rewrite = [];
        foreach (Product::getUrlRewriteInformations($product['id_product']) as $item) {
            $rewrite[$item['id_lang']] = $item['link_rewrite'];
        }

        $link_rewrite = $rewrite[$id_lang];
        $image_id = array_key_exists('cover_image_id', $product) ? $product['cover_image_id'] : $product['id_image'];

        $product['image_path'] = $this->getImageLink($link_rewrite, $image_id, $prop['image_type']);
        $product['image_width'] = $prop['width'];
        $product['image_height'] = $prop['height'];

        if ($prop['mod_rewrite'] && (bool) pqnp_config('PRODUCT_LINK_REWRITE')) {
            $product['link'] = $this->getProductLink((int) $product['id_product']);
        } else {
            $product['link'] = $this->makeProductLink((int) $product['id_product'], (int) $id_lang);
        }

        if (pqnp_config('CAMPAIGN_ACTIVE')) {
            $product['link'] = $this->setCampaignVariables($product['link'], ['product_name' => $product['name']]);
        }

        $product['link'] = $this->setStatisticsVariables($product['link']);

        $decimals = 2;

        $product['thumb_path'] = $this->getImageLink($link_rewrite, $image_id, $prop['image_thumb_type']);
        $product['thumb_width'] = $prop['thumb_width'];
        $product['thumb_height'] = $prop['thumb_height'];

        if (array_key_exists('sign', $prop['current_currency'])) {
            $product['currency'] = $prop['current_currency']['sign'];
        } else {
            $product['currency'] = $prop['current_currency']['iso_code'];
        }

        if (isset($product['specific_prices']['reduction_type']) && 'amount' === $product['specific_prices']['reduction_type']) {
            $product['discount'] = number_format(abs((((float) $product['specific_prices']['reduction'] / (float) $product['price_without_reduction']) * 100)), $decimals).'%';
        } elseif (isset($product['specific_prices']['reduction_type'])) {
            $product['discount'] = number_format(abs($product['specific_prices']['reduction'] * 100), $decimals).'%';
        }

        $product['price_convert'] = (
            pqnp_ini_config('price_convert')
                                                            ? Tools::convertPrice((float) $product['price'], (int) $prop['current_currency']['id_currency'])
                                                            : (float) $product['price']
        );

        $product['price_display'] = Tools::displayPrice((float) $product['price_convert'], (int) $prop['current_currency']['id_currency']);

        $product['price_without_reduction'] = $product['price_without_reduction'];
        $product['price_without_reduction_convert'] = (
            pqnp_ini_config('price_convert')
                                                        ? Tools::convertPrice((float) $product['price_without_reduction'], (int) $prop['current_currency']['id_currency'])
                                                        : (float) $product['price_without_reduction']
        );

        $product['price_without_reduction_display'] = Tools::displayPrice((float) $product['price_without_reduction_convert'], (int) $prop['current_currency']['id_currency']);

        $product['price_tax_exc'] = $product['price_tax_exc'];
        $product['price_tax_exc_convert'] = (
            pqnp_ini_config('price_convert')
                                                        ? Tools::convertPrice((float) $product['price_tax_exc'], (int) $prop['current_currency']['id_currency'])
                                                        : (float) $product['price_tax_exc']
        );
        $product['price_tax_exc_display'] = Tools::displayPrice((float) $product['price_tax_exc_convert'], (int) $prop['current_currency']['id_currency']);

        $product['wholesale_price'] = $product['wholesale_price'];
        $product['wholesale_price_convert'] = (
            pqnp_ini_config('price_convert')
                                                        ? Tools::convertPrice((float) $product['wholesale_price'], (int) $prop['current_currency']['id_currency'])
                                                        : (float) $product['wholesale_price']
        );
        $product['wholesale_price_display'] = Tools::displayPrice((float) $product['wholesale_price_convert'], (int) $prop['current_currency']['id_currency']);

        $unit_price = (float) $product['price'];
        $unit_price_tax_exc = (float) $product['price_tax_exc'];
        if ((float) $product['unit_price_ratio'] > 0) {
            $unit_price = (float) $product['price'] / (float) $product['unit_price_ratio'];
            $unit_price_tax_exc = (float) $product['price_tax_exc'] / (float) $product['unit_price_ratio'];
        }

        $product['unit_price'] = number_format($unit_price, $decimals);
        $product['unit_price_convert'] = number_format(
            pqnp_ini_config('price_convert')
                                            ? Tools::convertPrice($unit_price, (int) $prop['current_currency']['id_currency'])
                                            : $unit_price,
            $decimals
        );

        $product['unit_price_display'] = Tools::displayPrice($unit_price, (int) $prop['current_currency']['id_currency']);

        $product['unit_price_tax_exc'] = number_format($unit_price_tax_exc, $decimals);
        $product['unit_price_tax_exc_convert'] = number_format(
            pqnp_ini_config('price_convert')
                                                    ? Tools::convertPrice($unit_price_tax_exc, (int) $prop['current_currency']['id_currency'])
                                                    : $unit_price_tax_exc,
            $decimals
        );
        $product['unit_price_tax_exc_display'] = Tools::displayPrice($unit_price_tax_exc, (int) $prop['current_currency']['id_currency']);

        $product['pre_tax_retail_price'] = (float) $product['price_tax_exc'] + (float) $product['reduction'];

        $unity = (int) $product['unity'] <= 0 ? 1 : (int) $product['unity'];

        $unit_price_bo = (float) $product['pre_tax_retail_price'] / $unity;

        $product['unit_price_bo'] = number_format($unit_price_bo, $decimals);
        $product['unit_price_bo_convert'] = number_format(
            pqnp_ini_config('price_convert')
                                                ? Tools::convertPrice($unit_price_bo, (int) $prop['current_currency']['id_currency'])
                                                : $unit_price_bo,
            $decimals
        );
        $product['unit_price_bo_display'] = Tools::displayPrice($unit_price_bo, (int) $prop['current_currency']['id_currency']);

        // {$price_display|EUR}
        // {$name|12|...|en} // force langauge to english

        // the array will fill later in NewsletterProTemplate.php
        $product['dynamic_vars'] = [];
    }

    /**
     * Get the campaign statistics params as string.
     *
     * @return string
     */
    public function getCampaignParams()
    {
        $params = $this->getCampaignParamsArray();

        $string = '';
        foreach ($params as $key => $line) {
            $string .= '&'.$key.'='.$line;
        }

        $string = str_replace("\n", '', $string);
        $string = preg_replace('/(?<=\w)\s(?=\w)/', '+', $string);
        $string = str_replace(' ', '', $string);

        return $string;
    }

    /**
     * Append google campaign variables to the link.
     *
     * @param string $link
     * @param array  $campaign_vars
     *
     * @return string
     */
    public function setCampaignVariables($link, $campaign_vars)
    {
        $params = $this->getCampaignParams();

        foreach ($campaign_vars as &$var) {
            $var = urlencode($var);
        }

        $link = trim($link);

        $linkParts = explode('#', $link);
        $link = array_shift($linkParts);

        $last_letter = Tools::substr($link, Tools::strlen($link) - 1, Tools::strlen($link));

        if (preg_match('/\?/', $link)) {
            if ('?' === $last_letter) {
                $link = $link.Tools::substr($params, 1);
            } else {
                $link = $link.$params;
            }
        } else {
            $link = $link.'?'.Tools::substr($params, 1);
        }

        $link = $this->replaceVars($link, $campaign_vars);

        $link = preg_replace('/\?\&/', '?', $link);

        if (count($linkParts) > 0) {
            return $link.'#'.join('#', $linkParts);
        }

        return $link;
    }

    /**
     * Set campagin statistics variables.
     *
     * @param string $link
     * @param array  $tpl_vars
     */
    public function setStatisticsVariables($link, $tpl_vars = [])
    {
        $params = $this->getStatisticsParams();

        $link = trim($link);

        $exp = explode('#', $link);

        $tail = '';

        if (count($exp) > 1) {
            $link = $exp[0];
            $tail = '#'.$exp[1];
        }

        $last_letter = Tools::substr($link, Tools::strlen($link) - 1, Tools::strlen($link));

        if (preg_match('/\?/', $link)) {
            if ('?' === $last_letter) {
                $link = $link.Tools::substr($params, 1);
            } else {
                $link = $link.$params;
            }
        } else {
            if ('?' === $last_letter) {
                $link = $link.'?'.Tools::substr($params, 1);
            } else {
                $link = $link.'?'.$params;
            }
        }

        $link = $this->replaceVars($link, $tpl_vars);
        $link = preg_replace('/\?\&/', '?', $link);

        return $link.$tail;
    }

    /**
     * Get the campaign statistics variables as string.
     *
     * @return string
     */
    public function getStatisticsParams()
    {
        $params = [
            'newsletterpro_source' => 'newsletter',
            'id_newsletter' => '{id_newsletter_pro_tpl_history}',
        ];

        $string = '';
        foreach ($params as $key => $line) {
            $string .= '&'.$key.'='.$line;
        }

        $string = str_replace("\n", '', $string);
        $string = preg_replace('/(?<=\w)\s(?=\w)/', '+', $string);
        $string = str_replace(' ', '', $string);

        return $string;
    }

    /**
     * Replace the newsletter template variables.
     *
     * @param string $template
     * @param array  $variables
     *
     * @return string
     */
    public function replaceVars($template, $variables)
    {
        self::$replace_vars = $variables;
        $template = preg_replace_callback(
            '/{(?P<tag>[^}]+)}/',
            [$this, 'replaceCallback'],
            $template
        );

        return $template;
    }

    /**
     * Replace the newsletter template variables callback.
     *
     * @param array $matches
     *
     * @return string
     */
    public function replaceCallback($matches)
    {
        $tag = $matches[1]; // tag

        return isset(self::$replace_vars[$tag]) ? str_replace(' ', '+', trim(self::$replace_vars[$tag])) : '{'.$tag.'}';
    }

    /**
     * Search product by name, reference, category.
     *
     * @param string $query
     * @param int    $id_lang
     * @param int    $id_currency
     *
     * @return json
     */
    public function searchProducts($query, $id_lang = null, $id_currency = null)
    {
        $data = [];
        $id_category = (int) Tools::getValue('get_products');

        if (!isset($id_lang)) {
            $id_lang = (int) pqnp_config('LANG');
        }

        $order_by_prefix = null;
        $front = false;
        $order_by = '`name`';
        $order_way = 'ASC';
        $limit = 1000;
        $id_supplier = false;

        $start = 0;
        $only_active = pqnp_config('ONLY_ACTIVE_PRODUCTS');

        $query_new_products = $this->l('new products');
        $query_price_drop = $this->l('price drop');
        $datetime = date('Y-m-d H:i:s');
        $sql = '';

        if (Version::isBetween('1.5.0.0', '1.5.0.6')) {
            $sql = 'SELECT p.*, pl.* , t.`rate` AS tax_rate, m.`name` AS manufacturer_name, s.`name` AS supplier_name,
				i.`id_image`, il.`legend`, cl.`name` AS category_default
				FROM `'._DB_PREFIX_.'product` p
				'.$this->context->shop->addSqlAssociation('product', 'p').'
				LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` '.$this->context->shop->addSqlRestrictionOnLang('pl').')
				LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr ON (p.`id_tax_rules_group` = tr.`id_tax_rules_group`
					AND tr.`id_country` = '.(int) $this->context->country->id.'
					AND tr.`id_state` = 0)
				LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.`id_tax` = tr.`id_tax`)
				LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product`)  AND i.cover = 1
				LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON ( i.`id_image` = il.`id_image`) AND il.`id_lang` = '.(int) $id_lang.'
				LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (m.`id_manufacturer` = p.`id_manufacturer`)
				LEFT JOIN `'._DB_PREFIX_.'supplier` s ON (s.`id_supplier` = p.`id_supplier`)

				LEFT JOIN `'._DB_PREFIX_.'category_lang` cl
					ON( cl.`id_lang` = pl.`id_lang` AND p.`id_category_default` = cl.`id_category` ) '.
                ($id_category ? 'LEFT JOIN `'._DB_PREFIX_.'category_product` c ON (c.`id_product` = p.`id_product`)' : '');

            if (preg_match('/^(\s+)?'.$query_price_drop.'(\s+)?$/i', $query, $price_drop)) {
                $sql .= ' INNER JOIN `'._DB_PREFIX_.'specific_price` sp	ON (
					sp.`id_product` = p.`id_product`
					AND ((sp.`from` <= "'.$datetime.'" AND sp.`to` >= "'.$datetime.'") OR sp.`to` = "0000-00-00 00:00:00")
				) ';
            }

            $sql .= 'WHERE pl.`id_lang` = '.(int) $id_lang.
                    ($id_category ? ' AND c.`id_category` = '.(int) $id_category : '').
                    ($only_active ? ' AND p.`active` = 1' : '');

            if (preg_match('/^(\s+)?'.$query_new_products.'(\s+)?$/i', $query) && empty($price_drop)) {
                $order_by = 'p.`date_add`';
                $order_way = 'DESC';
                $sql .= ' AND ( DATEDIFF(
						p.`date_add`,
						DATE_SUB(
							NOW(),
							INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY
						)
					) > 0 ) = 1 ';
            } elseif (empty($price_drop)) {
                $sql .= ' AND ( pl.`name` LIKE "%'.pSQL($query).'%" OR cl.`name` LIKE "%'.pSQL($query).'%" OR p.`reference` LIKE "%'.pSQL($query).'%" )';
            }

            $sql .= ' GROUP BY p.`id_product`
					ORDER BY '.(isset($order_by_prefix) ? pSQL($order_by_prefix).'.' : '').''.pSQL($order_by).' '.pSQL($order_way).
                    ($limit > 0 ? ' LIMIT '.(int) $start.','.(int) $limit : '');
        } elseif (Version::isBetween('1.5.0.4', '1.5.0.18')) {
            $sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, product_attribute_shop.`id_product_attribute`,
					pl.`description`, pl.`description_short`, pl.`available_now`,
					pl.`available_later`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, image_shop.`id_image`,
					il.`legend`, m.`name` AS manufacturer_name, tl.`name` AS tax_name, t.`rate`, cl.`name` AS category_default,
					DATEDIFF(product_shop.`date_add`, DATE_SUB(NOW(),
					INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).'
						DAY)) > 0 AS new,
					(product_shop.`price` * IF(t.`rate`,((100 + (t.`rate`))/100),1)) AS orderprice
					FROM `'._DB_PREFIX_.'category_product` cp
					LEFT JOIN `'._DB_PREFIX_.'product` p
						ON p.`id_product` = cp.`id_product`
					'.Shop::addSqlAssociation('product', 'p').'
					LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa
					ON (p.`id_product` = pa.`id_product`)
					'.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.`default_on` = 1').'
					'.Product::sqlStock('p', 'product_attribute_shop', false, $this->context->shop).'
					LEFT JOIN `'._DB_PREFIX_.'category_lang` cl
						ON (product_shop.`id_category_default` = cl.`id_category`
						AND cl.`id_lang` = '.(int) $id_lang.Shop::addSqlRestrictionOnLang('cl').'  )
					LEFT JOIN `'._DB_PREFIX_.'product_lang` pl
						ON (p.`id_product` = pl.`id_product`
						AND pl.`id_lang` = '.(int) $id_lang.Shop::addSqlRestrictionOnLang('pl').'  )
					LEFT JOIN `'._DB_PREFIX_.'image` i
						ON (i.`id_product` = p.`id_product`)'.
                        Shop::addSqlAssociation('image', 'i', false, 'i.cover=1').'
					LEFT JOIN `'._DB_PREFIX_.'image_lang` il
						ON (image_shop.`id_image` = il.`id_image`
						AND il.`id_lang` = '.(int) $id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr
						ON (product_shop.`id_tax_rules_group` = tr.`id_tax_rules_group`
						AND tr.`id_country` = '.(int) $this->context->country->id.'
						AND tr.`id_state` = 0
						AND tr.`zipcode_from` = 0)
					LEFT JOIN `'._DB_PREFIX_.'tax` t
						ON (t.`id_tax` = tr.`id_tax`)
					LEFT JOIN `'._DB_PREFIX_.'tax_lang` tl
						ON (t.`id_tax` = tl.`id_tax`
						AND tl.`id_lang` = '.(int) $id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'manufacturer` m
						ON m.`id_manufacturer` = p.`id_manufacturer`';

            if (preg_match('/^(\s+)?'.$query_price_drop.'(\s+)?$/i', $query, $price_drop)) {
                $sql .= ' INNER JOIN `'._DB_PREFIX_.'specific_price` sp	ON (
					sp.`id_product` = p.`id_product`
					AND ((sp.`from` <= "'.pSQL($datetime).'" AND sp.`to` >= "'.pSQL($datetime).'") OR sp.`to` = "0000-00-00 00:00:00")
				) ';
            }

            $sql .= 'WHERE product_shop.`id_shop` = '.(int) $this->context->shop->id.'
					AND (pa.id_product_attribute IS NULL OR product_attribute_shop.id_shop='.(int) $this->context->shop->id.')
					AND (i.id_image IS NULL OR image_shop.id_shop='.(int) $this->context->shop->id.')'
                        .($only_active ? ' AND product_shop.`active` = 1' : '')
                        .($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '')
                        .($id_supplier ? ' AND p.id_supplier = '.(int) $id_supplier : '');

            if (preg_match('/^(\s+)?'.$query_new_products.'(\s+)?$/i', $query) && empty($price_drop)) {
                $order_by = 'product_shop.`date_add`';
                $order_way = 'DESC';
                $sql .= ' AND ( DATEDIFF(
						product_shop.`date_add`,
						DATE_SUB(
							NOW(),
							INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY
						)
					) > 0 ) = 1 ';
            } elseif (empty($price_drop)) {
                $sql .= ' AND ( pl.`name` LIKE "%'.pSQL($query).'%" OR cl.`name` LIKE "%'.pSQL($query).'%" OR p.`reference` LIKE "%'.pSQL($query).'%" )';
            }

            $sql .= ' GROUP BY p.`id_product`
					ORDER BY '.pSQL($order_by).' '.pSQL($order_way).' LIMIT 0,'.(int) $limit.';';
        } else {
            $sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, product_attribute_shop.`id_product_attribute`,
					pl.`description`, pl.`description_short`, pl.`available_now`,
					pl.`available_later`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, image_shop.`id_image`,
					il.`legend`, m.`name` AS manufacturer_name, tl.`name` AS tax_name, t.`rate`, cl.`name` AS category_default,
					DATEDIFF(product_shop.`date_add`, DATE_SUB(NOW(),
					INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).'
						DAY)) > 0 AS new,
					(product_shop.`price` * IF(t.`rate`,((100 + (t.`rate`))/100),1)) AS orderprice
					FROM `'._DB_PREFIX_.'category_product` cp
					LEFT JOIN `'._DB_PREFIX_.'product` p
						ON p.`id_product` = cp.`id_product`
						'.Shop::addSqlAssociation('product', 'p').'
					LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa
					ON (p.`id_product` = pa.`id_product`)
						'.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.`default_on` = 1').'
						'.Product::sqlStock('p', 'product_attribute_shop', false, $this->context->shop).'
					LEFT JOIN `'._DB_PREFIX_.'category_lang` cl
						ON (product_shop.`id_category_default` = cl.`id_category`
						AND cl.`id_lang` = '.(int) $id_lang.Shop::addSqlRestrictionOnLang('cl').'  )
					LEFT JOIN `'._DB_PREFIX_.'product_lang` pl
						ON (p.`id_product` = pl.`id_product`
						AND pl.`id_lang` = '.(int) $id_lang.Shop::addSqlRestrictionOnLang('pl').'  )
					LEFT JOIN `'._DB_PREFIX_.'image` i
						ON (i.`id_product` = p.`id_product`)'.
                        Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').'
					LEFT JOIN `'._DB_PREFIX_.'image_lang` il
						ON (image_shop.`id_image` = il.`id_image`
						AND il.`id_lang` = '.(int) $id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr
						ON (product_shop.`id_tax_rules_group` = tr.`id_tax_rules_group`
						AND tr.`id_country` = '.(int) $this->context->country->id.'
						AND tr.`id_state` = 0
						AND tr.`zipcode_from` = 0)
					LEFT JOIN `'._DB_PREFIX_.'tax` t
						ON (t.`id_tax` = tr.`id_tax`)
					LEFT JOIN `'._DB_PREFIX_.'tax_lang` tl
						ON (t.`id_tax` = tl.`id_tax`
						AND tl.`id_lang` = '.(int) $id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'manufacturer` m
						ON m.`id_manufacturer` = p.`id_manufacturer`';

            if (preg_match('/^(\s+)?'.$query_price_drop.'(\s+)?$/i', $query, $price_drop)) {
                $sql .= ' INNER JOIN `'._DB_PREFIX_.'specific_price` sp	ON (
					sp.`id_product` = p.`id_product`
					AND ((sp.`from` <= "'.pSQL($datetime).'" AND sp.`to` >= "'.pSQL($datetime).'") OR sp.`to` = "0000-00-00 00:00:00")
				) ';
            }

            $sql .= ' WHERE product_shop.`id_shop` = '.(int) $this->context->shop->id.'
					AND (pa.id_product_attribute IS NULL OR product_attribute_shop.id_shop='.(int) $this->context->shop->id.')
					AND (i.id_image IS NULL OR image_shop.id_shop='.(int) $this->context->shop->id.')'
                        .($only_active ? ' AND product_shop.`active` = 1' : '')
                        .($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '')
                        .($id_supplier ? ' AND p.id_supplier = '.(int) $id_supplier : '');

            if (preg_match('/^(\s+)?'.$query_new_products.'(\s+)?$/i', $query) && empty($price_drop)) {
                $order_by = 'product_shop.`date_add`';
                $order_way = 'DESC';
                $sql .= ' AND ( DATEDIFF(
						product_shop.`date_add`,
						DATE_SUB(
							NOW(),
							INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY
						)
					) > 0 ) = 1 ';
            } elseif (empty($price_drop)) {
                $sql .= ' AND ( pl.`name` LIKE "%'.pSQL($query).'%" OR cl.`name` LIKE "%'.pSQL($query).'%" OR p.`reference` LIKE "%'.pSQL($query).'%" )';
            }

            $sql .= ' GROUP BY p.`id_product`
					ORDER BY '.pSQL($order_by).' '.pSQL($order_way).' LIMIT 0,'.(int) $limit.';';
        }

        if ((int) pqnp_ini_config('get_category_products_prestashop')) {
            $products = Product::searchByName($id_lang, $query);
        } else {
            $products = Db::getInstance()->executeS($sql);
        }

        if (empty($products)) {
            $data['products'] = $products;

            return Tools::jsonEncode($data);
        }

        $data['products'] = $this->getProductsAttributes($id_lang, $products, $id_currency);

        return Tools::jsonEncode($data);
    }

    /**
     * Sort products id's.
     *
     * @param array $products
     * @param int   $ids
     *
     * @return array
     */
    private function sortProductsIds($products, $ids)
    {
        $products_sorted = [];

        foreach ($products as $product) {
            $id_product = $product['id_product'];

            if (($search = array_search($id_product, $ids)) !== false) {
                $products_sorted[$search] = $product;
            }
        }

        ksort($products_sorted);

        return array_values($products_sorted);
    }

    /**
     * Get product image.
     *
     * @param int    $id
     * @param string $image_type
     *
     * @return json
     */
    public function getImageOfProduct($id, $image_type)
    {
        $errors = [];
        $product = [];
        $response = ['status' => false, 'errors' => &$errors, 'product' => []];

        if (!$id) {
            $response['status'] = true;

            return Tools::jsonEncode($response);
        }

        if ($image_size = Image::getSize($image_type)) {
            $id_lang = (int) pqnp_config('LANG');
            $prod = new Product($id, false, $id_lang);
            $id_image = (int) $prod->getCoverWs();

            $src = $this->getImageLink($prod->link_rewrite, $id_image, $image_type);

            $product['id_product'] = (int) $prod->id;
            $product['src'] = $src;
            $product['width'] = $image_size['width'];
            $product['height'] = $image_size['height'];
        } else {
            $errors[] = $this->l('Invalid image type!');
        }

        if (!$product) {
            $errors[] = $this->l('Invalid product!');
        }

        if (empty($errors)) {
            $response['status'] = true;
            $response['product'] = $product;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Get products images.
     *
     * @param int    $ids
     * @param string $image_type
     *
     * @return json
     */
    public function getImagesOfProducts($ids, $image_type)
    {
        $errors = [];
        $products = [];
        $response = ['status' => false, 'errors' => &$errors, 'products' => $products];

        if (!$ids) {
            $response['status'] = true;

            return Tools::jsonEncode($response);
        }

        if ($image_size = Image::getSize($image_type)) {
            $sql = $this->getProductsByIdSql($ids);
            $prod = Db::getInstance()->executeS($sql);

            foreach ($prod as $product) {
                $id_product = (int) $product['id_product'];
                $products[$id_product]['id_product'] = $id_product;
                $products[$id_product]['src'] = $this->getImageLink($product['link_rewrite'], $product['id_image'], $image_type);
                $products[$id_product]['width'] = $image_size['width'];
                $products[$id_product]['height'] = $image_size['height'];
            }
        } else {
            $errors[] = $this->l('Invalid image type!');
        }

        if (empty($errors)) {
            $response['status'] = true;
            $response['products'] = $products;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Get products sql.
     *
     * @param array $ids
     * @param int   $id_lang
     *
     * @return string
     */
    private function getProductsByIdSql($ids, $id_lang = null)
    {
        if (!$ids) {
            return '';
        }

        $ids_implode = trim(implode(',', $ids), ',');

        $id_category = (int) Tools::getValue('get_products');
        if (!isset($id_lang)) {
            $id_lang = (int) pqnp_config('LANG');
        }

        $order_by_prefix = null;
        $front = false;
        $order_by = '`name`';
        $order_way = 'ASC';
        $limit = 1000;
        $id_supplier = false;

        $start = 0;
        $only_active = pqnp_config('ONLY_ACTIVE_PRODUCTS');

        $sql = '';

        if (Version::isBetween('1.5.0.0', '1.5.0.6')) {
            $sql = 'SELECT p.*, pl.* , t.`rate` AS tax_rate, m.`name` AS manufacturer_name, s.`name` AS supplier_name,
				i.`id_image`, il.`legend`,cl.`name` AS category_default
				FROM `'._DB_PREFIX_.'product` p
				'.$this->context->shop->addSqlAssociation('product', 'p').'
				LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` '.$this->context->shop->addSqlRestrictionOnLang('pl').')
				LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr ON (p.`id_tax_rules_group` = tr.`id_tax_rules_group`
					AND tr.`id_country` = '.(int) $this->context->country->id.'
					AND tr.`id_state` = 0)
				LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.`id_tax` = tr.`id_tax`)
				LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product`)  AND i.cover = 1
				LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON ( i.`id_image` = il.`id_image`) AND il.`id_lang` = '.(int) $id_lang.'
				LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (m.`id_manufacturer` = p.`id_manufacturer`)
				LEFT JOIN `'._DB_PREFIX_.'supplier` s ON (s.`id_supplier` = p.`id_supplier`)

				LEFT JOIN `'._DB_PREFIX_.'category_lang` cl
					ON( cl.`id_lang` = pl.`id_lang` AND p.`id_category_default` = cl.`id_category` ) '.
                ($id_category ? 'LEFT JOIN `'._DB_PREFIX_.'category_product` c ON (c.`id_product` = p.`id_product`)' : '');

            $sql .= 'WHERE pl.`id_lang` = '.(int) $id_lang.
                    ($id_category ? ' AND c.`id_category` = '.(int) $id_category : '').
                    ($only_active ? ' AND p.`active` = 1' : '');

            $sql .= ' AND (p.`id_product` IN ('.$ids_implode.')) ';

            $sql .= ' GROUP BY p.`id_product`
					ORDER BY '.(isset($order_by_prefix) ? pSQL($order_by_prefix).'.' : '').''.pSQL($order_by).' '.pSQL($order_way).
                    ($limit > 0 ? ' LIMIT '.(int) $start.','.(int) $limit : '');
        } elseif (Version::isBetween('1.5.0.4', '1.5.0.18')) {
            $sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, product_attribute_shop.`id_product_attribute`,
					pl.`description`, pl.`description_short`, pl.`available_now`,
					pl.`available_later`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, image_shop.`id_image`,
					il.`legend`, m.`name` AS manufacturer_name, tl.`name` AS tax_name, t.`rate`, cl.`name` AS category_default,
					DATEDIFF(product_shop.`date_add`, DATE_SUB(NOW(),
					INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).'
						DAY)) > 0 AS new,
					(product_shop.`price` * IF(t.`rate`,((100 + (t.`rate`))/100),1)) AS orderprice
					FROM `'._DB_PREFIX_.'category_product` cp
					LEFT JOIN `'._DB_PREFIX_.'product` p
						ON p.`id_product` = cp.`id_product`
					'.Shop::addSqlAssociation('product', 'p').'
					LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa
					ON (p.`id_product` = pa.`id_product`)
					'.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.`default_on` = 1').'
					'.Product::sqlStock('p', 'product_attribute_shop', false, $this->context->shop).'
					LEFT JOIN `'._DB_PREFIX_.'category_lang` cl
						ON (product_shop.`id_category_default` = cl.`id_category`
						AND cl.`id_lang` = '.(int) $id_lang.Shop::addSqlRestrictionOnLang('cl').'  )
					LEFT JOIN `'._DB_PREFIX_.'product_lang` pl
						ON (p.`id_product` = pl.`id_product`
						AND pl.`id_lang` = '.(int) $id_lang.Shop::addSqlRestrictionOnLang('pl').'  )
					LEFT JOIN `'._DB_PREFIX_.'image` i
						ON (i.`id_product` = p.`id_product`)'.
                        Shop::addSqlAssociation('image', 'i', false, 'i.cover=1').'
					LEFT JOIN `'._DB_PREFIX_.'image_lang` il
						ON (image_shop.`id_image` = il.`id_image`
						AND il.`id_lang` = '.(int) $id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr
						ON (product_shop.`id_tax_rules_group` = tr.`id_tax_rules_group`
						AND tr.`id_country` = '.(int) $this->context->country->id.'
						AND tr.`id_state` = 0
						AND tr.`zipcode_from` = 0)
					LEFT JOIN `'._DB_PREFIX_.'tax` t
						ON (t.`id_tax` = tr.`id_tax`)
					LEFT JOIN `'._DB_PREFIX_.'tax_lang` tl
						ON (t.`id_tax` = tl.`id_tax`
						AND tl.`id_lang` = '.(int) $id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'manufacturer` m
						ON m.`id_manufacturer` = p.`id_manufacturer`';

            $sql .= 'WHERE product_shop.`id_shop` = '.(int) $this->context->shop->id.'
					AND (pa.id_product_attribute IS NULL OR product_attribute_shop.id_shop='.(int) $this->context->shop->id.')
					AND (i.id_image IS NULL OR image_shop.id_shop='.(int) $this->context->shop->id.')'
                        .($only_active ? ' AND product_shop.`active` = 1' : '')
                        .($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '')
                        .($id_supplier ? ' AND p.id_supplier = '.(int) $id_supplier : '');

            $sql .= ' AND (p.`id_product` IN ('.$ids_implode.')) ';

            $sql .= ' GROUP BY p.`id_product`
					ORDER BY '.pSQL($order_by).' '.pSQL($order_way).' LIMIT 0,'.(int) $limit.';';
        } else {
            $sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, product_attribute_shop.`id_product_attribute`,
					pl.`description`, pl.`description_short`, pl.`available_now`,
					pl.`available_later`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, image_shop.`id_image`,
					il.`legend`, m.`name` AS manufacturer_name, tl.`name` AS tax_name, t.`rate`, cl.`name` AS category_default,
					DATEDIFF(product_shop.`date_add`, DATE_SUB(NOW(),
					INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).'
						DAY)) > 0 AS new,
					(product_shop.`price` * IF(t.`rate`,((100 + (t.`rate`))/100),1)) AS orderprice
					FROM `'._DB_PREFIX_.'category_product` cp
					LEFT JOIN `'._DB_PREFIX_.'product` p
						ON p.`id_product` = cp.`id_product`
						'.Shop::addSqlAssociation('product', 'p').'
					LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa
					ON (p.`id_product` = pa.`id_product`)
						'.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.`default_on` = 1').'
						'.Product::sqlStock('p', 'product_attribute_shop', false, $this->context->shop).'
					LEFT JOIN `'._DB_PREFIX_.'category_lang` cl
						ON (product_shop.`id_category_default` = cl.`id_category`
						AND cl.`id_lang` = '.(int) $id_lang.Shop::addSqlRestrictionOnLang('cl').'  )
					LEFT JOIN `'._DB_PREFIX_.'product_lang` pl
						ON (p.`id_product` = pl.`id_product`
						AND pl.`id_lang` = '.(int) $id_lang.Shop::addSqlRestrictionOnLang('pl').'  )
					LEFT JOIN `'._DB_PREFIX_.'image` i
						ON (i.`id_product` = p.`id_product`)'.
                        Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').'
					LEFT JOIN `'._DB_PREFIX_.'image_lang` il
						ON (image_shop.`id_image` = il.`id_image`
						AND il.`id_lang` = '.(int) $id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'tax_rule` tr
						ON (product_shop.`id_tax_rules_group` = tr.`id_tax_rules_group`
						AND tr.`id_country` = '.(int) $this->context->country->id.'
						AND tr.`id_state` = 0
						AND tr.`zipcode_from` = 0)
					LEFT JOIN `'._DB_PREFIX_.'tax` t
						ON (t.`id_tax` = tr.`id_tax`)
					LEFT JOIN `'._DB_PREFIX_.'tax_lang` tl
						ON (t.`id_tax` = tl.`id_tax`
						AND tl.`id_lang` = '.(int) $id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'manufacturer` m
						ON m.`id_manufacturer` = p.`id_manufacturer`';

            $sql .= ' WHERE product_shop.`id_shop` = '.(int) $this->context->shop->id.'
					AND (pa.id_product_attribute IS NULL OR product_attribute_shop.id_shop='.(int) $this->context->shop->id.')
					AND (i.id_image IS NULL OR image_shop.id_shop='.(int) $this->context->shop->id.')'
                        .($only_active ? ' AND product_shop.`active` = 1' : '')
                        .($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '')
                        .($id_supplier ? ' AND p.id_supplier = '.(int) $id_supplier : '');

            $sql .= ' AND (p.`id_product` IN ('.$ids_implode.')) ';

            $sql .= ' GROUP BY p.`id_product`
					ORDER BY '.pSQL($order_by).' '.pSQL($order_way).' LIMIT 0,'.(int) $limit.';';
        }

        return $sql;
    }

    /**
     * Get products by id's.
     *
     * @param array $ids
     *
     * @return json
     */
    public function getProductsById($ids)
    {
        $data = [];
        $sql = $this->getProductsByIdSql($ids);

        if (!$sql) {
            return Tools::jsonEncode(['products' => []]);
        }

        $products = Db::getInstance()->executeS($sql);
        $products = $this->sortProductsIds($products, $ids);

        if (empty($products)) {
            $data['products'] = $products;

            return Tools::jsonEncode($data);
        }

        $id_lang = (int) pqnp_config('LANG');
        $data['products'] = $this->getProductsAttributes($id_lang, $products);

        return Tools::jsonEncode($data);
    }

    /**
     * Get product by id.
     *
     * @param int $id
     * @param int $id_lang
     *
     * @return bool
     */
    public function getProductById($id, $id_lang = null, $productProperties = false)
    {
        $sql = $this->getProductsByIdSql([$id], $id_lang);

        if (!$sql) {
            return false;
        }

        $products = Db::getInstance()->executeS($sql);

        if (empty($products)) {
            return false;
        }

        if (!isset($id_lang)) {
            $id_lang = (int) pqnp_config('LANG');
        }

        $products = $this->getProductsAttributes($id_lang, $products, null, $productProperties);
        if (isset($products[0])) {
            return $products[0];
        }

        return false;
    }

    /**
     * Get shop url.
     *
     * @param int $id_shop
     *
     * @return string
     */
    public function getShopUrl($id_shop = null)
    {
        if (!isset($id_shop)) {
            $id_shop = (int) $this->context->shop->id;
        }

        $base_uri = __PS_BASE_URI__;
        $default_shop_url = Tools::getHttpHost(true).$base_uri;

        $shop_url = '';

        if ((bool) Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE') && ($shop = Shop::getShop((int) $id_shop))) {
            $shop_url = $this->context->shop->getBaseURL();
        } else {
            $shop_url = $default_shop_url;
        }

        $len = Tools::strlen($shop_url);

        if (isset($shop_url[$len - 1])) {
            $char = $shop_url[$len - 1];
            if ('/' !== $char) {
                $shop_url .= '/';
            }
        }

        $shop_url = $this->relplaceAdminLink($shop_url);

        return $shop_url;
    }

    /**
     * Prepare emails for sending newsletters.
     *
     * @return json
     */
    public function prepareEmails($content = null, $emails_json = null)
    {
        $emails = Tools::jsonEncode([]);
        $post = &$_POST;

        if (isset($post['prepareEmails'])) {
            $emails = $post['prepareEmails'];
        }

        NewsletterProLog::clearSend();

        NewsletterProSendConnection::clearAll();

        $emails = Tools::jsonDecode($emails);

        if (isset($emails_json)) {
            $emails = array_merge($emails, Tools::jsonDecode($emails_json));
        }

        $response = NewsletterProAjaxResponse::newInstance();

        try {
            $prepare = NewsletterProSendManager::newInstance()->prepare;

            if (isset($content) && $content) {
                $template = NewsletterProTemplate::newString(['', $content])->load();
                $prepare->setTemplate($template);
            } else {
                $template = NewsletterProTemplate::newFile(pqnp_config('NEWSLETTER_TEMPLATE'))->load();
                $prepare->setTemplate($template);
            }

            $prepare->setEmails($emails)->add();
            $response->set('count_emails', count($prepare->emails));
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    /**
     * Unserialize a string.
     *
     * @param string $serialized
     *
     * @return array
     */
    public static function unSerialize($serialized)
    {
        if (is_string($serialized) && preg_match('/a:[0-9]+:\{.*\}/', $serialized)) {
            return @unserialize($serialized);
        }

        return [];
    }

    /**
     * Select all customers.
     *
     * @return json
     */
    public function selectAllCustomers()
    {
        $sql_shops_id = '';
        $get_active_shops_id = NewsletterProTools::getActiveShopsId();

        foreach ($get_active_shops_id as $key => $id_shop) {
            $sql_shops_id .= 'c.`id_shop` = '.(int) $id_shop.(end($get_active_shops_id) == $id_shop ? '' : ' OR ');
        }

        $sql = 'SELECT c.`id_customer` AS `id`, c.`email`
				FROM `'._DB_PREFIX_.'customer` c
				WHERE( '.$sql_shops_id.' )
				ORDER BY c.`id_customer` DESC;';

        $userlist = Db::getInstance()->executeS($sql);

        foreach (array_keys($userlist) as $key) {
            $userlist[$key]['user_type'] = 'customer';
        }

        return Tools::jsonEncode($userlist);
    }

    public function leftMenuActive($val)
    {
        $response = NewsletterProAjaxResponse::newInstance();

        if (!pqnp_config('LEFT_MENU_ACTIVE', $val)) {
            $response->addError($this->l('The menu layout has not been changed in database.'));
        }

        return $response->display();
    }

    /**
     * Change settings view active only.
     *
     * @param bool $val
     *
     * @return json
     */
    public function viewActiveOnly($val)
    {
        if (pqnp_config('VIEW_ACTIVE_ONLY', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings convert css to inline style.
     *
     * @param bool $val
     *
     * @return json
     */
    public function convertCssToInlineStyle($val)
    {
        if (pqnp_config('CONVERT_CSS_TO_INLINE_STYLE', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings rum multiple tasks.
     *
     * @param bool $val
     *
     * @return json
     */
    public function runMultimpleTasks($val)
    {
        if (pqnp_config('RUN_MULTIPLE_TASKS', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings.
     *
     * @param bool $val
     *
     * @return json
     */
    public function displayCustomerAccountSettings($val)
    {
        if (pqnp_config('DISPLYA_MY_ACCOUNT_NP_SETTINGS', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings subscribe by category.
     *
     * @param bool $val
     *
     * @return json
     */
    public function subscribeByCategory($val)
    {
        if (pqnp_config('SUBSCRIBE_BY_CATEGORY', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings subscribe by list of interests.
     *
     * @param bool $val
     *
     * @return json
     */
    public function subscribeByCListOfInterest($val)
    {
        if (pqnp_config('CUSTOMER_SUBSCRIBE_BY_LOI', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings subscribe by list of interests.
     *
     * @param bool $val
     *
     * @return json
     */
    public function subscribeByCAListOfInterest($val)
    {
        if (pqnp_config('CUSTOMER_ACCOUNT_SUBSCRIBE_BY_LOI', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * @param bool $val
     *
     * @return json
     */
    public function devMode($val)
    {
        if (pqnp_config('DEV_MODE', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings send newsletter on subscribe.
     *
     * @param bool $val
     *
     * @return json
     */
    public function sendNewsletterOnSubscribe($val)
    {
        if (pqnp_config('SEND_NEWSLETTER_ON_SUBSCRIBE', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings forward feature active.
     *
     * @param bool $val
     *
     * @return json
     */
    public function forwardingFeatureActive($val)
    {
        if (pqnp_config('FWD_FEATURE_ACTIVE', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings forward feature active.
     *
     * @param bool $val
     *
     * @return json
     */
    public function sendEmbededImagesActive($val)
    {
        $response = NewsletterProAjaxResponse::newInstance([]);

        if (ini_get('allow_url_fopen')) {
            if (!pqnp_config('SEND_EMBEDED_IMAGES', $val)) {
                $response->addError($this->l('An error occurred.'));
            }
        } else {
            pqnp_config('SEND_EMBEDED_IMAGES', 0);
            $response->addError($this->l('You can embed files from a URL if allow_url_fopen is on in php.ini.'));
        }

        return $response->display();
    }

    /**
     * Change settings forward feature active.
     *
     * @param bool $val
     *
     * @return json
     */
    public function sendEmailMimeText($val)
    {
        if (pqnp_config('EMAIL_MIME_TEXT', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings view active products.
     *
     * @param bool $val
     *
     * @return json
     */
    public function chimpSyncUnsubscribed($val)
    {
        if (pqnp_config('CHIMP_SYNC_UNSUBSCRIBED', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings view active products.
     *
     * @param bool $val
     *
     * @return json
     */
    public function displayOnliActiveProducts($val)
    {
        if (pqnp_config('ONLY_ACTIVE_PRODUCTS', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings product link rewrite.
     *
     * @param bool $val
     *
     * @return json
     */
    public function productFriendlyURL($val)
    {
        if (pqnp_config('PRODUCT_LINK_REWRITE', $val)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings debug mode.
     *
     * @param bool $val
     *
     * @return json
     */
    public function debugMode($bool)
    {
        if (pqnp_config('DEBUG_MODE', $bool)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    public function loadMinified($bool)
    {
        if (pqnp_config('LOAD_MINIFIED', (bool) $bool)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Change settings subscription secure subscribe.
     *
     * @param bool $val
     *
     * @return json
     */
    public function subscriptionSecureSubscribe($bool)
    {
        if (pqnp_config('SUBSCRIPTION_SECURE_SUBSCRIBE', $bool)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    public function subscriptionControllerEnabled($bool)
    {
        if (pqnp_config('SUBSCRIPTION_CONTROLLER_ENABLED', $bool)) {
            return Tools::jsonEncode(['status' => true]);
        } else {
            return Tools::jsonEncode(['status' => false]);
        }
    }

    /**
     * Clear the secure subscribed temporary email addresses.
     *
     * @param bool $val
     *
     * @return json
     */
    public function clearSubscribersTemp()
    {
        $errors = [];
        $response = ['status' => false, 'errors' => &$errors, 'msg' => ''];

        $sql = 'DELETE FROM `'._DB_PREFIX_.'newsletter_pro_subscribers_temp` WHERE `date_add` < "'.date('Y-m-d H:i:s', strtotime('-1 weeks')).'"';

        if (!Db::getInstance()->execute($sql)) {
            $errors[] = $this->l('The emails has not been cleared.');
        }

        if (empty($errors)) {
            $response['msg'] = $this->l('The emails has been cleared successfully.');
            $response['status'] = true;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Imprt email addresses from the blcok newsletter module.
     *
     * @return json
     */
    public function importEmailsFromBlockNewsletter($newsletter_date_add = null)
    {
        $id_default_shop = (int) pqnp_config('PS_SHOP_DEFAULT');

        $errors = [];
        $response = ['status' => false, 'errors' => &$errors, 'msg' => ''];

        $bn_info = $this->getBlockNewsletterInfo();

        $cout_success = 0;
        $cout_errors = 0;

        if ($bn_info['isInstalled']) {
            $emails_to_import = Db::getInstance()->executeS('
				SELECT * FROM `'._DB_PREFIX_.NewsletterProDefaultNewsletterTable::getTableName().'`
				'.(isset($newsletter_date_add) ? ' WHERE newsletter_date_add >= "'.pSQL($newsletter_date_add).'"' : '').'
			');

            if ($emails_to_import) {
                foreach ($emails_to_import as $row) {
                    try {
                        $id_shop = isset($row['id_shop']) ? $row['id_shop'] : $id_default_shop;
                        $id_shop_group = isset($row['id_shop_group']) ? $row['id_shop_group'] : $id_default_shop;
                        $email = $row['email'];
                        $newsletter_date_add = isset($row['newsletter_date_add']) ? $row['newsletter_date_add'] : date('Y-m-d H:i:s');
                        $ip_registration_newsletter = isset($row['ip_registration_newsletter']) ? $row['ip_registration_newsletter'] : '';
                        $active = isset($row['active']) ? $row['active'] : 1;

                        $id = NewsletterProSubscribers::getIdByEmail($email);
                        // check if the email not exists
                        if (!$id) {
                            $subscriber = new NewsletterProSubscribers();
                            $subscriber->id_shop = (int) $id_shop;
                            $subscriber->id_shop_group = (int) $id_shop_group;
                            $subscriber->email = $email;
                            $subscriber->newsletter_date_add = $newsletter_date_add;
                            $subscriber->ip_registration_newsletter = $ip_registration_newsletter;
                            $subscriber->active = (int) $active;

                            if ($subscriber->add()) {
                                ++$cout_success;
                            } else {
                                ++$cout_errors;
                            }
                        }
                    } catch (Exception $e) {
                        ++$cout_errors;
                    }
                }
            }
        }

        if (empty($errors)) {
            if ($cout_success > 0 && 0 == $cout_errors) {
                $response['msg'] = sprintf($this->l('(%s) emails has been imported successfully.'), $cout_success);
            } elseif ($cout_success > 0 && $cout_errors > 0) {
                $response['msg'] = sprintf($this->l('(%s) emails has been imported successfully and at (%s) emails an error occurred.'), $cout_success, $cout_errors);
            } elseif (0 == $cout_success && $cout_errors > 0) {
                $response['msg'] = sprintf($this->l('An error occurred at (%s) emails.'), $cout_errors);
            } elseif (!empty($emails_to_import)) {
                $response['msg'] = $this->l('All the emails are imported.');
            } else {
                $response['msg'] = $this->l('There are no emails to import.');
            }

            $response['status'] = true;
        }

        return Tools::jsonEncode($response);
    }

    public function importEmailsFromBlockNewsletterCron($newsletter_date_add)
    {
        return $this->importEmailsFromBlockNewsletter($newsletter_date_add);
    }

    /**
     * Get newsletter pro subscription info.
     *
     * @return array
     */
    public function getNewsletterProSubscriptionInfo()
    {
        $hooks = [];
        $popupTypesHooks = pqnp_config('SUBSCRIPTION_HOOK_POPUP_TYPE');

        foreach (NewsletterProSubscriptionHook::$hooks as $key => $hook) {
            $id_hook = Hook::getIdByName($hook);

            $hooks[$key]['id_hook'] = $id_hook;
            $hooks[$key]['name'] = $hook;
            $hooks[$key]['isRegistred'] = false;
            $hooks[$key]['position'] = 0;
            $hooks[$key]['popup_type'] = 0;

            $upperHook = Tools::strtoupper($hook);
            if (array_key_exists($upperHook, $popupTypesHooks)) {
                $hooks[$key]['popup_type'] = (int) $popupTypesHooks[$upperHook];
            }

            if ($this->isRegisteredInHook($hook) && $this->isHookableOn($hook)) {
                $hooks[$key]['isRegistred'] = true;
                $hooks[$key]['position'] = $this->getModulePosition($this, $id_hook);
            }
        }

        return [
            'name' => $this->name,
            'isInstalled' => true,
            'isEnabled' => true,
            'hooks' => $hooks,
        ];
    }

    /**
     * Get newsletter pro info.
     *
     * @return array
     */
    public function getNewsletterProInfo()
    {
        $hooks = [];

        foreach ($this->getHooksList() as $key => $hook) {
            $id_hook = Hook::getIdByName($hook);

            $hooks[$key]['id_hook'] = $id_hook;
            $hooks[$key]['name'] = $hook;
            $hooks[$key]['isRegistred'] = false;
            $hooks[$key]['position'] = 0;

            if ($this->isRegisteredInHook($hook) && $this->isHookableOn($hook)) {
                $hooks[$key]['isRegistred'] = true;
                $hooks[$key]['position'] = $this->getModulePosition($this, $id_hook);
            }
        }

        return [
            'name' => $this->name,
            'isInstalled' => true,
            'isEnabled' => true,
            'hooks' => $hooks,
        ];
    }

    /**
     * Get module position.
     *
     * @param object $obj
     * @param int    $id_hook
     *
     * @return int
     */
    public function getModulePosition($obj, $id_hook)
    {
        if (method_exists($obj, 'getPosition')) {
            return $obj->getPosition($id_hook);
        } else {
            if (isset(Hook::$preloadModulesFromHooks)) {
                if (isset(Hook::$preloadModulesFromHooks[$id_hook])) {
                    if (isset(Hook::$preloadModulesFromHooks[$id_hook]['module_position'][$obj->id])) {
                        return Hook::$preloadModulesFromHooks[$id_hook]['module_position'][$obj->id];
                    } else {
                        return 0;
                    }
                }
            }

            $result = Db::getInstance()->getRow('
				SELECT `position`
				FROM `'._DB_PREFIX_.'hook_module`
				WHERE `id_hook` = '.(int) $id_hook.'
				AND `id_module` = '.(int) $obj->id.'
				AND `id_shop` = '.(int) Context::getContext()->shop->id);

            return (int) $result['position'];
        }
    }

    /**
     * Get block newsletter info.
     *
     * @return array
     */
    public function getBlockNewsletterInfo()
    {
        $hooks = [];
        $modules = ['ps_emailsubscription', 'blocknewsletter'];
        $name = 'ps_emailsubscription';

        foreach ($modules as $moduleName) {
            if (Module::isInstalled($moduleName)) {
                $name = $moduleName;
                break;
            }
        }

        $is_installed = Module::isInstalled($name);

        if ($is_installed) {
            $blocknewsletter = Module::getInstanceByName($name);

            foreach (NewsletterProSubscriptionHook::$hooks as $key => $hook) {
                $id_hook = Hook::getIdByName($hook);

                $hooks[$key]['id_hook'] = $id_hook;
                $hooks[$key]['name'] = $hook;
                $hooks[$key]['isRegistred'] = false;
                $hooks[$key]['position'] = 0;

                if ($blocknewsletter && $blocknewsletter->isRegisteredInHook($hook) && $blocknewsletter->isHookableOn($hook)) {
                    $hooks[$key]['isRegistred'] = true;
                    $hooks[$key]['position'] = $this->getModulePosition($blocknewsletter, $id_hook);
                }
            }
        }

        return [
            'name' => $name,
            'isInstalled' => $is_installed,
            'isEnabled' => NewsletterPro::isModuleEnabled($name),
            'hooks' => $hooks,
        ];
    }

    /**
     * Check if the module is enabled.
     *
     * @param string $name
     *
     * @return bool
     */
    public static function isModuleEnabled($name)
    {
        if (method_exists('Module', 'isEnabled')) {
            return Module::isEnabled($name);
        } else {
            if (!Cache::isStored('Module::isEnabled'.$name)) {
                $active = false;
                $id_module = Db::getInstance()->getValue('SELECT `id_module` FROM `'._DB_PREFIX_.'module` WHERE `name` = \''.pSQL($name).'\'');
                if (Db::getInstance()->getValue('SELECT `id_module` FROM `'._DB_PREFIX_.'module_shop` WHERE `id_module` = '.(int) $id_module.' AND `id_shop` = '.(int) Context::getContext()->shop->id)) {
                    $active = true;
                }
                Cache::store('Module::isEnabled'.$name, (bool) $active);
            }

            return Cache::retrieve('Module::isEnabled'.$name);
        }
    }

    /**
     * Get module google analytics info.
     *
     * @return array
     */
    public function getGAnalyticsModuleInfo()
    {
        $hooks = [];
        $name = 'ganalytics';
        $is_installed = Module::isInstalled($name);

        if ($is_installed) {
            $module = Module::getInstanceByName($name);

            foreach (['displayHeader', 'displayFooter', 'displayOrderConfirmation'] as $key => $hook) {
                $id_hook = Hook::getIdByName($hook);

                $hooks[$key]['id_hook'] = $id_hook;
                $hooks[$key]['name'] = $hook;
                $hooks[$key]['isRegistred'] = false;
                $hooks[$key]['position'] = 0;

                if ($module->isRegisteredInHook($hook) && $module->isHookableOn($hook)) {
                    $hooks[$key]['isRegistred'] = true;
                    $hooks[$key]['position'] = $this->getModulePosition($module, $id_hook);
                }
            }
        }

        return [
            'name' => $name,
            'isInstalled' => $is_installed,
            'isEnabled' => NewsletterPro::isModuleEnabled($name),
            'hooks' => $hooks,
        ];
    }

    /**
     * Get block newsletter registred hooks.
     *
     * @return array
     */
    public function getBlockNewsletterHooksRegistered()
    {
        $hooks = [];
        $bn_info = $this->getBlockNewsletterInfo();

        foreach ($bn_info['hooks'] as $hook) {
            if ($hook['isRegistred']) {
                $hooks[] = $hook;
            }
        }

        return $hooks;
    }

    /**
     * Get newsletter pro subscription hooks.
     *
     * @return array
     */
    public function getNewsletterProSubscriptionHooks()
    {
        $np_hooks = $this->getNewsletterProSubscriptionInfo();

        return $np_hooks['hooks'];
    }

    private function buildRegisteredHooks($hooks, $bn_hooks = [])
    {
        $register_hooks = [];
        $start_hooks = $hooks;

        foreach ($bn_hooks as $bn_hook) {
            foreach ($start_hooks as $key => $hook) {
                if ($bn_hook['isRegistred'] && $bn_hook['name'] == $hook) {
                    $register_hooks[] = [
                                            'id_hook' => $bn_hook['id_hook'],
                                            'name' => $hook,
                                            'hasPosition' => true,
                                            'position' => $bn_hook['position'],
                                        ];

                    unset($start_hooks[$key]);
                }
            }
        }

        foreach ($start_hooks as $hook) {
            $register_hooks[] = [
                                    'id_hook' => Hook::getIdByName($hook),
                                    'name' => $hook,
                                    'hasPosition' => false,
                                    'position' => 0,
                                ];
        }

        return $register_hooks;
    }

    /**
     * Build unregistred hooks.
     *
     * @param array $hooks
     * @param array $bn_hooks
     *
     * @return array
     */
    public function buildUnregistredHooks($hooks, $bn_hooks = [])
    {
        $bn_hooks_names = [];
        foreach ($bn_hooks as $hook) {
            $bn_hooks_names[] = $hook['name'];
        }

        return array_diff($bn_hooks_names, $hooks);
    }

    /**
     * Check if the module is registred in a hook.
     *
     * @param string $hook
     *
     * @return bool
     */
    public function isRegisteredInHook($hook)
    {
        if ('header' == $hook) {
            $hook_name = Db::getInstance()->getValue('
				SELECT `name` FROM `'._DB_PREFIX_.'hook_alias` WHERE `alias` = "'.pSQL($hook).'"
			');

            if ($hook_name) {
                $hook = $hook_name;
            }
        }

        return parent::isRegisteredInHook($hook);
    }

    /**
     * Enable the newsletter pro subscription
     * The module will be registered in the hook displayTop.
     *
     * @param array $hooks Regsitrer module to the hooks, leave it empty if you want to copy the hooks from the blocknewsletter module
     *
     * @return null
     */
    public function updateNewsletterProSubscription($hooks = [])
    {
        $removeHooks = array_diff(NewsletterProSubscriptionHook::$hooks, $hooks);

        foreach (['displayTop', 'displayHeader'] as $hookName) {
            if (!$this->isRegisteredInHook($hookName)) {
                $this->registerHook($hookName);
            }
        }

        $bn_info = $this->getBlockNewsletterInfo();
        $np_info = $this->getNewsletterProSubscriptionInfo();

        if ($bn_info['isInstalled']) {
            $blocknewsletter = Module::getInstanceByName($bn_info['name']);

            if ($bn_info['isEnabled']) {
                $blocknewsletter->disable();
            }

            $register_hooks = $this->buildRegisteredHooks($hooks, $bn_info['hooks']);

            if (!empty($register_hooks)) {
                foreach ($register_hooks as $hook) {
                    $this->registerHook($hook['name']);

                    if ($hook['hasPosition']) {
                        $this->updatePosition($hook['id_hook'], 0, $hook['position']);
                    }
                }
            } else {
                // copy the hooks from the blocknewsletter module
                foreach ($bn_info['hooks'] as $hook) {
                    if ($hook['isRegistred']) {
                        $this->registerHook($hook['name']);
                        $this->updatePosition($hook['id_hook'], 0, $hook['position']);
                    }
                }
            }

            $unregister_hooks = $this->buildUnregistredHooks($hooks, $np_info['hooks']);

            if (!empty($unregister_hooks)) {
                foreach ($unregister_hooks as $hook_name) {
                    $this->unregisterHook($hook_name);
                }
            }

            foreach ($removeHooks as $removeHook) {
                if (!$this->isRegisteredInHook($removeHook)) {
                    $this->unregisterHook($removeHook);
                }
            }
        } else {
            if (!empty($hooks)) {
                foreach ($hooks as $hook) {
                    $this->registerHook($hook);
                }
            }
        }
    }

    /**
     * Register/Unregister hooks by hooks info.
     *
     * @param array $hooks_info
     *
     * @return bool
     */
    public static function executeHooksByInfo($hooks_info)
    {
        $module = NewsletterPro::getInstance();
        $success = [];

        foreach ($hooks_info as $hook) {
            if ($hook['isRegistred']) {
                $success[] = $module->registerHook($hook['name']);
                $module->updatePosition($hook['id_hook'], 0, ++$hook['position']);
            } else {
                $success[] = $module->unregisterHook($hook['name']);
            }
        }

        return true;
    }

    /**
     * Disable newsletter pro subscription option.
     */
    public function disableNewsletterProSubscription()
    {
        $bn_info = $this->getBlockNewsletterInfo();
        $np_info = $this->getNewsletterProSubscriptionInfo();

        if ($bn_info['isInstalled']) {
            $blocknewsletter = Module::getInstanceByName($bn_info['name']);

            if (!$bn_info['isEnabled']) {
                $blocknewsletter->enable();
            }

            foreach ($bn_info['hooks'] as $hook) {
                if ($hook['isRegistred']) {
                    $blocknewsletter->registerHook($hook['name']);
                    $blocknewsletter->updatePosition($hook['id_hook'], 0, $hook['position']);
                }
            }
        }

        foreach ($np_info['hooks'] as $hook) {
            if ($hook['isRegistred']) {
                $this->unregisterHook($hook['id_hook']);
            }
        }
    }

    /**
     * Setup the newsletter pro subscription.
     *
     * @param bool  $bool  Enable/Disable the newsletter pro subscription
     * @param array $hooks Regsitrer module to the hooks, leave it empty if you want to copy the hooks from the blocknewsletter module
     *
     * @return null
     */
    public function newsletterproSubscriptionActive($bool, $hooks = [])
    {
        $popupTypes = $this->request->get('popupTypes', []);

        pqnp_config('SUBSCRIPTION_HOOK_POPUP_TYPE', NewsletterProSubscriptionHook::convertToUpper($popupTypes));
        pqnp_config('SUBSCRIPTION_ACTIVE', (int) $bool);

        if ($bool) {
            $this->updateNewsletterProSubscription($hooks);
        } else {
            $this->disableNewsletterProSubscription();
        }

        return $this->response->json();
    }

    public function changeSubscriptionControllerTemplate()
    {
        $templateId = (int) $this->request->get('templateId', -1);

        if (0 == $templateId) {
            pqnp_config('SUBSCRIPTION_CONTROLLER_TEMPLATE_ID', $templateId);

            return $this->response->json();
        }

        $subscriptionTpl = new NewsletterProSubscriptionTpl($templateId);

        if (!Validate::isLoadedObject($subscriptionTpl)) {
            $this->response->error($this->l('Invalid template id.'));

            return $this->response->json();
        }

        pqnp_config('SUBSCRIPTION_CONTROLLER_TEMPLATE_ID', $templateId);

        return $this->response->json();
    }

    public function getSubscriptionControllerTemplates()
    {
        $templates = NewsletterProSubscriptionTpl::getControllerTemplates();

        return $this->response->setData([
            'templates' => $templates,
        ])->json();
    }

    /**
     * Change newsletter template.
     *
     * @param string $template
     *
     * @return json
     */
    public function changeNewsletterTemplate($template)
    {
        $path = $this->tpl_location.'newsletter/'.$template;

        if (preg_match('/^.*.html$/', $template) && file_exists($path)) {
            pqnp_config('NEWSLETTER_TEMPLATE', $template);

            return Tools::jsonEncode(['status' => true, 'msg' => '']);
        } else {
            return Tools::jsonEncode(['status' => false, 'msg' => $this->l('Invalid template')]);
        }
    }

    /**
     * Change product template.
     *
     * @param string $template
     *
     * @return json
     */
    public function changeProductTemplate($template)
    {
        $path = $this->tpl_location.'product/'.$template;

        if (preg_match('/^.*.html$/', $template) && file_exists($path)) {
            pqnp_config('PRODUCT_TEMPLATE', $template);

            return Tools::jsonEncode(['status' => true, 'msg' => '']);
        } else {
            return Tools::jsonEncode(['status' => false, 'msg' => $this->l('Invalid template')]);
        }
    }

    /**
     * Change produt image size.
     *
     * @param string $image_size
     *
     * @return json
     */
    public function changeProductImageSize($image_size)
    {
        $errors = [];

        if (false != Image::getSize($image_size)) {
            pqnp_config('IMAGE_TYPE', trim($image_size));
        } else {
            $errors[] = $this->l('Invalid image type!');
        }

        return Tools::jsonEncode($errors);
    }

    /**
     * Change product currency.
     *
     * @param int $get_currency
     *
     * @return json
     */
    public function changeProductCurrency($get_currency)
    {
        $errors = [];

        $currencies = Currency::getCurrencies();
        $currency_exists = false;
        foreach ($currencies as $currency) {
            if ((int) $currency['id_currency'] == (int) $get_currency) {
                $currency_exists = true;
                break;
            }
        }

        $get_currency = (true == $currency_exists) ? (int) $get_currency : (int) pqnp_config('PS_CURRENCY_DEFAULT');

        if (!pqnp_config('CURRENCY', $get_currency)) {
            $errors[] = $this->l('Currency not changed');
        }

        return Tools::jsonEncode($errors);
    }

    /**
     * Change product language.
     *
     * @param int $id
     *
     * @return json
     */
    public function changeProductLanguage($id)
    {
        $errors = [];
        $languages = Language::getLanguages(true, $this->context->shop->id);

        $id_lang_exists = false;
        foreach ($languages as $language) {
            if ((int) $language['id_lang'] == (int) $id) {
                $id_lang_exists = true;
                break;
            }
        }
        $id = (true == $id_lang_exists) ? (int) $id : (int) pqnp_config('PS_LANG_DEFAULT');

        if (!pqnp_config('LANG', $id)) {
            $errors[] = $this->l('Language not changed');
        }

        return Tools::jsonEncode($errors);
    }

    /**
     * Get product template content.
     *
     * @param array $data
     * @param bool  $readcontent
     *
     * @return json
     */
    public function getProductTemplateContent($data, $readcontent)
    {
        $errors = [];
        $content = '';
        $columns = 0;
        $render = '';
        $response = [
            'status' => false,
            'errors' => &$errors,
            'content' => &$content,
            'render' => &$render,
            'columns' => &$columns,
        ];
        $name = $data['filename'];
        $path = $data['path'];

        if (file_exists($path)) {
            if ((Tools::file_get_contents($path)) !== false) {
                if (!pqnp_config('PRODUCT_TEMPLATE', $name)) {
                    $errors[] = $this->l('Error on creating the configuration!');
                } else {
                    if (pqnp_config('DEBUG_MODE')) {
                        $content = $this->getProductContent();
                    } else {
                        $content = @$this->getProductContent();
                    }

                    $columns = $this->getProductColumns();

                    if ($readcontent) {
                        try {
                            if (pqnp_config('DEBUG_MODE')) {
                                $render = $this->getProductContent(true);
                            } else {
                                $render = @$this->getProductContent(true);
                            }
                        } catch (Exception $e) {
                            $errors[] = $e->getMessage();
                        }
                    }
                }
            } else {
                $errors[] = $this->l('The file cannot be read, check the CHMOD !');
            }
        } else {
            $errors[] = $this->l('File not exists!');
        }

        if (empty($errors)) {
            $response['status'] = true;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Get product content.
     *
     * @param bool $view
     *
     * @return string
     */
    public function getProductContent($view = false)
    {
        $template = pqnp_config('PRODUCT_TEMPLATE');
        $path = $this->tpl_location.'product/'.$template;
        if (file_exists($path)) {
            $content = Tools::file_get_contents($path);

            if (false == $view) {
                return $content;
            }

            // remove comments
            // $content = preg_replace('/<!--[\s\S]*?-->/', '', $content);

            // remove columns for the old templates
            // $content = preg_replace('/\{columns=\d+\}/', '', $content);

            $image_type = (string) pqnp_config('IMAGE_TYPE');
            $img_name = (string) $this->context->language->iso_code.'-default-'.$image_type.'.jpg';
            $image_path = _PS_PROD_IMG_DIR_.$img_name;

            if (file_exists($image_path)) {
                $image_path = Tools::getHttpHost(true)._THEME_PROD_DIR_.$img_name;
            } elseif (file_exists(_PS_PROD_IMG_DIR_.(string) $this->context->language->iso_code.'.jpg')) {
                $image_path = Tools::getHttpHost(true)._THEME_PROD_DIR_.(string) $this->context->language->iso_code.'.jpg';
            } else {
                $files = scandir(_PS_PROD_IMG_DIR_);
                $files = preg_grep('/^('.$this->context->language->iso_code.').*'.$image_type.'.*.jpg$/', $files);

                $img_name = array_values($files);
                $image_path = isset($img_name[0]) ? $img_name[0] : '';
                $image_path = Tools::getHttpHost(true)._THEME_PROD_DIR_.$image_path;
            }

            $size = Image::getSize($image_type);

            $header_content = '';
            if (preg_match('/<!-- start header -->\s*?<!--(?P<header>[\s\S]*)-->\s*?<!-- end header -->/', $content, $match)) {
                $header_content = trim($match['header']);
            }

            if (!preg_match('/content\s+=\s+template/', $header_content)) {
                $content = str_replace(['{image_path}', '{image_width}', '{image_height}'], [$image_path, $size['width'], $size['height']], $content);
            }

            return $content;
        }

        return false;
    }

    /**
     * Get product columns.
     *
     * @return int
     */
    public function getProductColumns()
    {
        $template = pqnp_config('PRODUCT_TEMPLATE');
        $path = $this->tpl_location.'product/'.$template;
        if (file_exists($path)) {
            $content = Tools::file_get_contents($path);

            if (preg_match('/\{columns=(?P<number>\d+)\}/', $content, $match)) {
                return (int) $match['number'];
            }
        }

        return 0;
    }

    /**
     * Delete images.
     *
     * @param array $data
     *
     * @return json
     */
    public function deleteImage($data)
    {
        $errors = [];
        $response = ['status' => 0, 'errors' => &$errors];
        $name = $data['filename'];
        $path = $data['path'];
        $thumb_filename = $data['thumb_filename'];
        $thumb_path = $data['thumb_path'];

        if (file_exists($path)) {
            if (!unlink($path)) {
                $errors[] = sprintf($this->l('You cannot delete the image "%s", please check the CHMOD !'), $name);
            }
        } else {
            $errors[] = sprintf($this->l('The image thumbnail "%s" file not exists!'), $name);
        }

        if (file_exists($thumb_path)) {
            if (!unlink($thumb_path)) {
                $errors[] = sprintf($this->l('You cannot delete the image thumbnail "%s", please check the CHMOD !'), $thumb_filename);
            }
        } else {
            $errors[] = sprintf($this->l('The image thumbnail "%s" file not exists!'), $thumb_filename);
        }

        if (empty($errors)) {
            $response['status'] = 1;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Get send history.
     *
     * @return json
     */
    public function getSendHistory()
    {
        $sql = 'SELECT  s.`id_newsletter_pro_send`,
						s.`id_newsletter_pro_tpl_history`,
						s.`active`,
						s.`emails_count`,
						s.`emails_success`,
						s.`emails_error`,
						s.`emails_completed`,
						s.`error_msg`,
						s.`date`,
						s.`template`,
						ss.`id_newsletter_pro_send_step`,
						h.`clicks`,
						h.`opened`,
						h.`unsubscribed`,
						h.`fwd_unsubscribed`,

				GROUP_CONCAT(ss.`id_newsletter_pro_send_step`) AS `steps`
				FROM `'._DB_PREFIX_.'newsletter_pro_send` s
					LEFT JOIN `'._DB_PREFIX_.'newsletter_pro_send_step` ss
						ON (ss.`id_newsletter_pro_send` = s.`id_newsletter_pro_send`)

					LEFT JOIN `'._DB_PREFIX_.'newsletter_pro_tpl_history` h
						ON (s.`id_newsletter_pro_tpl_history` = h.`id_newsletter_pro_tpl_history`)

				WHERE s.`active` = 0
				GROUP BY s.`id_newsletter_pro_send`
				ORDER BY s.`date` DESC;';

        if ($histories = Db::getInstance()->executeS($sql)) {
            foreach ($histories as &$history) {
                $error_msg_db = Db::getInstance()->executeS('
					SELECT `error_msg` 
					FROM `'._DB_PREFIX_.'newsletter_pro_send_step`
					WHERE `id_newsletter_pro_send` = '.(int) $history['id_newsletter_pro_send'].'
				');

                $error_msg = [];

                foreach ($error_msg_db as $value) {
                    $value['error_msg'] = trim($value['error_msg']);

                    if (!empty($value['error_msg'])) {
                        $em = unserialize($value['error_msg']);
                        foreach ($em as $val) {
                            foreach (array_keys($val) as $error) {
                                $error_msg[] = $error;
                            }
                        }
                    }
                }

                $history['date'] = date('Y-m-d', strtotime($history['date']));
                $history['error_msg'] = array_unique($error_msg);
                $history['template'] = Tools::ucfirst(pathinfo($history['template'], PATHINFO_FILENAME));
            }

            return Tools::jsonEncode($histories);
        } else {
            return Tools::jsonEncode([]);
        }
    }

    /**
     * Get forwarders list.
     *
     * @return json
     */
    public function getForwardList()
    {
        $sql = 'SELECT `id_newsletter_pro_forward`, `from`, `to`, `date_add`, COUNT(*) as `count`
				FROM `'._DB_PREFIX_.'newsletter_pro_forward`
				GROUP BY `from`';

        if ($result = Db::getInstance()->executeS($sql)) {
            return Tools::jsonEncode($result);
        } else {
            return Tools::jsonEncode([]);
        }
    }

    /**
     * Search forwarder.
     *
     * @param string $value
     *
     * @return json
     */
    public function searchForwarder($value)
    {
        $sql = 'SELECT `id_newsletter_pro_forward`, `from`, `to`, `date_add`, COUNT(*) as `count`
				FROM `'._DB_PREFIX_.'newsletter_pro_forward`
				WHERE `from` LIKE "%'.pSQL($value).'%" OR `to` LIKE "%'.pSQL($value).'%"
				GROUP BY `from`';

        if ($result = Db::getInstance()->executeS($sql)) {
            return Tools::jsonEncode($result);
        } else {
            return Tools::jsonEncode([]);
        }
    }

    /**
     * Sleep newsletter.
     *
     * @param int $seconds
     *
     * @return json
     */
    public function sleepNewsletter($seconds)
    {
        if (pqnp_config('SLEEP', $seconds)) {
            return Tools::jsonEncode(['status' => true, 'msg' => $this->l('Updated')]);
        } else {
            return Tools::jsonEncode(['status' => false, 'msg' => $this->l('Update error')]);
        }
    }

    /**
     * Verify template filename.
     *
     * @param string $name
     *
     * @return bool
     */
    public function verifyName($name)
    {
        // frech characters àâçéèêëîïôûùüÿñæœ
        // turkey characters İıÖöÜüÇçĞğŞş₤
        if ('' == $name) {
            return $this->l('Please insert the name');
        } elseif (Tools::strlen($name) < 4) {
            return $this->l('Name must contain at least 4 characters');
        } elseif (!preg_match('/^[a-zA-Z0-9\-_\.\%àâçéèêëîïôûùüÿñæœčšđžćČŠĐĆŽİıÖöÜüÇçĞğŞş₤]+$/i', $name)) {
            return $this->l('Name contain illegal characters');
        } elseif (preg_match('/\.html$/', $name)) {
            return $this->l('Do not use .html extension');
        }

        return true;
    }

    /**
     * Show newsletter help template.
     *
     * @return string
     */
    public function showNewsletterHelp()
    {
        include_once dirname(__FILE__).'/classes/NewsletterProExtendTemplateVars.php';

        $external_vars = NewsletterProExtendTemplateVars::$external_vars;
        $help_vars = [];
        foreach ($external_vars as $path => $to_load) {
            if ($to_load) {
                $path = dirname(__FILE__).'/'.$path;
                $variable_name = pathinfo($path, PATHINFO_FILENAME);

                $help = pqnp_template_path($this->dir_location.'views/templates/admin/variables_help/'.$variable_name.'.tpl');

                if (file_exists($help) && is_file($help)) {
                    $help_vars[] = $this->context->smarty->fetch($help);
                }
            }
        }
        $this->context->smarty->assign('help_vars', $help_vars);

        return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/help_newsletter.tpl'));
    }

    /**
     * Show product help template.
     *
     * @return string
     */
    public function showProductHelp()
    {
        $languages = Language::getLanguages(false);
        $languages_iso = [];

        foreach ($languages as $language) {
            $languages_iso[$language['id_lang']] = $language['iso_code'];
        }

        $currencies = NewsletterPro::getCurrenciesByIdShop($this->context->shop->id);

        $currencies_iso = [];

        foreach ($currencies as $currency) {
            $currencies_iso[$currency['id_currency']] = $currency['iso_code'];
        }

        $this->context->smarty->assign([
            'lang_iso' => trim(implode(', ', $languages_iso)),
            'currencies_iso' => trim(implode(', ', $currencies_iso)),
        ]);

        return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/help_product.tpl'));
    }

    /**
     * Get currencies by is shop.
     *
     * @param int $id_shop
     *
     * @return array
     */
    public static function getCurrenciesByIdShop($id_shop = 0)
    {
        // if there is a problem with the currencies, uncomment this line
        // return Currency::getCurrencies(false, true);

        $context = Context::getContext();

        if (method_exists('Currency', 'getCurrenciesByIdShop')) {
            return Currency::getCurrenciesByIdShop($context->shop->id);
        } else {
            return Db::getInstance()->executeS('
				SELECT *
				FROM `'._DB_PREFIX_.'currency` c
				LEFT JOIN `'._DB_PREFIX_.'currency_shop` cs ON (cs.`id_currency` = c.`id_currency`)
				'.($id_shop ? ' WHERE cs.`id_shop` = '.(int) $id_shop : '').'
				ORDER BY `name` ASC
			');
        }
    }

    /**
     * Search emails.
     *
     * @return json
     */
    public function searchEmails()
    {
        $query = Tools::getValue('search_emails');

        $sql = 'SELECT c.`id_customer`, c.`firstname`, c.`lastname`, c.`email`, gl.`name` AS `group_name`, l.`name` AS `lang_name`, s.`name` AS `shop_name`
				FROM `'._DB_PREFIX_.'customer` c
				LEFT JOIN `'._DB_PREFIX_.'group_lang` gl ON ( gl.`id_group` = c.`id_default_group` ) AND gl.`id_lang` = '.(int) $this->context->language->id.'
				LEFT JOIN `'._DB_PREFIX_.'lang` l ON ( l.`id_lang` = c.`id_lang` )
				LEFT JOIN `'._DB_PREFIX_.'shop` s ON ( s.`id_shop` = c.`id_shop` )
				WHERE c.`firstname` LIKE "%'.pSQL($query).'%"
				OR c.`lastname` LIKE "%'.pSQL($query).'%" OR c.`email` LIKE "%'.pSQL($query).'%"
				OR gl.`name` LIKE "%'.pSQL($query).'%"  OR l.`name` LIKE "%'.pSQL($query).'%"  OR s.`name` LIKE "%'.pSQL($query).'%"';

        $result = Db::getInstance()->executeS($sql);
        $costomers_id = [];

        if (!empty($result)) {
            foreach ($result as $customer) {
                $costomers_id[] = $customer['id_customer'];
            }
        }

        return Tools::jsonEncode($costomers_id);
    }

    /**
     * Display produt image.
     *
     * @param bool $boolean
     *
     * @return json
     */
    public function displayProductImage($boolean)
    {
        $data = [];
        $boolean = ('true' == $boolean) ? 1 : 0;
        if (pqnp_config('DISPLAY_PRODUCT_IMAGE', $boolean)) {
            $data['status'] = true;
            $data['msg'] = '';
        } else {
            $data['status'] = false;
            $data['msg'] = $this->l('The field cannot be updated');
        }

        return Tools::jsonEncode($data);
    }

    /**
     * Clear prestashop cache.
     *
     * @return bool
     */
    public function clearCache()
    {
        if (method_exists('Tools', 'clearSmartyCache')) {
            if (Version::isLower('1.6')) {
                if (method_exists('Tools', 'clearSmartyCache')) {
                    Tools::clearSmartyCache();
                }

                if (method_exists('Media', 'clearCache')) {
                    Media::clearCache();
                }

                $autoload = Autoload::getInstance();
                if (method_exists($autoload, 'generateIndex')) {
                    $autoload->generateIndex();
                }
            } else {
                if (method_exists('Tools', 'clearSmartyCache')) {
                    Tools::clearSmartyCache();
                }

                if (method_exists('Tools', 'clearXMLCache')) {
                    Tools::clearXMLCache();
                }

                if (method_exists('Media', 'clearCache')) {
                    Media::clearCache();
                }

                if (method_exists('Tools', 'generateIndex')) {
                    Tools::generateIndex();
                }
            }

            return true;
        }

        return false;
    }

    /**
     * Clear newsletter history.
     *
     * @return json
     */
    public function clearHistory()
    {
        $response = ['status' => false];

        $sql = 'SELECT `id_newsletter_pro_tpl_history` AS `id`
				FROM `'._DB_PREFIX_.'newsletter_pro_send`
				WHERE `id_newsletter_pro_tpl_history` > 0;';

        if ($ids = Db::getInstance()->executeS($sql)) {
            foreach ($ids as $id) {
                Db::getInstance()->delete('newsletter_pro_tpl_history', '`id_newsletter_pro_tpl_history`='.(int) $id['id']);
                Db::getInstance()->delete('newsletter_pro_unsibscribed', '`id_newsletter_pro_tpl_history`='.(int) $id['id']);
            }
        }

        if (Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'newsletter_pro_send` WHERE 1')) {
            $response['status'] = true;
        } else {
            $response['status'] = false;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Save smtp configuration.
     *
     * @param array $data
     *
     * @return json
     */
    public function saveSMTP($data)
    {
        $response = ['status' => false, 'errors' => []];

        if ((int) pqnp_ini_config('demo_mode')) {
            $name = Tools::strtolower(trim($data['name']));
            $name_demo = Tools::strtolower(pqnp_demo_mode('demo_freeze_smtp_name'));

            if ($name == $name_demo) {
                $response['status'] = false;
                $response['errors'][] = $this->l('This is a demo, you cannot override this SMTP connection.');

                return Tools::jsonEncode($response);
            }
        }

        if (!trim($data['name'])) {
            $this->_errors[] = $this->l('The SMTP "Name" field is required.');
        }

        if (!trim($data['from_email'])) {
            $this->_errors[] = $this->l('The "From email" field is required.');
        } else {
            $sql = 'SELECT `name` FROM `'._DB_PREFIX_.'newsletter_pro_smtp` WHERE `id_newsletter_pro_smtp`='.(int) $data['id_newsletter_pro_smtp'].'';
            $name = Db::getInstance()->getValue($sql);

            if ($name !== $data['name']) {
                $sql = 'SELECT count(*) FROM `'._DB_PREFIX_.'newsletter_pro_smtp` WHERE `name` = "'.pSQL($data['name']).'" AND `id_newsletter_pro_smtp`!='.(int) $data['id_newsletter_pro_smtp'].'';

                if (Db::getInstance()->getValue($sql)) {
                    $this->_errors[] = $this->l('Duplicate SMTP name.');
                }
            }
        }

        if (empty($this->_errors)) {
            $mail = new NewsletterProMail((int) $data['id_newsletter_pro_smtp']);
            $mail->name = $data['name'];
            $mail->method = $data['method'];
            $mail->from_name = $data['from_name'];
            $mail->from_email = $data['from_email'];
            $mail->reply_to = $data['reply_to'];
            $mail->domain = $data['domain'];
            $mail->server = $data['server'];
            $mail->user = $data['user'];
            $mail->passwd = ('' == trim($data['passwd']) ? $mail->passwd : $data['passwd']);
            $mail->encryption = Tools::strtolower($data['encryption']);
            $mail->port = $data['port'];
            $mail->list_unsubscribe_active = (int) $data['list_unsubscribe_active'];
            $mail->list_unsubscribe_email = trim($data['list_unsubscribe_email']);

            if (Validate::isLoadedObject($mail) && $mail->update() && pqnp_config('SMTP', $mail->id)) {
                $response['obj'] = $data;
                $response['status'] = true;
            } else {
                $response['status'] = false;
            }
        } else {
            $response['errors'] = $this->_errors;
            $response['status'] = false;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Delete smtp configuration.
     *
     * @param int $id
     *
     * @return json
     */
    public function deleteSMTP($id)
    {
        $response = NewsletterProAjaxResponse::newInstance([
            'status' => false,
            'msg' => '',
            'count' => 0,
        ]);

        try {
            $sql = 'SELECT count(*)
					FROM `'._DB_PREFIX_.'newsletter_pro_task`
					WHERE `id_newsletter_pro_smtp`='.(int) $id.'
					AND `done` = 0;';

            if ($count = Db::getInstance()->getValue($sql)) {
                $response->addError($this->l('The SMTP cannot be deleted because is used in').' '.$count.' '.$this->l('tasks. Change the tasks SMTP or delete them.'));
                $response->set('count', $count);
            } else {
                $mail = NewsletterProMail::newInstance((int) $id);

                if (!Validate::isLoadedObject($mail)) {
                    return $response->addError(sprintf($this->l('Invalid connection id "%s"'), $id))->display();
                }

                if ((int) pqnp_ini_config('demo_mode')) {
                    $demo_return = NewsletterProDemoMode::deleteSMTP($mail->name);

                    if ($demo_return) {
                        return $demo_return;
                    }
                }

                $error_msg = $this->l('An error occurred when deleting the connection.');

                if (!$mail->delete()) {
                    $response->addError($error_msg);
                } else {
                    if (!NewsletterProSendConnection::deleteBySmtpId((int) $id)) {
                        $response->addError($error_msg);
                    }
                }

                if (!$this->countSMTP()) {
                    pqnp_config('SMTP', '0');
                }

                $response->set('status', true);
                $response->set('count', Db::getInstance()->Affected_Rows());
            }
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    /**
     * Count smtp connections.
     *
     * @return int
     */
    public function countSMTP()
    {
        $sql = 'SELECT count(*) FROM `'._DB_PREFIX_.'newsletter_pro_smtp` WHERE 1';

        return Db::getInstance()->getValue($sql);
    }

    /**
     * Add a new smtp connection.
     *
     * @param array $data
     */
    public function addSMTP($data)
    {
        $response = ['status' => false, 'errors' => []];

        if (!trim($data['name'])) {
            $this->_errors[] = $this->l('The SMTP "Name" is required.');
        }

        if (!trim($data['from_email'])) {
            $this->_errors[] = $this->l('The "From email" field is required.');
        } else {
            $sql = 'SELECT count(*) FROM `'._DB_PREFIX_.'newsletter_pro_smtp` WHERE `name` = "'.pSQL($data['name']).'"';
            if (Db::getInstance()->getValue($sql)) {
                $this->_errors[] = $this->l('Duplicate name.');
            }
        }

        if (!trim($data['user']) && NewsletterProMail::METHOD_SMTP == (int) $data['method']) {
            $this->_errors[] = $this->l('The "SMTP user" is required.');
        }

        if (!$this->_errors) {
            $mail = new NewsletterProMail();
            $mail->name = trim($data['name']);
            $mail->method = (int) $data['method'];
            $mail->from_name = trim($data['from_name']);
            $mail->from_email = trim($data['from_email']);
            $mail->reply_to = trim($data['reply_to']);
            $mail->domain = trim($data['domain']);
            $mail->server = trim($data['server']);
            $mail->user = trim($data['user']);
            $mail->passwd = trim($data['passwd']);
            $mail->encryption = Tools::strtolower($data['encryption']);
            $mail->port = $data['port'];
            $mail->list_unsubscribe_active = (int) $data['list_unsubscribe_active'];
            $mail->list_unsubscribe_email = trim($data['list_unsubscribe_email']);

            if ($mail->add()) {
                if ($mail->id && pqnp_config('SMTP', $mail->id)) {
                    $sql = 'SELECT * FROM `'._DB_PREFIX_.'newsletter_pro_smtp` WHERE `id_newsletter_pro_smtp`='.(int) $mail->id.'';
                    $last_row = Db::getInstance()->getRow($sql);

                    $response['obj'] = $last_row;
                    $response['status'] = true;
                } else {
                    $response['status'] = false;
                }
            } else {
                $response['status'] = false;
            }
        } else {
            $response['errors'] = $this->_errors;
            $response['status'] = false;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Change smtp.
     *
     * @param int $id
     *
     * @return json
     */
    public function changeSMTP($id)
    {
        $response = ['status' => false, 'msg' => ''];

        if (pqnp_config('SMTP', $id)) {
            $response['status'] = true;
        } else {
            $response['msg'] = $this->l('The SMTP cannot be changed.');
            $response['status'] = false;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Change newsletter pro smtp active configuration.
     *
     * @param bool $bool
     *
     * @return json
     */
    public function smtpActive($bool)
    {
        $bool = 'true' == $bool ? 1 : 0;
        $response = ['status' => false];
        if (pqnp_config('SMTP_ACTIVE', $bool)) {
            $response['status'] = true;
        } else {
            $response['status'] = false;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Replace admin link.
     *
     * @param string $path
     *
     * @return string
     */
    public static function relplaceAdminLink($path)
    {
        if (self::REPLACE_ADMIN_PATH) {
            $module = NewsletterPro::getInstance();

            if (!empty($module->admin_name)) {
                $name = str_replace($module->admin_name.'/', '', $path);

                return $name;
            } else {
                return $path;
            }
        }

        return $path;
    }

    /**
     * Empty the personal list with email addresses.
     *
     * @return json
     */
    public function emptyAddedEmails()
    {
        $response = ['status' => false];
        if (Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'newsletter_pro_email` WHERE 1')) {
            $response['status'] = true;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Update the google analytics id.
     *
     * @param int $g_analytics_id
     *
     * @return json
     */
    public function updateGAnalyticsID($g_analytics_id)
    {
        $response = ['status' => false];
        if (pqnp_config('GOOGLE_ANALYTICS_ID', $g_analytics_id)) {
            $response['status'] = true;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Activate google analytics configuration.
     *
     * @param bool $bool
     *
     * @return json
     */
    public function activeGAnalytics($bool)
    {
        $bool = 'true' == $bool ? 1 : 0;
        $response = ['status' => false];
        if (pqnp_config('GOOGLE_ANALYTICS_ACTIVE', $bool)) {
            $response['status'] = true;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Activate universal analytics option.
     *
     * @param bool $bool
     *
     * @return json
     */
    public function universalAnaliytics($bool)
    {
        $bool = 'true' == $bool ? 1 : 0;
        $response = ['status' => false];
        if (pqnp_config('GOOGLE_UNIVERSAL_ANALYTICS_ACTIVE', $bool)) {
            $response['status'] = true;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Activate google analytics campaign.
     *
     * @param bool $bool
     *
     * @return json
     */
    public function activeCampaign($bool)
    {
        $ga_info = $this->getGAnalyticsModuleInfo();
        if ($ga_info['isInstalled']) {
            $header_hook = false;

            foreach ($ga_info['hooks'] as $hook) {
                if (('displayHeader' == $hook['name'] || 'header' == $hook['name']) && $hook['isRegistred']) {
                    $header_hook = $hook;
                    break;
                }
            }

            if ($header_hook) {
                $np_position = (int) $this->getModulePosition($this, $header_hook['id_hook']);
                $ga_position = (int) $header_hook['position'];

                if ($ga_position <= $np_position) {
                    $this->updatePosition($header_hook['id_hook'], 0, $ga_position);
                }
            }
        }

        $bool = 'true' == $bool ? 1 : 0;
        $response = ['status' => false];
        if (pqnp_config('CAMPAIGN_ACTIVE', $bool)) {
            $response['status'] = true;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Create image thumbnail.
     *
     * @param string $path
     * @param string $temp_path
     * @param string $name
     * @param string $thumb_height
     *
     * @return int
     */
    public function createImageThumb($path, $temp_path, $name, $thumb_height)
    {
        list(, , $image_type) = getimagesize($temp_path);

        $new_image = null;
        if (IMAGETYPE_JPEG == $image_type) {
            $new_image = imagecreatefromjpeg($temp_path);
        } elseif (IMAGETYPE_GIF == $image_type) {
            $new_image = imagecreatefromgif($temp_path);
        } elseif (IMAGETYPE_PNG == $image_type) {
            $new_image = imagecreatefrompng($temp_path);
        }

        $get_width = imagesx($new_image);
        $get_height = imagesy($new_image);

        $ratio = $thumb_height / $get_height;
        $thumb_width = $get_width * $ratio;

        $thumb = imagecreatetruecolor($thumb_width, $thumb_height);
        imagecopyresampled($thumb, $new_image, 0, 0, 0, 0, $thumb_width, $thumb_height, $get_width, $get_height);

        $thumb_name = 'thumb_'.$name;
        $thumb_path = $path.'thumb/'.$thumb_name;
        if (IMAGETYPE_JPEG == $image_type) {
            imagejpeg($thumb, $thumb_path, 100);
        } elseif (IMAGETYPE_GIF == $image_type) {
            imagegif($thumb, $thumb_path);
        } elseif (IMAGETYPE_PNG == $image_type) {
            imagepng($thumb, $thumb_path);
        }
    }

    /**
     * Upload image.
     *
     * @param array $image
     * @param int   $img_width
     *
     * @return json
     */
    public function uploadImage($image, $img_width)
    {
        $errors = [];
        $response = ['status' => false, 'errors' => &$errors];

        if (!empty($image)) {
            $path = $this->dir_location.'images/';
            $validate_file = $this->verifyFileErros($image);

            if (true === $validate_file) {
                if (preg_match('/\.jpg|\.jpeg|\.gif|\.png$/i', $image['name'])) {
                    if (!file_exists($path)) {
                        $errors[] = $this->l('Images path does not exist');
                    } else {
                        $image_ext = pathinfo($image['name'], PATHINFO_EXTENSION);
                        $name = uniqid().'.'.$image_ext;
                        $name = str_replace(' ', '-', $name);
                        $temp_path = $image['tmp_name'];
                        $full_path = $path.$name;

                        if ((int) $img_width > 0 && Tools::strlen((string) $img_width) <= 4) {
                            list(, , $image_type) = getimagesize($temp_path);

                            $new_image = null;
                            if (IMAGETYPE_JPEG == $image_type) {
                                $new_image = imagecreatefromjpeg($temp_path);
                            } elseif (IMAGETYPE_GIF == $image_type) {
                                $new_image = imagecreatefromgif($temp_path);
                            } elseif (IMAGETYPE_PNG == $image_type) {
                                $new_image = imagecreatefrompng($temp_path);
                            }

                            $get_width = imagesx($new_image);
                            $get_height = imagesy($new_image);

                            $ratio = $img_width / $get_width;
                            $img_height = $get_height * $ratio;

                            $img = imagecreatetruecolor($img_width, $img_height);
                            imagecopyresampled($img, $new_image, 0, 0, 0, 0, $img_width, $img_height, $get_width, $get_height);

                            $img_path = $path.$name;
                            if (IMAGETYPE_JPEG == $image_type) {
                                imagejpeg($img, $img_path, 100);
                            } elseif (IMAGETYPE_GIF == $image_type) {
                                imagegif($img, $img_path);
                            } elseif (IMAGETYPE_PNG == $image_type) {
                                imagepng($img, $img_path);
                            }

                            $this->createImageThumb($path, $temp_path, $name, 50);

                            $response['status'] = true;
                        } else {
                            if (move_uploaded_file($temp_path, $full_path)) {
                                $this->createImageThumb($path, $full_path, $name, 50);
                                $response['status'] = true;
                            } else {
                                $errors[] = $this->l('Image was not uploaded, please check the CHMOD.');
                            }
                        }
                    }
                } else {
                    $errors[] = $this->l('The file extension is not allowed');
                }
            } else {
                $errors[] = $validate_file;
            }
        } else {
            $errors[] = $this->l('No file was uploaded');
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Upload a csv file.
     *
     * @param string $file
     *
     * @return json
     */
    public function uploadCSV($file)
    {
        $response = ['status' => false, 'msg' => '', 'data' => []];

        $validate = $this->verifyFileErros($file);
        if (true === $validate) {
            $csv_path = 'csv/import/';
            $path = $this->dir_location.$csv_path;
            $name = $file['name'];
            $tmp_name = $file['tmp_name'];

            $extension = pathinfo($name, PATHINFO_EXTENSION);
            if (preg_match('/csv/i', $extension)) {
                $unique_name = $this->uniqueName($name);
                $full_path = $path.$unique_name;

                if (file_exists($path) && move_uploaded_file($tmp_name, $full_path)) {
                    $response['status'] = true;
                    $response['data']['name'] = $unique_name;
                    $response['data']['uri'] = $this->uri_location.$unique_name;
                    $response['data']['url'] = $this->url_location.$unique_name;
                } else {
                    $response['msg'] = $this->l('The file cannot be uploaded.');
                }
            } else {
                $response['msg'] = $this->l('The file extension is not allowed.');
            }
        } else {
            $response['msg'] = $validate;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Get an unique name.
     *
     * @param string $name
     *
     * @return string
     */
    public function uniqueName($name)
    {
        $name = preg_replace('/[\s]/', '_', trim($name));

        return uniqid().'_'.$name;
    }

    /**
     * Verify upload file errors.
     *
     * @param string $file
     *
     * @return string
     */
    public function verifyFileErros($file)
    {
        $message = '';

        if (UPLOAD_ERR_OK == $file['error'] && 0 != $file['size']) {
            return true;
        } elseif (UPLOAD_ERR_INI_SIZE == $file['error']) {
            $message = $this->l('The uploaded file exceeds the upload_max_filesize directive in php.ini');
        } elseif (UPLOAD_ERR_PARTIAL == $file['error']) {
            $message = $this->l('The uploaded file was only partially uploaded');
        } elseif (UPLOAD_ERR_NO_FILE == $file['error']) {
            $message = $this->l('No file was uploaded');
        } elseif (UPLOAD_ERR_NO_TMP_DIR == $file['error']) {
            $message = $this->l('Missing a temporary folder');
        } elseif (UPLOAD_ERR_CANT_WRITE == $file['error']) {
            $message = $this->l('Failed to write file to disk');
        } elseif (UPLOAD_ERR_EXTENSION == $file['error']) {
            $message = $this->l('A PHP extension stopped the file upload');
        } else {
            $message = $this->l('File error');
        }

        return $message;
    }

    /**
     * Delete the csv file by name.
     *
     * @param string $name
     *
     * @return json
     */
    public function deleteCSVByName($name)
    {
        $response = ['status' => false, 'msg' => ''];

        $path = $this->dir_location.'csv/import/'.$name;

        if (file_exists($path) && unlink($path)) {
            $response['status'] = true;
        } else {
            $response['msg'] = $this->l('The file cannot be deleted.');
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Get the csv file line info.
     *
     * @param  array/null $line
     *
     * @return array
     */
    private function getCSVLineInfo($line)
    {
        $from = 1;
        $to = 0;
        if (isset($line) && is_array($line) && isset($line['from'], $line['to'])) {
            $from = (int) $line['from'] < 1 ? 1 : (int) $line['from'];
            $to = (int) $line['to'] < 0 ? 0 : (int) $line['to'];
        }

        return [
            'from' => $from,
            'to' => $to,
        ];
    }

    /**
     * Load a csv file.
     *
     * @param string $filename
     * @param string $delimiter
     *
     * @return bool
     */
    public function loadCSV($filename, $delimiter = ',', $line = null)
    {
        $full_path = $this->dir_location.'csv/import/'.$filename;

        $line_info = $this->getCSVLineInfo($line);
        $from = $line_info['from'];
        $to = $line_info['to'];

        if ($data = $this->csvToArray($full_path, $delimiter, $from, $to)) {
            $db_fields = [
                    'email' => 'Email',
                    'firstname' => 'First Name',
                    'lastname' => 'Last Name',
                    'id_lang' => 'Language ID',
                    'id_shop' => 'Shop ID',
                    'id_shop_group' => 'Shop Group ID',
                    'ip_registration_newsletter' => 'Registration IP Address',
                    'active' => 'Active',
                ];

            $count = null;
            $header = null;
            $rows = null;

            extract($data);

            $this->context->smarty->assign([
                    'count' => $count - 1,
                    'header' => $header,
                    'rows' => $rows,
                    'db_fields' => $db_fields,
                ]);

            return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/preview_details.tpl'));
        }

        return false;
    }

    /**
     * Sanitize utf8.
     *
     * @param string $value
     *
     * @return string
     */
    public function sanitizeUTF8($value)
    {
        return preg_replace('/[^(\x20-\x7F)]*/', '', trim($value, " \t\n\r\0\x0B,;"));
    }

    /**
     * Import emails from a csv file.
     *
     * @param string $filename
     * @param string $delimiter
     * @param array  $fields_get
     *
     * @return bool
     */
    public function importCSV($filename, $delimiter = ',', $fields_get = [], $line = null, $filter_name = null)
    {
        $line_info = $this->getCSVLineInfo($line);
        $from = $line_info['from'];
        $to = $line_info['to'];

        $full_path = $this->dir_location.'csv/import/'.$filename;

        if ($data = $this->csvToArray($full_path, $delimiter, $from, $to)) {
            $count = null;
            $header = null;
            $rows = null;

            extract($data);

            $fields = [];
            foreach ($fields_get as $field) {
                $fields[$field['db_field']] = $field['csv_field'];
            }

            if (!isset($fields['email'])) {
                $this->context->smarty->assign([
                        'status' => false,
                        'msg' => $this->l('You have to set the email field!'),
                    ]);

                return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/import_details.tpl'));
            }

            $valid_rows = [];

            if (!empty($rows)) {
                foreach ($rows as $key => $row) {
                    if (isset($fields['email'], $row[$fields['email']]) && Validate::isEmail($this->sanitizeUTF8($row[$fields['email']]))) {
                        $valid_rows[$key]['email'] = $this->sanitizeUTF8($row[$fields['email']]);
                        $valid_rows[$key]['date_add'] = date('Y:m:d H:i:s');
                        $valid_rows[$key]['firstname'] = null;
                        $valid_rows[$key]['lastname'] = null;
                        $valid_rows[$key]['id_lang'] = (int) $this->context->language->id;
                        $valid_rows[$key]['id_shop'] = (int) $this->context->shop->id;
                        $valid_rows[$key]['id_shop_group'] = (int) $this->context->shop->id_shop_group;
                        $valid_rows[$key]['ip_registration_newsletter'] = null;
                        $valid_rows[$key]['active'] = 1;

                        if (isset($fields['firstname'], $row[$fields['firstname']])) {
                            $valid_rows[$key]['firstname'] = $row[$fields['firstname']];
                        }

                        if (isset($fields['lastname'], $row[$fields['lastname']])) {
                            $valid_rows[$key]['lastname'] = $row[$fields['lastname']];
                        }

                        if (isset($fields['id_lang'], $row[$fields['id_lang']]) && 0 != $row[$fields['id_lang']]) {
                            $valid_rows[$key]['id_lang'] = $row[$fields['id_lang']];
                        }

                        if (isset($fields['id_shop'], $row[$fields['id_shop']]) && 0 != $row[$fields['id_shop']]) {
                            $valid_rows[$key]['id_shop'] = $row[$fields['id_shop']];
                        }

                        if (isset($fields['id_shop_group'], $row[$fields['id_shop_group']]) && 0 != $row[$fields['id_shop_group']]) {
                            $valid_rows[$key]['id_shop_group'] = $row[$fields['id_shop_group']];
                        }

                        if (isset($fields['ip_registration_newsletter'], $row[$fields['ip_registration_newsletter']])) {
                            $valid_rows[$key]['ip_registration_newsletter'] = $row[$fields['ip_registration_newsletter']];
                        }

                        if (isset($fields['active'], $row[$fields['active']])) {
                            $valid_rows[$key]['active'] = $row[$fields['active']];
                        }
                    }
                }
            }

            if (empty($valid_rows)) {
                $this->context->smarty->assign([
                        'status' => false,
                        'msg' => $this->l('The column with emails was not found!'),
                    ]);

                return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/import_details.tpl'));
            }

            $newsletter_pro_email = Db::getInstance()->executeS('SELECT `email`, `active` FROM `'._DB_PREFIX_.'newsletter_pro_email`');

            if (self::CSV_IMPORT_STRICT) {
                $newsletter = [];
                if (Db::getInstance()->getValue("
					SELECT COUNT(*) AS `count`
					FROM INFORMATION_SCHEMA.TABLES
					WHERE  TABLE_SCHEMA = '"._DB_NAME_."'
					AND TABLE_NAME = '"._DB_PREFIX_."newsletter'
				")) {
                    $newsletter = Db::getInstance()->executeS('SELECT `email`, `active` FROM `'._DB_PREFIX_.'newsletter`');
                }
            }

            if (self::CSV_IMPORT_STRICT) {
                $customer = Db::getInstance()->executeS('SELECT `email`, `newsletter` AS `active` FROM `'._DB_PREFIX_.'customer` WHERE `newsletter` = 1');
            } else {
                $customer = [];
            }

            $emails_db = array_merge($newsletter, $customer);

            foreach ($emails_db as $key => $email) {
                $emails_db[$key] = trim($email['email']);
            }

            foreach ($newsletter_pro_email as $key => $email) {
                $newsletter_pro_email[$key] = trim($email['email']);
            }

            foreach ($valid_rows as $key => $row) {
                $row['email'] = trim($row['email']);

                if (in_array($row['email'], $emails_db)) {
                    unset($valid_rows[$key]);
                } elseif (in_array($row['email'], $newsletter_pro_email)) {
                    $update_sql = 'UPDATE `'._DB_PREFIX_.'newsletter_pro_email` SET ';
                    $update_sql .= '`firstname` = '.(null == $row['firstname'] ? 'NULL' : '"'.pSQL($row['firstname']).'"').', ';
                    $update_sql .= '`lastname` = '.(null == $row['lastname'] ? 'NULL' : '"'.pSQL($row['lastname']).'"').', ';
                    $update_sql .= '`filter_name` = '.(!isset($filter_name) ? 'NULL' : '"'.pSQL(trim($filter_name)).'"').', ';
                    $update_sql .= '`id_lang` = '.(int) $row['id_lang'].', ';
                    $update_sql .= '`id_shop` = '.(int) $row['id_shop'].', ';
                    $update_sql .= '`id_shop_group` = '.(int) $row['id_shop_group'].', ';
                    $update_sql .= '`ip_registration_newsletter` = '.
                                    (null == $row['ip_registration_newsletter'] ? 'NULL' : '"'.(int) $row['ip_registration_newsletter'].'"').
                                    ', ';
                    $update_sql .= '`date_add` = "'.pSQL($row['date_add']).'", ';
                    $update_sql .= '`active` = '.(int) $row['active'];
                    $update_sql .= ' WHERE `email` = "'.pSQL($row['email']).'" ;';

                    Db::getInstance()->execute($update_sql);
                } elseif (Validate::isEmail($row['email'])) {
                    $sql = 'INSERT INTO `'._DB_PREFIX_.'newsletter_pro_email` (`email`, `firstname`, `lastname`,`filter_name` ,`id_lang`, `id_shop`,
							`id_shop_group`, `ip_registration_newsletter`, `active`, `date_add`) VALUES ';
                    $sql .= '(';
                    $sql .= '"'.$row['email'].'", ';
                    $sql .= (null == $row['firstname'] ? 'NULL,' : '"'.pSQL($row['firstname']).'", ');
                    $sql .= (null == $row['lastname'] ? 'NULL,' : '"'.pSQL($row['lastname']).'", ');
                    $sql .= (!isset($filter_name) ? 'NULL,' : '"'.pSQL(trim($filter_name)).'", ');
                    $sql .= (int) $row['id_lang'].', ';
                    $sql .= (int) $row['id_shop'].', ';
                    $sql .= (int) $row['id_shop_group'].', ';
                    $sql .= (null == $row['ip_registration_newsletter'] ? 'NULL,' : '"'.(int) $row['ip_registration_newsletter'].'", ');
                    $sql .= (int) $row['active'].', ';
                    $sql .= '"'.pSQL($row['date_add']).'" ';
                    $sql .= ') ';
                    $sql = Tools::substr($sql, 0, -1).';';

                    Db::getInstance()->execute($sql);
                }

                // Fix rows
                foreach (array_keys($row) as $k) {
                    if (!array_key_exists($k, $fields)) {
                        unset($valid_rows[$key][$k]);
                    }
                }
            }

            if (!empty($valid_rows)) {
                foreach ($header as $key => $head) {
                    if (!in_array($head, $fields)) {
                        unset($header[$key]);
                    }
                }

                // sort items for a proper display
                $sorted_valid_rows = [];

                foreach ($valid_rows as $key => $value) {
                    $sorted_value = [];
                    foreach (array_keys($fields) as $fk) {
                        if (array_key_exists($fk, $value)) {
                            $sorted_value[$fk] = $value[$fk];
                        }
                    }

                    $sorted_valid_rows[$key] = $sorted_value;
                }

                $this->context->smarty->assign([
                    'count' => $count - 1,
                    'header' => $header,
                    'rows' => $sorted_valid_rows,
                    'valid' => count($valid_rows),
                ]);

                return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/import_details.tpl'));
            } else {
                $this->context->smarty->assign([
                        'status' => false,
                        'msg' => $this->l('No emails imported. Only those emails that are not duplicate can be imported!'),
                    ]);

                return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/import_details.tpl'));
            }
        }

        return false;
    }

    /**
     * Convert csv file to array.
     *
     * @param string $filename
     * @param string $delimiter
     *
     * @return array/boolean
     */
    public function csvToArray($filename = '', $delimiter = ',', $from = 2, $to = 0, $has_header = true)
    {
        ini_set('auto_detect_line_endings', true);
        if (!file_exists($filename) || !is_readable($filename)) {
            return false;
        }

        if ($has_header) {
            $from = $from - 1;
            $to = $to - 1;
        }

        $diff = $to - $from;
        $current_row_count = 0;

        $rows = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

        foreach ($rows as $key => $value) {
            // this solve the russian characters plobelm
            // $value = utf8_encode($value);

            $rows[$key] = mb_convert_encoding($rows[$key], 'UTF-8', mb_detect_encoding($rows[$key], 'UTF-8, ISO-8859-1, ISO-8859-15', true));
        }

        foreach ($rows as $key => $line) {
            if (preg_match('/^(,+|\s+?|)$/', $line)) {
                unset($rows[$key]);
            }
        }

        if (!empty($rows)) {
            $regex = '/'.$delimiter.'(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/';

            $header = null;
            if ($has_header) {
                $row_head = rtrim($rows[0], ';, ');
                $header = preg_split($regex, $row_head, -1, PREG_SPLIT_DELIM_CAPTURE);
                unset($rows[0]);
            }

            $content = [];
            foreach ($rows as $key => $row) {
                ++$current_row_count;

                $row = rtrim($row, ';, ');

                $row = preg_split($regex, $row, -1, PREG_SPLIT_DELIM_CAPTURE);
                foreach ($row as $k => $v) {
                    if (isset($header[$k])) {
                        $header[$k] = trim($header[$k]);
                        if (isset($v[0], $v[Tools::strlen($v) - 1]) && '"' == $v[0] && '"' == $v[Tools::strlen($v) - 1]) {
                            $v = Tools::substr($v, 1, -1);
                        }
                        $row[$header[$k]] = str_replace('""', '"', $v);
                        unset($row[$k]);
                    }
                }

                if ($diff > 0 && ($current_row_count >= $from && $current_row_count < $to)) {
                    // this will import the renge records
                    $content[$key + 1] = $row;
                } elseif ($diff <= 0) {
                    // this case will import all the records
                    $content[$key + 1] = $row;
                } elseif ($current_row_count >= $to) {
                    // this care will break the loop if all the required records has been imported
                    break;
                }
            }

            return ['count' => count($content) + 1, 'header' => $header, 'rows' => $content];
        } else {
            return false;
        }
    }

    /**
     * Save newsletter campaign parameters.
     *
     * @param array $params
     *
     * @return bool
     */
    public function saveParameteres($params)
    {
        if (Configuration::updateValue('NEWSLETTER_PRO_CAMPAIGN', $params, false, 0, 0)) {
            return true;
        }

        return false;
    }

    /**
     * Save campaign.
     *
     * @param array $data
     *
     * @return json
     */
    public function saveCampaign($data)
    {
        $response = ['status' => false];
        if (count($data) > 1) {
            $campaign = ['UTM_SOURCE' => preg_replace('/[&?]/', '', $data['utm_source']),
                                'UTM_MEDIUM' => preg_replace('/[&?]/', '', $data['utm_medium']),
                                'UTM_CAMPAIGN' => preg_replace('/[&?]/', '', $data['utm_campaign']),
                                'UTM_CONTENT' => preg_replace('/[&?]/', '', $data['utm_content']),
                        ];

            if (pqnp_config('CAMPAIGN', $campaign) && $this->saveParameteres($data['params'])) {
                $response['status'] = true;
            }
        } else {
            $response['status'] = null;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Setup the google analytics campaign default parameters.
     *
     * @return json
     */
    public function makeDefaultParameteres()
    {
        $response = ['status' => false, 'params' => '', 'campaign' => $this->default_campaign_params];

        if (pqnp_config('CAMPAIGN', $this->default_campaign_params) && $this->saveParameteres('')) {
            $response['status'] = true;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Get task template.
     *
     * @return string
     */
    public function getTaskTemplate()
    {
        $tpl = pqnp_template_path($this->dir_location.'views/templates/admin/task/template.tpl');

        return $this->context->smarty->fetch($tpl);
    }

    /**
     * Transform a date format to a jQuery date format.
     *
     * @param string $date_string
     *
     * @return string
     */
    public function dateToJQuery($date_string)
    {
        $pattern = ['d', 'j', 'l', 'z', 'F', 'M', 'n', 'm', 'Y', 'y'];

        $replace = [
            'dd', 'd', 'DD', 'o',
            'MM', 'M', 'm', 'mm',
            'yy', 'y',
        ];

        foreach ($pattern as &$p) {
            $p = '/'.$p.'/';
        }

        return preg_replace($pattern, $replace, $date_string);
    }

    /**
     * Sort array elements by date.
     *
     * @param array $a
     * @param array $b
     *
     * @return array
     */
    private static function sortByDate($a, $b)
    {
        return strtotime($a['date']) < strtotime($b['date']);
    }

    /**
     * Get product templates.
     *
     * @return json
     */
    public function getProductTemplates()
    {
        $list = [];
        $path = $this->dir_location.'mail_templates/product/';

        $result = NewsletterProTools::getDirectoryIterator($path, '/^.+\.html$/i');

        $id = 1;
        $i = 0;
        foreach ($result as $file) {
            $list[$i]['id'] = $id;
            $list[$i]['name'] = Tools::ucfirst(str_replace('_', ' ', pathinfo($file->getFilename(), PATHINFO_FILENAME)));
            $list[$i]['filename'] = $file->getFilename();
            $list[$i]['path'] = $file->getPathName();
            $list[$i]['date'] = date($this->context->language->date_format_full, filemtime($file->getPathName()));
            $list[$i]['selected'] = false;
            $list[$i]['info'] = [];

            if (is_readable($file->getPathName())) {
                $content = Tools::file_get_contents($file->getPathName());

                if (preg_match('/<!-- start header -->[\s\S]*?<!--([\s\S]*?)-->[\s\S]*?<!-- end header -->/', $content, $match)) {
                    $header = $match[1];

                    if (preg_match('/info\s+?=\s+?(.*);/', $header, $m)) {
                        $list[$i]['info'] = Tools::jsonDecode(trim($m[1]), true);
                    }
                }
            }

            if ($list[$i]['filename'] == pqnp_config('PRODUCT_TEMPLATE')) {
                $list[$i]['selected'] = true;
            }

            ++$id;
            ++$i;
        }

        usort($list, [$this, 'sortByDate']);

        return Tools::jsonEncode($list);
    }

    /**
     * Get images.
     *
     * @return json
     */
    public function getImages()
    {
        $list = [];
        $path = $this->dir_location.'images/';

        $result = NewsletterProTools::getDirectoryIterator($path, '/\.jpg|\.jpeg|\.png|\.gif$/i');

        $id = 1;
        $i = 0;
        foreach ($result as $file) {
            $list[$i]['id'] = $id;
            $list[$i]['name'] = Tools::ucfirst(str_replace('_', ' ', pathinfo($file->getFilename(), PATHINFO_FILENAME)));
            $list[$i]['filename'] = $file->getFilename();
            $thumb_filename = 'thumb_'.$file->getFilename();
            $list[$i]['thumb_filename'] = $thumb_filename;
            $list[$i]['path'] = $file->getPathName();
            $list[$i]['size'] = $file->getSize();

            $image_size = getimagesize($file->getPathName());
            $width = $image_size[0];
            $height = $image_size[1];

            $list[$i]['width'] = $width;
            $list[$i]['height'] = $height;

            $list[$i]['link'] = $this->url_location.'images/'.$file->getFilename();
            $list[$i]['thumb_link'] = $this->url_location.'images/thumb/'.$thumb_filename;

            $list[$i]['thumb_path'] = null;
            $thumb_path = $file->getPath().'/thumb/'.$thumb_filename;

            $list[$i]['thumb_path'] = null;
            if (file_exists($thumb_path)) {
                $list[$i]['thumb_path'] = $thumb_path;
            }

            $list[$i]['date'] = date($this->context->language->date_format_full, filemtime($file->getPathName()));

            ++$id;
            ++$i;
        }

        usort($list, [$this, 'sortByDate']);

        return Tools::jsonEncode($list);
    }

    /**
     * Get added list.
     *
     * @param bool $encode
     *
     * @return json/array
     */
    public function getAdded($encode = true)
    {
        $sql = $this->getAddedSql();

        $userlist = Db::getInstance()->executeS($sql);

        foreach ($userlist as &$user) {
            $user['img_path'] = $this->getLangImageById($user['id_lang']);
        }

        if ($encode) {
            return Tools::jsonEncode($userlist);
        } else {
            return $userlist;
        }
    }

    /**
     * Get added list sql.
     *
     * @param string $and
     * @param string $end
     *
     * @return string
     */
    private function getAddedSql($and = '', $end = '')
    {
        $sql_shops_id = '';
        $get_active_shops_id = NewsletterProTools::getActiveShopsId();
        $subscribed = (int) pqnp_config('VIEW_ACTIVE_ONLY');

        foreach ($get_active_shops_id as $id_shop) {
            $sql_shops_id .= 'n.`id_shop` = '.(int) $id_shop.(end($get_active_shops_id) == $id_shop ? '' : ' OR ');
        }

        $sql = 'SELECT n.`id_newsletter_pro_email`, n.`email`, n.`firstname`, n.`lastname`, n.`id_shop`, n.`id_lang`,
				n.`date_add` AS `newsletter_date_add`, n.`active`, s.`name` AS `shop_name`, l.`name` AS `language`
				FROM `'._DB_PREFIX_.'newsletter_pro_email` n
				LEFT JOIN `'._DB_PREFIX_.'lang` l ON (l.`id_lang` = n.`id_lang`)
				LEFT JOIN `'._DB_PREFIX_.'shop` s ON (s.`id_shop` = n.`id_shop`)
				WHERE ( '.$sql_shops_id.' ) ';
        $sql .= ($subscribed ? ' AND n.`active` = '.(int) $subscribed : '');

        if (Tools::strlen(trim($and)) > 0) {
            $sql .= ' AND '.$and.' ';
        }

        $sql .= ' ORDER BY n.`id_newsletter_pro_email` ASC ';

        if (Tools::strlen(trim($end)) > 0) {
            $sql .= ' '.$end.' ';
        }

        $sql .= ';';

        return $sql;
    }

    /**
     * Update added email.
     *
     * @param int $id
     *
     * @return int
     */
    public function updateAdded($id)
    {
        parse_str(Tools::file_get_contents('php://input'), $put);
        $data = [
            'active' => (int) $put['active'],
        ];

        return (int) (Db::getInstance()->update('newsletter_pro_email', $data, '`id_newsletter_pro_email`= '.(int) $id));
    }

    /**
     * Delete added.
     *
     * @param int $id
     *
     * @return int
     */
    public function deleteAdded($id)
    {
        return (int) Db::getInstance()->delete('newsletter_pro_email', '`id_newsletter_pro_email`= '.(int) $id, 1);
    }

    /**
     * Delete forwarder to email.
     *
     * @param string $email
     *
     * @return int
     */
    public function deleteForwardToEmail($email)
    {
        return (int) Db::getInstance()->delete('newsletter_pro_forward', '`to`="'.pSQL($email).'"');
    }

    /**
     * Delete forwarder from email.
     *
     * @param email $email
     *
     * @return int
     */
    public function deleteForwardFromEmail($email)
    {
        return (int) Db::getInstance()->delete('newsletter_pro_forward', '`from`="'.pSQL($email).'"');
    }

    /**
     * Clear forwarders.
     *
     * @return int
     */
    public function clearForwarders()
    {
        $sql = 'DELETE FROM `'._DB_PREFIX_.'newsletter_pro_forward` WHERE 1';

        return (int) Db::getInstance()->execute($sql);
    }

    /**
     * Search in personal list with email addresses.
     *
     * @param string $value
     *
     * @return json
     */
    public function searchAdded($value)
    {
        $search = ' ( n.`email` LIKE "%'.pSQL($value).'%" OR n.`firstname` LIKE "%'.pSQL($value).'%" OR n.`lastname` LIKE "%'.pSQL($value).'%" )';
        $sql = $this->getAddedSql($search);

        $userlist = Db::getInstance()->executeS($sql);
        foreach ($userlist as &$user) {
            $user['img_path'] = $this->getLangImageById($user['id_lang']);
        }

        return Tools::jsonEncode($userlist);
    }

    /**
     * Filter added.
     *
     * @param array $filters
     *
     * @return json
     */
    public function filterAdded($filters)
    {
        if (is_array($filters) && !empty($filters)) {
            if (isset($filters['range_selection']['min'], $filters['range_selection']['max'])) {
                if ($filters['range_selection']['min'] && $filters['range_selection']['max']) {
                    $range_selection = $filters['range_selection'];
                }

                unset($filters['range_selection']);
            }
        }

        if (is_array($filters) && !empty($filters) || isset($range_selection)) {
            $sql = '';

            $filters_count = count($filters);
            $filters_index = 1;
            foreach ($filters as $type => $ids) {
                if ('languages' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' n.id_lang = '.(int) $id.' OR ';
                        } else {
                            $sql .= ' n.id_lang = '.(int) $id.' ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('shops' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' n.id_shop = '.(int) $id.' OR ';
                        } else {
                            $sql .= ' n.id_shop = '.(int) $id.' ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('csv_name' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' n.`filter_name` = "'.pSQL($id).'" OR ';
                        } else {
                            $sql .= ' n.`filter_name` = "'.pSQL($id).'" ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('subscribed' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' n.active = '.(int) $id.' OR ';
                        } else {
                            $sql .= ' n.active = '.(int) $id.' ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                }

                if ($filters_index != $filters_count) {
                    $sql .= ' AND ';
                }

                ++$filters_index;
            }

            if (true == pqnp_config('VIEW_ACTIVE_ONLY') && $filters_count > 0) {
                $sql .= ' AND n.`active` = 1';
            } elseif (true == pqnp_config('VIEW_ACTIVE_ONLY')) {
                $sql .= ' n.`active` = 1';
            } else {
                $sql .= '';
            }

            $end = '';
            if (isset($range_selection)) {
                $lim_start = (int) $range_selection['min'];
                if ($lim_start < 0) {
                    $lim_start = 0;
                }

                $lim_end = (int) $range_selection['max'] - (int) $range_selection['min'];
                if ($lim_end < 0) {
                    $lim_end = 0;
                }

                $end = ' LIMIT '.(int) $lim_start.', '.(int) $lim_end.' ';
            }

            $sql = $this->getAddedSql($sql, $end);

            $userlist = Db::getInstance()->executeS($sql);

            foreach ($userlist as &$user) {
                $user['img_path'] = $this->getLangImageById($user['id_lang']);
            }

            return Tools::jsonEncode($userlist);
        }

        return $this->getAdded();
    }

    /**
     * Create added.
     *
     * @param array $post
     *
     * @return json
     */
    public function createAdded($post)
    {
        $errors = [];
        $response = ['status' => false, 'errors' => &$errors];

        $data = [
            'firstname' => pSQL($post['firstname']),
            'lastname' => pSQL($post['lastname']),
            'email' => pSQL($post['email']),
            'id_shop' => (int) $post['id_shop'],
            'id_lang' => (int) $post['id_lang'],
            'filter_name' => pSQL(trim($post['filter_name'])),
        ];

        if (!Validate::isName($data['firstname'])) {
            $errors[] = $this->l('Invalid First Name!');
        }

        if (!Validate::isName($data['lastname'])) {
            $errors[] = $this->l('Invalid Last Name!');
        }

        if (!Validate::isEmail($data['email'])) {
            $errors[] = $this->l('Invalid email address!');
        }

        $count_email = 'SELECT count(*) FROM `'._DB_PREFIX_.'newsletter_pro_email` WHERE `email` = "'.pSQL($data['email']).'"';
        if (Db::getInstance()->getValue($count_email)) {
            $errors[] = $this->l('Duplicate email address!');
        }

        if (!Validate::isInt($data['id_shop'])) {
            $errors[] = $this->l('Invalid shop!');
        }

        if (!Validate::isInt($data['id_lang'])) {
            $errors[] = $this->l('Invalid language!');
        }

        if (!Validate::isString($data['filter_name'])) {
            $errors[] = $this->l('CSV Name is invalid.');
        }

        if (empty($errors)) {
            if (Db::getInstance()->insert('newsletter_pro_email', $data)) {
                $response['status'] = true;
            } else {
                $errors[] = $this->l('The email address cannot be added!');
            }
        }

        return Tools::jsonEncode($response);
    }

    public function getCustomSubscriptionColumns()
    {
        $custom_columns = pqnp_config('SHOW_CUSTOM_COLUMNS');
        $valid_columns = [];

        if (!empty($custom_columns)) {
            foreach ($custom_columns as $name) {
                if (NewsletterProTools::columnExists('newsletter_pro_subscribers', $name)) {
                    $valid_columns[] = $name;
                }
            }
        }

        return $valid_columns;
    }

    public function getVisitorsNpColumns()
    {
        $columns = NewsletterProTools::getDbColumns('newsletter_pro_subscribers');

        foreach ($columns as $key => $column) {
            if ('id_newsletter_pro_subscribers' == $column) {
                unset($columns[$key]);
            }
        }

        return array_values($columns);
    }

    /**
     * Get newsletter pro visitors subscribed sql.
     *
     * @param string $and
     * @param string $end
     *
     * @return string
     */
    private function getVisitorsNPSql($and = '', $end = '')
    {
        $sql_shops_id = '';
        $get_active_shops_id = NewsletterProTools::getActiveShopsId();
        $subscribed = (int) pqnp_config('VIEW_ACTIVE_ONLY');

        foreach ($get_active_shops_id as $id_shop) {
            $sql_shops_id .= 'n.`id_shop` = '.(int) $id_shop.(end($get_active_shops_id) == $id_shop ? '' : ' OR ');
        }

        $valid_columns = $this->getCustomSubscriptionColumns();

        $valid_columns_sql = '';
        foreach ($valid_columns as $name) {
            $valid_columns_sql .= 'n.`'.$name.'`,';
        }

        $sql = 'SELECT n.`id_newsletter_pro_subscribers`,
						n.`email`,
						n.`birthday`,
						n.`firstname`,
						n.`lastname`,
						n.`id_shop`,
						n.`id_lang`,
						n.`id_gender`,
						n.`date_add` AS `newsletter_date_add`,
						n.`active`, s.`name` AS `shop_name`,
						'.pSQL($valid_columns_sql).'
						l.`name` AS `language`
				FROM `'._DB_PREFIX_.'newsletter_pro_subscribers` n
				LEFT JOIN `'._DB_PREFIX_.'lang` l ON (l.`id_lang` = n.`id_lang`)
				LEFT JOIN `'._DB_PREFIX_.'shop` s ON (s.`id_shop` = n.`id_shop`)
				WHERE ( '.$sql_shops_id.' ) ';

        $sql .= ($subscribed ? ' AND n.`active` = '.(int) $subscribed : '');

        if (Tools::strlen(trim($and)) > 0) {
            $sql .= ' AND '.$and.' ';
        }

        $sql .= ' ORDER BY n.`id_newsletter_pro_subscribers` ASC ';

        if (Tools::strlen(trim($end)) > 0) {
            $sql .= ' '.$end.' ';
        }

        $sql .= ';';

        return $sql;
    }

    /**
     * Get newsletter pro visitors subscribed.
     *
     * @param bool $encode
     *
     * @return json/array
     */
    public function getVisitorsNP($encode = true)
    {
        $sql = $this->getVisitorsNPSql();

        $userlist = Db::getInstance()->executeS($sql);

        foreach ($userlist as &$user) {
            $user['img_path'] = $this->getLangImageById($user['id_lang']);
        }

        if ($encode) {
            return Tools::jsonEncode($userlist);
        } else {
            return $userlist;
        }
    }

    /**
     * Update newsletter pro visitors subscribed.
     *
     * @param int $id
     *
     * @return int
     */
    public function updateVisitorNP($id)
    {
        parse_str(Tools::file_get_contents('php://input'), $put);
        $data = [
            'active' => (int) $put['active'],
        ];

        return (int) (Db::getInstance()->update('newsletter_pro_subscribers', $data, '`id_newsletter_pro_subscribers`= '.(int) $id));
    }

    /**
     * Delete newsletter pro visitors subscribed.
     *
     * @param int $id
     *
     * @return int
     */
    public function deleteVisitorNP($id)
    {
        return (int) Db::getInstance()->delete('newsletter_pro_subscribers', '`id_newsletter_pro_subscribers`= '.(int) $id, 1);
    }

    /**
     * Search newsletter pro visitors subscribed.
     *
     * @param string $value
     *
     * @return json
     */
    public function searchVisitorNP($value, $conditions)
    {
        if (is_array($conditions) && !empty($conditions)) {
            $selected_condition = (int) $conditions['selected_condition'];
            $selected_field = $conditions['selected_field'];

            $db_fields = $this->getVisitorsNpColumns();
            $search_in_fields = $db_fields;

            if ('0' != $selected_field && in_array($selected_field, $db_fields)) {
                $search_in_fields = [$selected_field];
            }

            $search_query = '';

            // if the filter is setup to all fields, filter only the below columns

            if ('0' == $selected_field) {
                $new_fields = [];

                if (in_array('firstname', $search_in_fields)) {
                    $new_fields[] = 'firstname';
                }

                if (in_array('lastname', $search_in_fields)) {
                    $new_fields[] = 'lastname';
                }

                if (in_array('email', $search_in_fields)) {
                    $new_fields[] = 'email';
                }

                $search_in_fields = $new_fields;
            }

            foreach ($search_in_fields as $field) {
                if (!in_array($field, $db_fields)) {
                    continue;
                }

                $is_int = false;

                if (in_array($field, ['id_shop', 'id_shop_group', 'id_lang', 'id_gender', 'active'])) {
                    $is_int = true;
                } else {
                    $is_int = false;
                }

                switch ($selected_condition) {
                    case self::SEARCH_CONDITION_CONTAINS:
                            $search_query .= ' n.`'.$field.'` LIKE '.($is_int ? '"%'.(int) $value.'%"' : '"%'.pSQL($value).'%"').' OR';
                        break;

                    case self::SEARCH_CONDITION_IS:
                            $search_query .= ' n.`'.$field.'` = '.($is_int ? (int) $value : '"'.pSQL($value).'"').' OR';
                        break;

                    case self::SEARCH_CONDITION_IS_NOT:
                            $search_query .= ' n.`'.$field.'` != '.($is_int ? (int) $value : '"'.pSQL($value).'"').' OR';
                        break;

                    case self::SEARCH_CONDITION_GREATER:
                        $search_query .= ' n.`'.$field.'` >= '.($is_int ? (int) $value : '"'.pSQL($value).'"').' OR';
                        break;

                    case self::SEARCH_CONDITION_LESS:
                        $search_query .= ' n.`'.$field.'` <= '.($is_int ? (int) $value : '"'.pSQL($value).'"').' OR';
                        break;
                }
            }

            $search = '(
				'.rtrim($search_query, 'OR').'
			)';
        } else {
            $search = ' ( n.`email` LIKE "%'.pSQL($value).'%" OR n.`firstname` LIKE "%'.pSQL($value).'%" OR n.`lastname` LIKE "%'.pSQL($value).'%" )';
        }

        $sql = $this->getVisitorsNPSql($search);

        $userlist = Db::getInstance()->executeS($sql);
        foreach ($userlist as &$user) {
            $user['img_path'] = $this->getLangImageById($user['id_lang']);
        }

        return Tools::jsonEncode($userlist);
    }

    /**
     * Filter newsletter pro visitors subscribed.
     *
     * @param array $filters
     *
     * @return json
     */
    public function filterVisitorNP($filters)
    {
        if (is_array($filters) && !empty($filters)) {
            if (isset($filters['by_birthday']['from'], $filters['by_birthday']['to'])) {
                if ('' == trim($filters['by_birthday']['from']) || '' == trim($filters['by_birthday']['to'])) {
                    unset($filters['by_birthday']);
                }
            }

            if (isset($filters['range_selection']['min'], $filters['range_selection']['max'])) {
                if ($filters['range_selection']['min'] && $filters['range_selection']['max']) {
                    $range_selection = $filters['range_selection'];
                }

                unset($filters['range_selection']);
            }
        }

        if (is_array($filters) && !empty($filters) || isset($range_selection)) {
            $sql = '';

            $filters_count = count($filters);
            $filters_index = 1;
            foreach ($filters as $type => $ids) {
                if ('languages' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' n.id_lang = '.(int) $id.' OR ';
                        } else {
                            $sql .= ' n.id_lang = '.(int) $id.' ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('shops' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' n.id_shop = '.(int) $id.' OR ';
                        } else {
                            $sql .= ' n.id_shop = '.(int) $id.' ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('gender' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' n.id_gender = '.(int) $id.' OR ';
                        } else {
                            $sql .= ' n.id_gender = '.(int) $id.' ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('subscribed' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' n.active = '.(int) $id.' OR ';
                        } else {
                            $sql .= ' n.active = '.(int) $id.' ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('by_interest' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;

                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            if (-1 == $id) {
                                $sql .= " n.`list_of_interest` IS NULL OR n.`list_of_interest` = '' OR ";
                            } else {
                                $sql .= ' FIND_IN_SET ('.(int) $id.', n.`list_of_interest`) OR ';
                                // $sql .= '  n.`list_of_interest` = '.(int)$id.' OR '; not ok
                            }
                        } else {
                            if (-1 == $id) {
                                $sql .= " n.`list_of_interest` IS NULL OR n.`list_of_interest` = '' ";
                            } else {
                                $sql .= ' FIND_IN_SET ('.(int) $id.', n.`list_of_interest`) ';
                                // $sql .= ' n.`list_of_interest` = '.(int)$id.' '; not ok
                            }
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('by_birthday' == $type) {
                    $only_month_day = true;

                    if ($only_month_day) {
                        $form = date('m-d', strtotime(str_replace('.', '-', $filters['by_birthday']['from'])));
                        $to = date('m-d', strtotime(str_replace('.', '-', $filters['by_birthday']['to'])));

                        $sql .= ' DATE_FORMAT(n.birthday, "%m-%d") >= "'.pSQL($form).'" AND DATE_FORMAT(n.birthday, "%m-%d") <= "'.pSQL($to).'" ';
                    } else {
                        $form = date('Y-m-d', strtotime(str_replace('.', '-', $filters['by_birthday']['from'])));
                        $to = date('Y-m-d', strtotime(str_replace('.', '-', $filters['by_birthday']['to'])));

                        $sql .= ' n.birthday >= "'.pSQL($form).'" AND n.birthday <= "'.pSQL($to).'" ';
                    }
                }

                if ($filters_index != $filters_count) {
                    $sql .= ' AND ';
                }

                ++$filters_index;
            }

            if (true == pqnp_config('VIEW_ACTIVE_ONLY') && $filters_count > 0) {
                $sql .= ' AND n.`active` = 1';
            } elseif (true == pqnp_config('VIEW_ACTIVE_ONLY')) {
                $sql .= ' n.`active` = 1';
            } else {
                $sql .= '';
            }

            $end = '';
            if (isset($range_selection)) {
                $lim_start = (int) $range_selection['min'];
                if ($lim_start < 0) {
                    $lim_start = 0;
                }

                $lim_end = (int) $range_selection['max'] - (int) $range_selection['min'];
                if ($lim_end < 0) {
                    $lim_end = 0;
                }

                $end = ' LIMIT '.(int) $lim_start.', '.(int) $lim_end.' ';
            }

            $sql = $this->getVisitorsNPSql($sql, $end);

            $userlist = Db::getInstance()->executeS($sql);

            foreach ($userlist as &$user) {
                $user['img_path'] = $this->getLangImageById($user['id_lang']);
            }

            return Tools::jsonEncode($userlist);
        }

        return $this->getVisitorsNP();
    }

    /**
     * Get visitors.
     *
     * @param bool $encode
     *
     * @return json/array
     */
    public function getVisitors($encode = true)
    {
        $sql = $this->getVisitorsSql();
        if ($sql) {
            $userlist = Db::getInstance()->executeS($sql);

            // $default_lang = (int)pqnp_config('PS_LANG_DEFAULT');

            foreach ($userlist as &$user) {
                $user['img_path'] = $this->getLangImageById($user['id_lang']);
            }

            if ($encode) {
                return Tools::jsonEncode($userlist);
            } else {
                return $userlist;
            }
        } else {
            if ($encode) {
                return Tools::jsonEncode([]);
            } else {
                return [];
            }
        }
    }

    /**
     * Get visitors sql.
     *
     * @param string $and
     * @param string $end
     *
     * @return string/boolean
     */
    private function getVisitorsSql($and = '', $end = '')
    {
        $table_name = NewsletterProDefaultNewsletterTable::getTableName();

        $defaultLang = (int) pqnp_config('PS_LANG_DEFAULT');

        if ($table_name) {
            $sql_shops_id = '';
            $get_active_shops_id = NewsletterProTools::getActiveShopsId();
            $subscribed = (int) pqnp_config('VIEW_ACTIVE_ONLY');

            foreach ($get_active_shops_id as $id_shop) {
                $sql_shops_id .= 'n.`id_shop` = '.(int) $id_shop.(end($get_active_shops_id) == $id_shop ? '' : ' OR ');
            }

            $sql = '
				SELECT n.`id`, n.`id_shop`,
					'.(NewsletterProDefaultNewsletterTable::hasIdLang() ? 'n.`id_lang`' : $defaultLang.' as `id_lang`').',
					n.`ip_registration_newsletter`, n.`email`, n.`active`, sh.`name` AS `shop_name`
				FROM `'._DB_PREFIX_.pSQL($table_name).'` n
				INNER JOIN `'._DB_PREFIX_.'shop` sh ON (n.`id_shop` = sh.`id_shop`)
				WHERE ('.$sql_shops_id.')
			';
            $sql .= ($subscribed ? ' AND n.`active` = '.(int) $subscribed : '');

            if (Tools::strlen(trim($and)) > 0) {
                $sql .= ' AND '.$and.' ';
            }

            $sql .= ' ORDER BY n.`id` ASC ';

            if (Tools::strlen(trim($end)) > 0) {
                $sql .= ' '.$end.' ';
            }

            $sql .= ';';

            return $sql;
        } else {
            return false;
        }
    }

    /**
     * Update visitor subscribed.
     *
     * @param int $id
     *
     * @return int
     */
    public function updateVisitor($id)
    {
        if (!($table_name = NewsletterProDefaultNewsletterTable::getTableName())) {
            return false;
        }

        parse_str(Tools::file_get_contents('php://input'), $put);
        $data = [
            'active' => (int) $put['active'],
        ];

        return (int) (Db::getInstance()->update($table_name, $data, "`id`={$id}"));
    }

    /**
     * Delete visitor subscribed.
     *
     * @param int $id
     *
     * @return int
     */
    public function deleteVisitor($id)
    {
        $tableName = NewsletterProDefaultNewsletterTable::getTableName();

        return (int) Db::getInstance()->delete($tableName, '`id`='.(int) $id, 1);
    }

    /**
     * Search visitors.
     *
     * @param string $value
     *
     * @return json
     */
    public function searchVisitor($value)
    {
        $search = ' ( n.`email` LIKE "%'.pSQL($value).'%" )';
        $sql = $this->getVisitorsSql($search);

        // $default_lang = (int)pqnp_config('PS_LANG_DEFAULT');
        $userlist = Db::getInstance()->executeS($sql);
        foreach ($userlist as &$user) {
            $user['img_path'] = $this->getLangImageById($user['id_lang']);
        }

        return Tools::jsonEncode($userlist);
    }

    /**
     * Filter visitor subscribed.
     *
     * @param array $filters
     *
     * @return json
     */
    public function filterVisitor($filters)
    {
        if (is_array($filters) && !empty($filters)) {
            if (isset($filters['range_selection']['min'], $filters['range_selection']['max'])) {
                if ($filters['range_selection']['min'] && $filters['range_selection']['max']) {
                    $range_selection = $filters['range_selection'];
                }

                unset($filters['range_selection']);
            }
        }

        if (is_array($filters) && !empty($filters) || isset($range_selection)) {
            $sql = '';

            $filters_count = count($filters);
            $filters_index = 1;
            foreach ($filters as $type => $ids) {
                if ('shops' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' n.id_shop = '.(int) $id.' OR ';
                        } else {
                            $sql .= ' n.id_shop = '.(int) $id.' ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('subscribed' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' n.active = '.(int) $id.' OR ';
                        } else {
                            $sql .= ' n.active = '.(int) $id.' ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                }

                if ($filters_index != $filters_count) {
                    $sql .= ' AND ';
                }

                ++$filters_index;
            }

            if (true == pqnp_config('VIEW_ACTIVE_ONLY') && $filters_count > 0) {
                $sql .= ' AND n.`active` = 1';
            } elseif (true == pqnp_config('VIEW_ACTIVE_ONLY')) {
                $sql .= ' n.`active` = 1';
            } else {
                $sql .= '';
            }

            $end = '';

            if (isset($range_selection)) {
                $lim_start = (int) $range_selection['min'];
                if ($lim_start < 0) {
                    $lim_start = 0;
                }

                $lim_end = (int) $range_selection['max'] - (int) $range_selection['min'];
                if ($lim_end < 0) {
                    $lim_end = 0;
                }

                $end = ' LIMIT '.(int) $lim_start.', '.(int) $lim_end.' ';
            }

            $sql = $this->getVisitorsSql($sql, $end);

            // $default_lang = (int)pqnp_config('PS_LANG_DEFAULT');

            $userlist = Db::getInstance()->executeS($sql);

            foreach ($userlist as &$user) {
                $user['img_path'] = $this->getLangImageById($user['id_lang']);
            }

            return Tools::jsonEncode($userlist);
        }

        return $this->getVisitors();
    }

    /**
     * Get customers.
     *
     * @param bool $encode
     *
     * @return json/array
     */
    public function getCustomers($encode = true)
    {
        $sql = $this->getCustomersSql([
            'subscribed' => (int) pqnp_config('VIEW_ACTIVE_ONLY'),
        ]);

        $userlist = Db::getInstance()->executeS($sql);

        foreach ($userlist as &$user) {
            $user['img_path'] = $this->getLangImageById($user['id_lang']);
        }

        if ($encode) {
            return Tools::jsonEncode($userlist);
        }

        return $userlist;
    }

    /**
     * Get customers sql statement.
     *
     * @param array $cfg
     *
     * @return string
     */
    public function getCustomersSql($cfg = [])
    {
        $select = null;
        $from = null;
        $join = null;
        $where = null;
        $and = null;
        $end = null;
        $subscribed = null;

        extract($cfg);

        $sql_shops_id = '';
        $get_active_shops_id = NewsletterProTools::getActiveShopsId();
        foreach ($get_active_shops_id as $id_shop) {
            $sql_shops_id .= 'c.`id_shop` = '.(int) $id_shop.(end($get_active_shops_id) == $id_shop ? '' : ' OR ');
        }

        $sql = 'SELECT 	c.`firstname`, c.`lastname`, c.`email`, c.`id_customer` AS `id`,
						c.`id_lang`, c.`id_shop`, c.`id_default_group`, c.`newsletter`, c.`id_gender`,
						sh.`name` AS `shop_name`,
						npcc.`categories`,
						loi.`categories` AS categories_loi,
						GROUP_CONCAT(cg.`id_group`) AS `id_group` ';
        $sql .= (isset($select) ? ', '.$select.' ' : '');
        $sql .= ' FROM `'._DB_PREFIX_.'customer` c ';
        $sql .= (isset($from) ? ' '.$from.' ' : '');
        $sql .= ' LEFT JOIN `'._DB_PREFIX_.'customer_group` cg ON ( cg.`id_customer` = c.`id_customer` )
				LEFT JOIN `'._DB_PREFIX_.'newsletter_pro_customer_category` npcc ON (c.`id_customer` = npcc.`id_customer`)
				LEFT JOIN `'._DB_PREFIX_.'lang` lg ON (c.`id_lang` = lg.`id_lang`)
				LEFT JOIN `'._DB_PREFIX_.'newsletter_pro_customer_list_of_interests` loi ON (c.id_customer = loi.id_customer)
				'.((bool) pqnp_ini_config('filter_customers_by_address') ? 'LEFT JOIN `'._DB_PREFIX_.'address` a ON (c.`id_customer` = a.`id_customer`)' : '').'
				INNER JOIN `'._DB_PREFIX_.'shop` sh ON (c.`id_shop` = sh.`id_shop`) AND ('.$sql_shops_id.') ';
        $sql .= (isset($join) ? ' '.$join.' ' : '');

        if (isset($where)) {
            $sql .= (isset($where) ? ' WHERE '.$where.' ' : ' WHERE 1 ');
        } elseif (isset($subscribed)) {
            $sql .= ((int) pqnp_config('VIEW_ACTIVE_ONLY') ? ' WHERE c.`newsletter` = '.(int) $subscribed : ' WHERE 1 ');
        } else {
            $sql .= ' WHERE 1 ';
        }

        $sql .= (isset($and) && trim($and) ? ' AND '.$and.' ' : '');
        $sql .= ' GROUP BY c.id_customer
				ORDER BY c.`id_customer` ASC ';
        $sql .= (isset($end) && trim($end) ? ' '.$end.' ' : '');
        $sql .= ';';

        return $sql;
    }

    /**
     * Search customer.
     *
     * @param string $value
     *
     * @return json
     */
    public function searchCustomer($value)
    {
        $search = ' ( c.`email` LIKE "%'.pSQL($value).'%" OR c.`firstname` LIKE "%'.pSQL($value).'%" OR c.`lastname` LIKE "%'.pSQL($value).'%" )';
        $sql = $this->getCustomersSql([
            'and' => $search,
            'subscribed' => (int) pqnp_config('VIEW_ACTIVE_ONLY'),
        ]);

        $userlist = Db::getInstance()->executeS($sql);

        foreach ($userlist as &$user) {
            $user['img_path'] = $this->getLangImageById($user['id_lang']);
        }

        return Tools::jsonEncode($userlist);
    }

    /**
     * Filter customer.
     *
     * @param array $filters
     *
     * @return json
     */
    public function filterCustomer($filters)
    {
        $countries_ids = [];

        if (is_array($filters) && !empty($filters)) {
            if (isset($filters['by_birthday']['from'], $filters['by_birthday']['to'])) {
                if ('' == trim($filters['by_birthday']['from']) || '' == trim($filters['by_birthday']['to'])) {
                    unset($filters['by_birthday']);
                }
            }

            if (isset($filters['range_selection']['min'], $filters['range_selection']['max'])) {
                if ($filters['range_selection']['min'] && $filters['range_selection']['max']) {
                    $range_selection = $filters['range_selection'];
                }

                unset($filters['range_selection']);
            }
        }

        if ((is_array($filters) && !empty($filters)) || isset($range_selection)) {
            $sql = '';

            $filters_count = count($filters);
            $filters_index = 1;
            foreach ($filters as $type => $ids) {
                if ('groups' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' cg.`id_group` = '.(int) $id.' OR '; // $sql .= ' FIND_IN_SET ('.(int)$id.', cg.`id_group`) OR ';
                        } else {
                            $sql .= ' cg.`id_group` = '.(int) $id.' '; // $sql .= ' FIND_IN_SET ('.(int)$id.', cg.`id_group`) ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('languages' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' c.id_lang = '.(int) $id.' OR ';
                        } else {
                            $sql .= ' c.id_lang = '.(int) $id.' ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('shops' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' c.id_shop = '.(int) $id.' OR ';
                        } else {
                            $sql .= ' c.id_shop = '.(int) $id.' ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('gender' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' c.id_gender = '.(int) $id.' OR ';
                        } else {
                            $sql .= ' c.id_gender = '.(int) $id.' ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('subscribed' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;
                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            $sql .= ' c.newsletter = '.(int) $id.' OR ';
                        } else {
                            $sql .= ' c.newsletter = '.(int) $id.' ';
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('by_interest' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;

                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            if (-1 == $id) {
                                $sql .= " loi.`categories` IS NULL OR loi.`categories` = '' OR ";
                            } else {
                                $sql .= ' FIND_IN_SET ('.(int) $id.', loi.`categories`) OR ';
                                // $sql .= '  n.`list_of_interest` = '.(int)$id.' OR '; not ok
                            }
                        } else {
                            if (-1 == $id) {
                                $sql .= " loi.`categories` IS NULL OR loi.`categories` = '' ";
                            } else {
                                $sql .= ' FIND_IN_SET ('.(int) $id.', loi.`categories`) ';
                                // $sql .= ' n.`list_of_interest` = '.(int)$id.' '; not ok
                            }
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('categories' == $type) {
                    $sql .= ' ( ';
                    $ids_count = count($ids);
                    $ids_index = 1;

                    foreach ($ids as $id) {
                        if ($ids_index != $ids_count) {
                            if (-1 == $id) {
                                $sql .= " npcc.`categories` IS NULL OR npcc.`categories` = '' OR ";
                            } else {
                                $sql .= ' npcc.`categories` = '.(int) $id.' OR '; // $sql .= ' FIND_IN_SET ('.(int)$id.', npcc.`categories`) OR ';
                            }
                        } else {
                            if (-1 == $id) {
                                $sql .= " npcc.`categories` IS NULL OR npcc.`categories` = '' ";
                            } else {
                                $sql .= ' npcc.`categories` = '.(int) $id.' '; // $sql .= ' FIND_IN_SET ('.(int)$id.', npcc.`categories`) ';
                            }
                        }

                        ++$ids_index;
                    }
                    $sql .= ' ) ';
                } elseif ('purchased_product' == $type) {
                    $start_date = trim($ids['startDate']);
                    $end_date = trim($ids['endDate']);

                    $order_date_sql = '';

                    if (Tools::strlen($start_date) > 0 && Tools::strlen($end_date) > 0) {
                        $order_date_sql = ' AND o.`date_add` >= "'.pSQL($start_date).'" AND o.`date_add` <= "'.pSQL($end_date).'" ';
                    } elseif (Tools::strlen($start_date) > 0) {
                        $order_date_sql = ' AND o.`date_add` >= "'.pSQL($start_date).'" ';
                    } elseif (Tools::strlen($end_date) > 0) {
                        $order_date_sql = ' AND o.`date_add` <= "'.pSQL($end_date).'" ';
                    }

                    if (array_key_exists('withoutPurchase', $ids) && filter_var($ids['withoutPurchase'], FILTER_VALIDATE_BOOLEAN)) {
                        $sql .= ' (
                            select count(*) from `'._DB_PREFIX_.'orders` o
                            WHERE o.`id_customer` = c.`id_customer`
                            '.$order_date_sql.'
                        ) = 0 ';
                    } else {
                        $ids = array_key_exists('ids', $ids) ? $ids['ids'] : [];

                        $sql .= ' c.`id_customer` IN ( ';

                        $ids_str = trim(implode(',', $ids), ',');

                        $sql .= '
                            SELECT o.`id_customer`
                            FROM `'._DB_PREFIX_.'orders` o
                            INNER JOIN `'._DB_PREFIX_.'order_detail` car ON (
                                o.`id_order` = car.`id_order`
                                AND car.`product_id` IN ('.pSQL($ids_str).')
                                    )
                            WHERE 1
                            '.$order_date_sql.'
                        ';

                        $sql .= ' ) ';
                    }
                } elseif ('by_birthday' == $type) {
                    $only_month_day = true;

                    if ($only_month_day) {
                        $form = date('m-d', strtotime(str_replace('.', '-', $filters['by_birthday']['from'])));
                        $to = date('m-d', strtotime(str_replace('.', '-', $filters['by_birthday']['to'])));

                        $sql .= ' DATE_FORMAT(c.birthday, "%m-%d") >= "'.pSQL($form).'" AND DATE_FORMAT(c.birthday, "%m-%d") <= "'.pSQL($to).'" ';
                    } else {
                        $form = date('Y-m-d', strtotime(str_replace('.', '-', $filters['by_birthday']['from'])));
                        $to = date('Y-m-d', strtotime(str_replace('.', '-', $filters['by_birthday']['to'])));

                        $sql .= ' c.birthday >= "'.pSQL($form).'" AND c.birthday <= "'.pSQL($to).'" ';
                    }
                } elseif ('total_spent' == $type) {
                    $sql .= '(
						(SELECT SUM(o.`total_paid_real` / o.`conversion_rate`)
							FROM `'._DB_PREFIX_.'orders` o WHERE o.`id_customer` = c.`id_customer` '.Shop::addSqlRestriction(Shop::SHARE_ORDER, 'o').') >= '.(int) $filters['total_spent']['from'].'
						AND
						(SELECT SUM(o.`total_paid_real` / o.`conversion_rate`) 
							FROM `'._DB_PREFIX_.'orders` o WHERE o.`id_customer` = c.`id_customer` '.Shop::addSqlRestriction(Shop::SHARE_ORDER, 'o').') <= '.(int) $filters['total_spent']['to'].'
						)	
					';
                } elseif ('filter_by_country' == $type) {
                    $ids_count = count($ids);
                    $ids_index = 0;

                    $sql .= ' ( ';
                    foreach ($ids as $iso_code) {
                        if ((bool) pqnp_ini_config('filter_customers_by_address')) {
                            if (!array_key_exists($iso_code, $countries_ids)) {
                                $countries_ids[$iso_code] = (int) Country::getByIso($iso_code);
                            }

                            $countryId = (int) $countries_ids[$iso_code];

                            if ($ids_index < $ids_count - 1) {
                                $sql .= ' (a.`id_country` = '.(int) $countryId.') OR ';
                            } else {
                                $sql .= ' (a.`id_country` = '.(int) $countryId.') ';
                            }
                        } else {
                            if ($ids_index < $ids_count - 1) {
                                $sql .= ' LOWER(lg.`language_code`) LIKE "%-'.pSQL(Tools::strtolower($iso_code)).'" OR ';
                            } else {
                                $sql .= ' LOWER(lg.`language_code`) LIKE "%-'.pSQL(Tools::strtolower($iso_code)).'" ';
                            }
                        }

                        ++$ids_index;
                    }

                    $sql .= ' ) ';
                }

                if ($filters_index != $filters_count) {
                    $sql .= ' AND ';
                }

                ++$filters_index;
            }

            if (true == pqnp_config('VIEW_ACTIVE_ONLY') && $filters_count > 0) {
                $sql .= ' AND c.`newsletter` = 1';
            } elseif (true == pqnp_config('VIEW_ACTIVE_ONLY')) {
                $sql .= ' c.`newsletter` = 1';
            } else {
                $sql .= '';
            }

            $sql_filter = [
                'and' => $sql,
                'subscribed' => (int) pqnp_config('VIEW_ACTIVE_ONLY'),
            ];

            if (isset($range_selection)) {
                $lim_start = (int) $range_selection['min'];
                if ($lim_start < 0) {
                    $lim_start = 0;
                }

                $lim_end = (int) $range_selection['max'] - (int) $range_selection['min'];
                if ($lim_end < 0) {
                    $lim_end = 0;
                }

                $sql_filter['end'] = ' LIMIT '.(int) $lim_start.', '.(int) $lim_end.' ';
            }

            $sql = $this->getCustomersSql($sql_filter);

            $userlist = Db::getInstance()->executeS($sql);

            foreach ($userlist as &$user) {
                $user['img_path'] = $this->getLangImageById($user['id_lang']);
            }

            return Tools::jsonEncode($userlist);
        }

        return $this->getCustomers();
    }

    /**
     * Get language image by id.
     *
     * @param int $id
     *
     * @return string
     */
    public function getLangImageById($id)
    {
        $path = '';
        if (file_exists($this->lang_img_dir.$id.'.jpg')) {
            $path = $this->lang_img_path.$id.'.jpg';
        } else {
            $path = $this->lang_img_path.'none.jpg';
        }

        $path = $this->relplaceAdminLink($path);

        return $path;
    }

    /**
     * Update customer by id.
     *
     * @param int $id
     *
     * @return int
     */
    public function updateCustomer($id)
    {
        parse_str(Tools::file_get_contents('php://input'), $put);

        $data = [
            'newsletter' => (int) $put['newsletter'],
        ];

        return (int) (Db::getInstance()->update('customer', $data, '`id_customer`= '.(int) $id));
    }

    /**
     * Delete customer by id.
     *
     * @param int $id
     *
     * @return int
     */
    public function deleteCustomer($id)
    {
        if ((int) pqnp_ini_config('demo_mode')) {
            exit('This is a demo, it is not possible to delete the customer.');
        }

        $customer = new Customer($id);
        if (null != $customer->id) {
            return (int) $customer->delete();
        }

        return 0;
    }

    /**
     * Get tasks.
     *
     * @return json
     */
    public function getTasks()
    {
        $sql = 'SELECT * FROM `'._DB_PREFIX_.'newsletter_pro_task` t
				WHERE `done` = 0
				ORDER BY t.`date_start` ASC';
        if ($tasks = Db::getInstance()->executeS($sql)) {
            $current_day_time = strtotime(date('Y-m-d'));

            foreach ($tasks as &$task) {
                $status = &$task['status'];
                $date_start = strtotime($task['date_start']);

                $task['error_msg'] = NewsletterProTools::unSerialize($task['error_msg']);

                if (1 === (int) $status) {
                    $status = 1;
                } elseif ($current_day_time === $date_start) {
                    $status = $this->l('today');
                } elseif (($current_day_time + 24 * 60 * 60) === $date_start) {
                    $status = $this->l('tomorrow');
                } elseif ($current_day_time > $date_start) {
                    $status = $this->l('in the past');
                } else {
                    $status = '';
                }
            }

            return Tools::jsonEncode($tasks);
        } else {
            return Tools::jsonEncode([]);
        }
    }

    public function getSubscriptionConsent()
    {
        $results = Db::getInstance()->executeS('
			SELECT id_newsletter_pro_subscription_consent,
					email,
					subscribed,
					ip_address,
					consent_date,
					date_add,
					date_upd
			FROM `'._DB_PREFIX_.'newsletter_pro_subscription_consent` sc
			WHERE `id_newsletter_pro_subscription_consent` = (
				SELECT MAX(`id_newsletter_pro_subscription_consent`) 
				FROM `'._DB_PREFIX_.'newsletter_pro_subscription_consent` msc
				WHERE msc.`email` = sc.`email`
			)
		');

        return Tools::jsonEncode($results);
    }

    public function searchSubscriptionConsent($value)
    {
        $results = Db::getInstance()->executeS('
			SELECT id_newsletter_pro_subscription_consent,
					email,
					subscribed,
					ip_address,
					consent_date,
					date_add,
					date_upd
			FROM `'._DB_PREFIX_.'newsletter_pro_subscription_consent` sc
			WHERE `id_newsletter_pro_subscription_consent` = (
				SELECT MAX(`id_newsletter_pro_subscription_consent`) 
				FROM `'._DB_PREFIX_.'newsletter_pro_subscription_consent` msc
				WHERE msc.`email` = sc.`email`
				AND email LIKE "%'.pSQL($value).'%" OR email = "'.pSQL(Tools::encrypt($value)).'"
			)
		');

        return Tools::jsonEncode($results);
    }

    public function getSubscriptionConsentDetails($email)
    {
        $response = new NewsletterProAjaxResponse([
            'data' => [],
        ]);

        $results = Db::getInstance()->executeS('
			SELECT * from `'._DB_PREFIX_.'newsletter_pro_subscription_consent`
			WHERE `email` = "'.pSQL($email).'"
			ORDER BY `id_newsletter_pro_subscription_consent` desc
		');

        $response->set('data', $results);

        return $response->display();
    }

    public function deleteSubscriptionConsent($email)
    {
        $response = new NewsletterProAjaxResponse([]);

        NewsletterProSubscriptionConsent::deleteByEmail($email);

        return $response->display();
    }

    public function searchPrivacyData($email)
    {
        $response = new NewsletterProAjaxResponse([
            'data' => null,
        ]);

        $privacy_data = new NewsletterProPrivacyData();

        $data = $privacy_data->search($email);

        $response->set('data', $data);

        return $response->display();
    }

    public function clearPrivacyData($email)
    {
        $response = new NewsletterProAjaxResponse([
            'data' => null,
        ]);

        $privacy_data = new NewsletterProPrivacyData();

        $data = $privacy_data->clear($email);

        $response->set('data', $data);

        return $response->display();
    }

    /**
     * Render the template history.
     *
     * @param int $id_history
     *
     * @return json
     */
    public function renderTemplateHistory($id_history)
    {
        $url = $this->getTemplateHistoryUrl($id_history);
        // viewInBrowser will remove the opened email tracking if the browser is viewed by admin
        $url .= '&viewInBrowser=1';
        ;
        return Tools::jsonEncode([
            'downloadUri' => NewsletterProTools::getUri([
                'exportTemplateHistory' => true,
                'idTemplateHistory' => (int) $id_history,
            ]),
            'url' => $url,
            // jQueryNoConflict is important
            'content' => Tools::file_get_contents($url.'&jQueryNoConflict'),
        ]);
    }

    /**
     * Get template history url.
     *
     * @param int $id_history
     *
     * @return string
     */
    public function getTemplateHistoryUrl($id_history)
    {
        return urldecode(Context::getContext()->link->getModuleLink($this->name, 'newsletter', [
            'token_tpl' => $this->getTokenByIdHistory((int) $id_history),
            'email' => pqnp_config('PS_SHOP_EMAIL'),
        ]));
    }

    /**
     * Get token by id hisotry.
     *
     * @param int $id_history
     *
     * @return int
     */
    public function getTokenByIdHistory($id_history)
    {
        return Db::getInstance()->getValue('SELECT `token` FROM `'._DB_PREFIX_.'newsletter_pro_tpl_history` WHERE `id_newsletter_pro_tpl_history` = '.(int) $id_history);
    }

    /**
     * Get hisotry id by token.
     *
     * @param string $token
     *
     * @return int
     */
    public function getHistoryIdByToken($token)
    {
        return (int) Db::getInstance()->getValue('SELECT `id_newsletter_pro_tpl_history` FROM `'._DB_PREFIX_.'newsletter_pro_tpl_history` WHERE `token` = "'.pSQL($token).'"');
    }

    /**
     * Get task history.
     *
     * @return json
     */
    public function getTasksHistory()
    {
        $sql = 'SELECT t.`id_newsletter_pro_task`,
				t.`id_newsletter_pro_smtp`,
				t.`id_newsletter_pro_tpl_history`,
				t.`date_start`,
				t.`active`,
				t.`template`,
				t.`status`,
				t.`sleep`,
				t.`emails_count`,
				t.`emails_error`,
				t.`emails_success`,
				t.`emails_completed`,
				t.`done`,
				t.`error_msg`,
				h.`clicks`,
				h.`opened`,
				h.`unsubscribed`,
				h.`fwd_unsubscribed`,
				GROUP_CONCAT(ts.`id_newsletter_pro_task_step`) AS `steps`
				FROM `'._DB_PREFIX_.'newsletter_pro_task` t
				LEFT JOIN `'._DB_PREFIX_.'newsletter_pro_task_step` ts
					ON (ts.`id_newsletter_pro_task` = t.`id_newsletter_pro_task`)

				LEFT JOIN `'._DB_PREFIX_.'newsletter_pro_tpl_history` h
					ON (t.`id_newsletter_pro_tpl_history` = h.`id_newsletter_pro_tpl_history`)

				WHERE t.`done` = 1
				GROUP BY t.`id_newsletter_pro_task`
				ORDER BY t.`date_start` DESC;';

        if ($tasks = Db::getInstance()->executeS($sql)) {
            foreach ($tasks as &$task) {
                $task['date_start'] = date('Y-m-d', strtotime($task['date_start']));
                $task['error_msg'] = unserialize($task['error_msg']);
                $task['template'] = Tools::ucfirst(pathinfo($task['template'], PATHINFO_FILENAME));
            }

            return Tools::jsonEncode($tasks);
        } else {
            return Tools::jsonEncode([]);
        }
    }

    /**
     * Get unsubscribed details.
     *
     * @param int $id_newsletter
     *
     * @return string
     */
    public function getUnsubscribedDetails($id_newsletter)
    {
        $sql = 'SELECT * FROM `'._DB_PREFIX_.'newsletter_pro_unsibscribed` WHERE `id_newsletter_pro_tpl_history` = '.(int) $id_newsletter;
        $result = Db::getInstance()->executeS($sql);

        $this->context->smarty->assign([
            'result' => $result,
        ]);

        return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/task/unsubscribed_detail.tpl'));
    }

    /**
     * Get the task unsubscribed details.
     *
     * @param int $id_newsletter
     *
     * @return string
     */
    public function getTaskUnsubscribedDetails($id_newsletter)
    {
        return $this->getUnsubscribedDetails($id_newsletter);
    }

    /**
     * Get the task forwarders unsubscribed details.
     *
     * @param int $id_newsletter
     *
     * @return string
     */
    public function getTaskFwdUnsubscribedDetails($id_newsletter)
    {
        return $this->getFwdUnsubscribedDetails($id_newsletter);
    }

    /**
     * Get forwarders subscribed details.
     *
     * @param int $id_newsletter
     *
     * @return string
     */
    public function getFwdUnsubscribedDetails($id_newsletter)
    {
        $sql = 'SELECT * FROM `'._DB_PREFIX_.'newsletter_pro_fwd_unsibscribed` WHERE `id_newsletter_pro_tpl_history` = '.(int) $id_newsletter;
        $result = Db::getInstance()->executeS($sql);

        $this->context->smarty->assign([
            'result' => $result,
        ]);

        return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/task/unsubscribed_detail.tpl'));
    }

    /**
     * Get forwarders details.
     *
     * @param string $email
     *
     * @return string
     */
    public function getForwarderDetails($email)
    {
        $sql = 'SELECT * FROM `'._DB_PREFIX_.'newsletter_pro_forward` WHERE `from` = "'.pSQL($email).'" ;';
        $result = Db::getInstance()->executeS($sql);

        $this->context->smarty->assign([
            'result' => $result,
        ]);

        return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/task/forwarder_detail.tpl'));
    }

    /**
     * Get tasks history detail.
     *
     * @param int $id_step
     *
     * @return string
     */
    public function getTasksHistoryDetail($id_step)
    {
        $sql = 'SELECT * FROM `'._DB_PREFIX_.'newsletter_pro_task_step` WHERE `id_newsletter_pro_task_step` = '.(int) $id_step;
        $step = Db::getInstance()->getRow($sql);
        if ($step) {
            $step['emails_to_send'] = NewsletterProTools::unSerialize($step['emails_to_send']);
            $step['emails_sent'] = NewsletterProTools::unSerialize($step['emails_sent']);
        }
        $this->context->smarty->assign([
            'step' => $step,
        ]);

        return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/task/task_history_detail.tpl'));
    }

    /**
     * Get send history details.
     *
     * @param int $id_step
     *
     * @return string
     */
    public function getSendHistoryDetail($id_step)
    {
        $sql = 'SELECT * FROM `'._DB_PREFIX_.'newsletter_pro_send_step` WHERE `id_newsletter_pro_send_step` = '.(int) $id_step;
        $step = Db::getInstance()->getRow($sql);
        if ($step) {
            $step['emails_to_send'] = NewsletterProTools::unSerialize($step['emails_to_send']);
            $step['emails_sent'] = NewsletterProTools::unSerialize($step['emails_sent']);
        }
        $this->context->smarty->assign([
            'step' => $step,
        ]);

        return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/task/send_history_detail.tpl'));
    }

    /**
     * Clear task history.
     *
     * @return json
     */
    public function clearTaskHistory()
    {
        $response = ['status' => false, 'msg' => ''];

        $sql = 'SELECT `id_newsletter_pro_tpl_history` AS `id`
				FROM `'._DB_PREFIX_.'newsletter_pro_task`
				WHERE `done` = 1
				AND `id_newsletter_pro_tpl_history` > 0;';

        if ($ids = Db::getInstance()->executeS($sql)) {
            foreach ($ids as $id) {
                Db::getInstance()->delete('newsletter_pro_tpl_history', '`id_newsletter_pro_tpl_history`='.(int) $id['id']);
                Db::getInstance()->delete('newsletter_pro_unsibscribed', '`id_newsletter_pro_tpl_history`='.(int) $id['id']);
            }
        }

        $sql = 'DELETE t.*, ts.*
				FROM `'._DB_PREFIX_.'newsletter_pro_task` t
				LEFT JOIN `'._DB_PREFIX_.'newsletter_pro_task_step` ts ON ts.`id_newsletter_pro_task` = t.`id_newsletter_pro_task`
				WHERE t.`done` = 1;';

        if (Db::getInstance()->execute($sql)) {
            $response['status'] = true;
        } else {
            $response['msg'] = $this->l('The task history cannot be deleted!');
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Clear statistics.
     *
     * @return json
     */
    public function clearStatistics()
    {
        $errors = [];
        $response = ['status' => false, 'errors' => &$errors];

        $sql = 'DELETE FROM `'._DB_PREFIX_.'newsletter_pro_statistics` WHERE 1';

        if (!Db::getInstance()->execute($sql)) {
            $errors[] = $this->l('The statistics cannot be deleted!');
        }

        if (empty($errors)) {
            $response['status'] = true;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Clear send history.
     *
     * @return json
     */
    public function clearSendHistory()
    {
        $response = ['status' => false, 'msg' => ''];

        $sql = 'DELETE s.*, ss.*
			FROM `'._DB_PREFIX_.'newsletter_pro_send` s
			LEFT JOIN `'._DB_PREFIX_.'newsletter_pro_send_step` ss ON ss.`id_newsletter_pro_send` = s.`id_newsletter_pro_send`
			WHERE s.`active` = 0;';

        if (Db::getInstance()->execute($sql)) {
            $response['status'] = true;
        } else {
            $response['msg'] = $this->l('The history cannot be deleted!');
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Delete task by id.
     *
     * @param int $id
     *
     * @return int
     */
    public function deleteTask($id)
    {
        $sql = 'SELECT `id_newsletter_pro_tpl_history` FROM `'._DB_PREFIX_.'newsletter_pro_task` WHERE `id_newsletter_pro_task`='.(int) $id;

        if ($id_history = Db::getInstance()->getValue($sql)) {
            Db::getInstance()->delete('newsletter_pro_tpl_history', '`id_newsletter_pro_tpl_history`='.(int) $id_history, 1);
            Db::getInstance()->delete('newsletter_pro_unsibscribed', '`id_newsletter_pro_tpl_history`='.(int) $id_history, 1);
        }

        return (int) (Db::getInstance()->delete('newsletter_pro_task', '`id_newsletter_pro_task`='.(int) $id, 1)
                && Db::getInstance()->delete('newsletter_pro_task_step', '`id_newsletter_pro_task`='.(int) $id));
    }

    /**
     * Delete send history by id.
     *
     * @param int $id
     *
     * @return int
     */
    public function deleteSendHistory($id)
    {
        $sql = 'SELECT `id_newsletter_pro_tpl_history` FROM `'._DB_PREFIX_.'newsletter_pro_send` WHERE `id_newsletter_pro_send`='.(int) $id;

        if ($id_history = Db::getInstance()->getValue($sql)) {
            Db::getInstance()->delete('newsletter_pro_tpl_history', '`id_newsletter_pro_tpl_history`='.(int) $id_history, 1);
            Db::getInstance()->delete('newsletter_pro_unsibscribed', '`id_newsletter_pro_tpl_history`='.(int) $id_history, 1);
        }

        return (int) (Db::getInstance()->delete('newsletter_pro_send', '`id_newsletter_pro_send`='.(int) $id, 1)
                && Db::getInstance()->delete('newsletter_pro_send_step', '`id_newsletter_pro_send`='.(int) $id));
    }

    /**
     * Update task.
     *
     * @param int $id
     *
     * @return int
     */
    public function updateTask($id)
    {
        parse_str(Tools::file_get_contents('php://input'), $put);
        $data = [
            'id_newsletter_pro_smtp' => (int) $put['id_newsletter_pro_smtp'],
            'send_method' => pSQL($put['send_method']),
            'date_start' => pSQL($put['date_start']),
            'template' => addcslashes($put['template'], "'"),
            'active' => (int) $put['active'],
        ];

        $template = NewsletterProTemplate::newFile($put['template'])->load();
        $history = NewsletterProTplHistory::newInstance($put['id_newsletter_pro_tpl_history']);

        $history->template = $template->html(NewsletterProTemplateContent::CONTENT_HTML, true);
        $history->active = (bool) $put['active'];
        $history->template_name = $template->name;

        return (int) (Db::getInstance()->update('newsletter_pro_task', $data, '`id_newsletter_pro_task`= '.(int) $id) && $history->update());
    }

    /**
     * Remove duplicate emails.
     *
     * @param string $emails
     *
     * @return array
     */
    public function removeDuplicateEmails($emails)
    {
        $emails_return = [];
        foreach ($emails as $email) {
            if (!in_array($email, $emails_return)) {
                $emails_return[] = $email;
            }
        }

        return $emails_return;
    }

    /**
     * Create a new task.
     *
     * @param json $data
     */
    public function addTask($data)
    {
        $post = &$_POST;

        $emails = Tools::jsonEncode([]);

        if (isset($post['emails'])) {
            $emails = $post['emails'];
        }

        $emails = Tools::jsonDecode($emails);

        $response = ['status' => false, 'errors' => &$this->_errors];

        $time = (int) strtotime($data['mysql_date']);

        if (0 == $time) {
            $this->_errors[] = $this->l('The date is not valid!');
        }
        if (empty($emails)) {
            $this->_errors[] = $this->l('No email was selected!');
        }
        if (!trim($data['template'])) {
            $this->_errors[] = $this->l('Invalid template!');
        }
        if (!(int) $data['id_newsletter_pro_smtp'] && 'smtp' == $data['send_method']) {
            $this->_errors[] = $this->l('The smtp does not exists!');
        }
        if ((int) $data['sleep'] < 0) {
            $this->_errors[] = $this->l('Sleep time is not a valid integer!');
        }

        $emails = $this->removeDuplicateEmails($emails);

        if (empty($this->_errors)) {
            $step = $this->step;
            $last_tpl_id = 0;

            $template = NewsletterProTemplate::newFile($data['template'])->load();

            $history = NewsletterProTplHistory::newFromTemplate($template);
            $history->add();

            if (Db::getInstance()->insert('newsletter_pro_task', [
                'id_newsletter_pro_smtp' => (int) $data['id_newsletter_pro_smtp'],
                'id_newsletter_pro_tpl_history' => (int) $history->id,
                'send_method' => pSQL($data['send_method']),
                'started' => 0,
                'template' => addcslashes($data['template'], "'"),
                'date_start' => pSQL($data['mysql_date']),
                'date_modified' => pSQL(date('Y-m-d H:i:s')),
                'active' => 1,
                'status' => 0,
                'sleep' => (int) $data['sleep'],
                'emails_count' => count($emails),
                'error_msg' => NewsletterProTools::dbSerialize([]),
            ])) {
                $id_task = (int) Db::getInstance()->Insert_ID();

                $emails_chuck = array_chunk($emails, $step);

                foreach ($emails_chuck as $i => $emails_list) {
                    $task_smtp = [
                        'id_newsletter_pro_task' => $id_task,
                        'step' => ++$i,
                        'step_active' => 1,
                        'emails_to_send' => NewsletterProTools::dbSerialize($emails_list),
                        'emails_sent' => NewsletterProTools::dbSerialize([]),
                        'date' => date('Y-m-d H:i:s'),
                    ];
                    Db::getInstance()->insert('newsletter_pro_task_step', $task_smtp);
                }
                $response['status'] = true;
            } else {
                $this->_errors[] = $this->l('The task cannot be added to the database!');
            }
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Select all in progress task
     * The returned data must be identical with the getTasks function.
     *
     * @return array/boolean
     */
    public function getTasksInProgress($look_for = [])
    {
        if (!is_array($look_for)) {
            $look_for = [];
        }

        $sql = 'SELECT * FROM `'._DB_PREFIX_.'newsletter_pro_task`
				WHERE `status` = 1
				AND `done` = 0
				ORDER BY `date_start` ASC;';

        $result_look = false;
        if (!empty($look_for)) {
            $sql_look = 'SELECT * FROM `'._DB_PREFIX_.'newsletter_pro_task`
						WHERE `id_newsletter_pro_task` IN ('.implode(', ', $look_for).');';

            $result_look = Db::getInstance()->executeS($sql_look);
        }

        $result = Db::getInstance()->executeS($sql);

        if ($result || $result_look) {
            if (is_array($result)) {
                foreach ($result as &$row) {
                    $row['error_msg'] = NewsletterProTools::unSerialize($row['error_msg']);
                }
            }

            return Tools::jsonEncode([
                'result' => is_array($result) ? $result : [],
                'result_look' => is_array($result_look) ? array_values($result_look) : [],
            ]);
        } else {
            try {
                $sql = 'SELECT count(*) AS `count` FROM `'._DB_PREFIX_.'newsletter_pro_task`
						WHERE `status` = 0
						AND `done` = 0;';

                if ($count = Db::getInstance()->getValue($sql)) {
                    return (int) $count;
                }
            } catch (Exception $e) {
                return (int) false;
            }
        }

        return (int) false;
    }

    /**
     * Continue tasks.
     *
     * @param int $id
     *
     * @return int
     */
    public function continueTaskAjax($id)
    {
        if (Db::getInstance()->update('newsletter_pro_task', [
            'status' => 0,
            'done' => 0,
            'pause' => 0,
            ], 'id_newsletter_pro_task = '.(int) $id)) {
            $this->sendTaskAjax($id);

            return 1;
        }

        return 0;
    }

    /**
     * Send one task runned in background.
     *
     * @param int  $id
     * @param bool $jump_next
     */
    public function sendTaskAjax($id)
    {
        $task = NewsletterProTask::newInstance($id);
        if (Validate::isLoadedObject($task)) {
            $task->sendTaskAjax();
        }
    }

    /**
     * Pause task.
     *
     * @param int $id
     *
     * @return int
     */
    public function pauseTask($id)
    {
        return NewsletterProTask::newInstance($id)->pauseTask();
    }

    /**
     * Get all smtp connections.
     *
     * @return array
     */
    public function getAllSMTP()
    {
        $result = NewsletterProMail::getAllMails();

        $selected = pqnp_config('SMTP');
        if (!empty($result)) {
            if (!$selected) {
                $result[0]['selected'] = true;
            } else {
                $selected_exist = false;
                foreach ($result as &$value) {
                    if ($value['id_newsletter_pro_smtp'] == $selected) {
                        $selected_exist = true;
                        $value['selected'] = true;
                        break;
                    }
                }

                if (!$selected_exist) {
                    pqnp_config('SMTP', $result[0]['id_newsletter_pro_smtp']);
                    $result[0]['selected'] = true;
                }
            }
        }

        return $result;
    }

    /**
     * Get all smtp connections as a json format.
     *
     * @return json
     */
    public function getAllSMTPJson()
    {
        return Tools::jsonEncode($this->getAllSMTP());
    }

    /**
     * Get category tree.
     *
     * @return array
     */
    public function getCategoryTree()
    {
        $root = Category::getRootCategory();
        $tab_root = ['id_category' => $root->id, 'name' => $root->name];

        $category_tree = $this->renderCategoryTree([
            'root' => $tab_root,
            'selected_cat' => [],
            'input_name' => 'categoryBox',
            'use_radio' => false,
            'disabled_categories' => [],
            'use_search' => true,
            'use_in_popup' => false,
            'use_shop_context' => true,
            'option_no_decide' => true,
            'ajax_request_url' => Context::getContext()->link->getModuleLink($this->name, 'ajax', []),
        ]);

        return $category_tree;
    }

    /**
     * Set controller.
     */
    private function setController()
    {
        require_once $this->dir_location.'AdminNewsletterPro.php';
        $this->context->controller = new AdminNewsletterPro();
    }

    /**
     * Get the top catetory.
     *
     * @param int $id_lang
     *
     * @return object
     */
    public static function getTopCategory($id_lang = null)
    {
        if (method_exists('Category', 'getTopCategory')) {
            return Category::getTopCategory($id_lang);
        }

        if (is_null($id_lang)) {
            $id_lang = Context::getContext()->language->id;
        }

        $id_category = Db::getInstance()->getValue('
			SELECT `id_category`
			FROM `'._DB_PREFIX_.'category`
			WHERE `id_parent` = 0
		');

        return new Category($id_category, $id_lang);
    }

    /**
     * @Depracated ( Disabled )
     * Add the wright javascript tree plugin files
     *
     * @param object $controller
     */
    public function addTreeViewFiles($controller)
    {
        $jquery_plugins_foldername = _PS_ROOT_DIR_.'/js/jquery/plugins';

        if (file_exists($jquery_plugins_foldername.'/treeview-categories')) {
            $controller->addCSS(_PS_JS_DIR_.'jquery/plugins/treeview-categories/jquery.treeview-categories.css');
            $controller->addJs([
                _PS_JS_DIR_.'jquery/plugins/treeview-categories/jquery.treeview-categories.js',
                _PS_JS_DIR_.'jquery/plugins/treeview-categories/jquery.treeview-categories.async.js',
                _PS_JS_DIR_.'jquery/plugins/treeview-categories/jquery.treeview-categories.edit.js',
            ]);
        } else {
            $controller->addCSS(_PS_JS_DIR_.'jquery/plugins/treeview/jquery.treeview.css');
            $controller->addJs([
                _PS_JS_DIR_.'jquery/plugins/treeview/jquery.treeview.js',
                _PS_JS_DIR_.'jquery/plugins/treeview/jquery.treeview.async.js',
                _PS_JS_DIR_.'jquery/plugins/treeview/jquery.treeview.edit.js',
            ]);
        }
    }

    /**
     * Render category tree.
     *
     * @param array $cfg
     *
     * @return string
     */
    public function renderCategoryTree($cfg)
    {
        $root = isset($cfg['root']) ? $cfg['root'] : null;
        $selected_cat = isset($cfg['selected_cat']) ? $cfg['selected_cat'] : [];
        $input_name = isset($cfg['input_name']) ? $cfg['input_name'] : 'categoryBox';
        $use_radio = isset($cfg['use_radio']) ? $cfg['use_radio'] : false;
        $use_search = isset($cfg['use_search']) ? $cfg['use_search'] : false;
        $disabled_categories = isset($cfg['disabled_categories']) ? $cfg['disabled_categories'] : [];
        $use_in_popup = isset($cfg['use_in_popup']) ? $cfg['use_in_popup'] : false;
        $use_shop_context = isset($cfg['use_shop_context']) ? $cfg['use_shop_context'] : false;
        $ajax_request_url = isset($cfg['ajax_request_url']) ? $cfg['ajax_request_url'] : 'ajax.php';

        $option_no_decide = isset($cfg['option_no_decide']) ? $cfg['option_no_decide'] : false;

        if (!property_exists($this->context, 'controller') || !isset($this->context->controller)) {
            $this->setController();
        }

        $translations = [
            'selected' => $this->l('Selected'),
            'Collapse All' => $this->l('Collapse All'),
            'Expand All' => $this->l('Expand All'),
            'Check All' => $this->l('Check All'),
            'Uncheck All' => $this->l('Uncheck All'),
            'search' => $this->l('Find a category'),
        ];

        $top_category = NewsletterPro::getTopCategory();
        if (Tools::isSubmit('id_shop')) {
            $id_shop = Tools::getValue('id_shop');
        } else {
            if (Context::getContext()->shop->id) {
                $id_shop = Context::getContext()->shop->id;
            } else {
                if (!Shop::isFeatureActive()) {
                    $id_shop = Configuration::get('PS_SHOP_DEFAULT');
                } else {
                    $id_shop = 0;
                }
            }
        }

        $shop = new Shop($id_shop);
        $root_category = Category::getRootCategory(null, $shop);
        $disabled_categories[] = $top_category->id;
        if (!$root) {
            $root = ['name' => $root_category->name, 'id_category' => $root_category->id];
        }

        if (!$use_radio) {
            $input_name = $input_name.'[]';
        }

        $this->addTreeViewFiles($this->context->controller);

        $this->context->controller->addJs([
            $this->uri_location.'views/js/categories-tree.js',
        ]);

        if ($use_search) {
            $this->context->controller->addJs(_PS_JS_DIR_.'jquery/plugins/autocomplete/jquery.autocomplete.js');
        }

        $selected_cat_var = '';
        if (count($selected_cat) > 0) {
            if (isset($selected_cat[0])) {
                $selected_cat_var = (int) implode(',', $selected_cat);
            } else {
                $selected_cat_var = (int) implode(',', array_keys($selected_cat));
            }
        }

        $content = '';
        $home_is_selected = false;
        foreach ($selected_cat as $cat) {
            if (is_array($cat)) {
                $disabled = in_array($cat['id_category'], $disabled_categories);
                if ($cat['id_category'] != $root['id_category']) {
                    $content .= '<input '.($disabled ? 'disabled="disabled"' : '').' type="hidden" name="'.$input_name.'" value="'.$cat['id_category'].'" >';
                } else {
                    $home_is_selected = true;
                }
            } else {
                $disabled = in_array($cat, $disabled_categories);
                if ($cat != $root['id_category']) {
                    $content .= '<input '.($disabled ? 'disabled="disabled"' : '').' type="hidden" name="'.$input_name.'" value="'.$cat.'" >';
                } else {
                    $home_is_selected = true;
                }
            }
        }

        $this->context->smarty->assign([
            'input_name' => addcslashes($input_name, "'\""),
            'selected_cat' => $selected_cat_var,
            'selected_label' => addcslashes($translations['selected'], "'\""),
            'home' => addcslashes($root['name'], "'\""),
            'use_radio' => $use_radio,
            'use_search' => $use_search,
            'use_in_popup' => $use_in_popup,
            'use_shop_context' => $use_shop_context,
            'root' => $root,
            'content' => $content,
            'root_input' => ($root['id_category'] != $top_category->id || (Tools::isSubmit('ajax') && 'getCategoriesFromRootCategory' == Tools::getValue('action'))),
            'home_is_selected' => $home_is_selected,
            'ajax_request_url' => $ajax_request_url,
            'option_no_decide' => $option_no_decide,
        ]);

        return $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/front/category_tree.tpl'));
    }

    /**
     * Check if the campaign is running properly.
     *
     * @return json
     */
    public function checkIfCampaignIsRunning()
    {
        $errors = [];
        $response = ['status' => false, 'errors' => &$errors, 'msg' => ''];

        try {
            $curl = new NewsletterProCurl();

            $params = [
                'newsletterpro_source' => 'newsletter',
                'utm_source' => 'testCampaign',
            ];

            $index_url = $this->context->link->getPageLink('index');

            $result = $curl->request('GET', $curl->url($index_url, 'html'), $params);

            if (false !== strpos($result['response'], '[Debug]')) {
                preg_match('/("|\')(?P<url>.*newsletterpro_source=newsletter\&utm_source=testCampaign.*?)\1/', $result['response'], $match);
                if (isset($match['url'])) {
                    $result = $curl->request('GET', $curl->url($match['url'], 'html'), $params);
                }
            }

            if (205 == $result['code']) {
                $index = $result['response'];

                if ((bool) pqnp_config('GOOGLE_UNIVERSAL_ANALYTICS_ACTIVE')) {
                    if (!preg_match('/(?:\s+)?ga(?:\s+)?\((?:\s+)?\'set\'(?:\s+)?,(?:\s+)?\'campaignSource\'/', $index)) {
                        $errors[] = $this->l('The campaign is not set correctly!');
                    }
                } else {
                    if (!preg_match('/_setCampaignTrack(?:\s+)?(\'|")(?:\s+)?,(?:\s+)?true/', $index)) {
                        $errors[] = $this->l('The campaign is not set correctly!');
                    }
                }
            } elseif (200 == $result['code']) {
                $errors[] = $this->l('The campaign is not set correctly!');
            } elseif (1 == $result['code']) {
                $errors[] = $result['response'];
            } else {
                $errors[] = $this->l('Error on running this verification!');
            }
        } catch (Exception $e) {
            $errors[] = $e->getMessage();
        }

        if (empty($errors)) {
            $response['msg'] = $this->l('The campaign is set correctly!');
            $response['status'] = true;
        }

        return Tools::jsonEncode($response);
    }

    /**
     * Get statistics.
     *
     * @return json
     */
    public function getStatistics()
    {
        $id_lang = (int) $this->context->language->id;

        if (Version::isLower('1.5.1')) {
            $sql = 'SELECT
					nps.`id_product`, nps.`clicks`, pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`name`, pl.`id_lang`, p.*, asso_shop_image.`id_image`
					FROM '._DB_PREFIX_.'newsletter_pro_statistics nps
					LEFT JOIN '._DB_PREFIX_.'product p on (nps.id_product = p.id_product)
					LEFT JOIN '._DB_PREFIX_.'product_lang pl on (p.id_product = pl.id_product AND pl.id_lang = '.$id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'image` i
						ON (i.`id_product` = p.`id_product`)'.
                        @Shop::addSqlAssociation('image', 'i', true, 'image_shop.cover=1').'
					LEFT JOIN `'._DB_PREFIX_.'image_lang` il
						ON (asso_shop_image.`id_image` = il.`id_image`
						AND il.`id_lang` = '.(int) $id_lang.')

					GROUP BY nps.`id_product`
					ORDER BY nps.`clicks` DESC
					LIMIT 100;';
        } else {
            $sql = 'SELECT
					nps.`id_product`, nps.`clicks`, pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`name`, pl.`id_lang`, p.*, image_shop.`id_image`
					FROM '._DB_PREFIX_.'newsletter_pro_statistics nps
					LEFT JOIN '._DB_PREFIX_.'product p on (nps.id_product = p.id_product)
					LEFT JOIN '._DB_PREFIX_.'product_lang pl on (p.id_product = pl.id_product AND pl.id_lang = '.$id_lang.')
					LEFT JOIN `'._DB_PREFIX_.'image` i
						ON (i.`id_product` = p.`id_product`)'.
                        Shop::addSqlAssociation('image', 'i', true, 'image_shop.cover=1').'
					LEFT JOIN `'._DB_PREFIX_.'image_lang` il
						ON (image_shop.`id_image` = il.`id_image`
						AND il.`id_lang` = '.(int) $id_lang.')

					GROUP BY nps.`id_product`
					ORDER BY nps.`clicks` DESC
					LIMIT 100;';
        }

        $result = Db::getInstance()->executeS($sql);
        $products = [];

        if ($result) {
            $this->context->currency = new Currency((int) pqnp_config('PS_CURRENCY_DEFAULT'));
            $result = $this->getStatisticsProductsAttributes($id_lang, $result, (int) $this->context->currency->id);

            foreach ($result as $key => $value) {
                $products[$key]['id_product'] = $value['id_product'];
                $products[$key]['clicks'] = $value['clicks'];
                $products[$key]['name'] = $value['name'];
                $products[$key]['price_display'] = $value['price_display'];
                $products[$key]['link'] = $this->sanitizeLink($value['link']);
                $products[$key]['thumb_path'] = $value['thumb_path'];
                $products[$key]['top'] = $key + 1;
            }

            return Tools::jsonEncode($products);
        }

        return Tools::jsonEncode([]);
    }

    /**
     * Some of the queries are unuseful in the admin backoffice and a sanitize is required.
     *
     * @param string $link
     *
     * @return string
     */
    public function sanitizeLink($link)
    {
        $link = preg_replace('/&id_newsletter=[^}]+\}(?=&|$)/', '', $link);
        $link = preg_replace('/&newsletterpro_source=newsletter/', '', $link);

        return $link;
    }

    /**
     * Execute the module update.
     *
     * @return json
     */
    public function updateModule()
    {
        $errors = [];
        $response = ['status' => false, 'errors' => &$errors, 'message' => []];

        if (!$this->upgrade->execute()) {
            $errors = array_merge($errors, $this->upgrade->getErrors());
        }

        if (empty($errors)) {
            $response['status'] = true;
            $response['message'][] = $this->l('The module was update successfully!');
            $response['message'][] = $this->l('The browser will refresh in %s seconds.');
        }

        return Tools::jsonEncode($response);
    }

    public function clearUpdateWarnings()
    {
        NewsletterProUpgrade::clearWarningCookie();

        return $this->response->json();
    }

    /**
     * Get update details.
     *
     * @return array
     */
    public function getUpdateDetails()
    {
        $db_version = $this->getDbVersion();

        return [
            'needs_update' => ($db_version != $this->version),
            'db_version' => $db_version,
            'version' => $this->version,
        ];
    }

    /**
     * Get module database version.
     *
     * @return string
     */
    public function getDbVersion()
    {
        return Db::getInstance()->getValue('SELECT `version` FROM `'._DB_PREFIX_.'module` WHERE `name` = "'.pSQL($this->name).'"');
    }

    /**
     * Get the products statistics attributes.
     *
     * @param int   $id_lang
     * @param array $pr
     * @param int   $id_currency
     *
     * @return array
     */
    public function getStatisticsProductsAttributes($id_lang, $pr, $id_currency = null)
    {
        $prop = $this->getNewProperties(null, $id_currency);

        $products = Product::getProductsProperties($id_lang, $pr);

        foreach ($products as &$product) {
            $this->createProductTemplateVars($id_lang, $product, $prop);
        }

        return $products;
    }

    /**
     * Get the filter by purchase content.
     *
     * @return string
     */
    public function getFilterByPurchaseContent()
    {
        $tpl = $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/filter_by_purchase.tpl'));

        return $tpl;
    }

    /**
     * Get filter by birthday content.
     *
     * @param string $fbb_class
     *
     * @return string
     */
    public function getFilterByBirthdayContent($fbb_class = '')
    {
        $this->context->smarty->assign([
            'fbb_class' => $fbb_class,
        ]);

        $tpl = $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/filter_by_birthday.tpl'));

        return $tpl;
    }

    /**
     * Get range selection content.
     *
     * @return string
     */
    public function getRangeSelectionContent()
    {
        $tpl = $this->context->smarty->fetch(pqnp_template_path($this->dir_location.'views/templates/admin/filter_by_range.tpl'));

        return $tpl;
    }

    /**
     * Search by purchased product.
     *
     * @param string $query
     *
     * @return json
     */
    public function searchByPurchase($query)
    {
        return $this->searchProducts($query, (int) $this->context->language->id, (int) pqnp_config('PS_CURRENCY_DEFAULT'));
    }

    /**
     * Get customer language id.
     *
     * @param int $id_customer
     *
     * @return int
     */
    public function getCustomerIdLang($id_customer)
    {
        return Db::getInstance()->getValue('SELECT `id_lang` FROM '._DB_PREFIX_.'customer WHERE `id_customer` = '.(int) $id_customer);
    }

    /**
     * Strip array.
     *
     * @param array $array
     * @param int   $level
     *
     * @return array
     */
    public static function strip($array, $level = 10)
    {
        if (_PS_MAGIC_QUOTES_GPC_) {
            if ($level < 0) {
                return $array;
            }

            if (is_array($array)) {
                foreach ($array as $key => $value) {
                    if (is_array($value)) {
                        $array[$key] = self::strip($value, --$level);
                    } else {
                        $array[$key] = Tools::stripslashes($value);
                    }
                }
            } elseif (is_string($array)) {
                $array = Tools::stripslashes($array);
            }
        }

        return $array;
    }

    /**
     * Get hooks list.
     *
     * @return array
     */
    public function getHooksList()
    {
        $reflection = new ReflectionObject($this);
        $methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);
        $all_hooks = [];
        foreach ($methods as $method) {
            if (preg_match('/^hook[A-Z]/', $method->name) && __CLASS__ == $method->class) {
                $all_hooks[] = lcfirst(preg_replace('/^hook/', '', $method->name));
            }
        }

        return $all_hooks;
    }

    /**
     * For the order version of prestashop.
     *
     * @param bool $share
     *
     * @return array
     */
    public function getListOfID($share = false)
    {
        $shop_id = $this->getID();
        $shop_group_id = $this->getGroupID();

        if ($shop_id) {
            $list = ($share) ? Shop::getSharedShops($shop_id, $share) : [$shop_id];
        } elseif ($shop_group_id) {
            $list = Shop::getShops(true, $shop_group_id, true);
        } else {
            $list = Shop::getShops(true, null, true);
        }

        return $list;
    }

    /**
     * For the order version of prestashop.
     *
     * @param bool $use_default
     *
     * @return array
     */
    public function getID($use_default = false)
    {
        return (!$this->id && $use_default) ? (int) Configuration::get('PS_SHOP_DEFAULT') : (int) $this->id;
    }

    /**
     * For the order version of prestashop.
     *
     * @return int
     */
    public function getGroupID()
    {
        if (defined('_PS_ADMIN_DIR_')) {
            return Shop::getContextGroupID();
        }

        return (int) $this->id_group_shop;
    }

    /**
     * Get our modules.
     *
     * @return string
     */
    public function getOurModules()
    {
        return NewsletterProOurModules::newInstance()->get();
    }

    /**
     * Clear log files.
     *
     * @return json
     */
    public function clearLogFiles()
    {
        $response = new NewsletterProAjaxResponse([
            'msg' => '',
        ]);

        foreach (NewsletterProLog::getFiles() as $filename) {
            if (!NewsletterProLog::clear($filename)) {
                $response->addError(sprintf($this->l('The log file "%s" cannot be cleared.'), $filename));
            }
        }

        if ($response->success()) {
            $response->set('msg', $this->l('The log files has been cleared.'));
        }

        return $response->display();
    }

    /**
     * Create backup.
     *
     * @param string $name
     * @param bool   $check_duplicate
     *
     * @return bool
     */
    public function ajaxCreateBackup($name, $check_duplicate = true)
    {
        /** @var NewsletterProInstall */
        $install = null;
        include _NEWSLETTER_PRO_DIR_.'/sql/install.php';

        @ini_set('max_execution_time', '600');

        $response = new NewsletterProAjaxResponse([
            'msg' => '',
        ]);

        try {
            $backup_tables = $install->getTables();

            $required_config_shop = NewsletterProConfigurationShop::getAllShopsConfiguration();

            $name = trim($name);
            if (empty($name)) {
                $response->addError($this->l('The name cannot be empty.'));
            } elseif (!NewsletterProTools::isFileName($name)) {
                $response->addError($this->l('Some of the name characters are not allowed.'));
            }

            if (self::BACKUP_TYPE_XML == self::getBackupType()) {
                $backup = new NewsletterProBackupXml();
                // if the second value is true, the backup will be in hex values
                $backup->create($backup_tables, false);

                $backup->addHeader('configuration_shop', serialize($required_config_shop));
                $backup->addHeader('configuration', serialize(Config::get()));
                $backup->addHeader('configuration_campaign', Configuration::get('NEWSLETTER_PRO_CAMPAIGN'));

                $bn_info = $this->getNewsletterProInfo();

                $backup->addHeader('hooks', serialize($bn_info['hooks']));

                $path_name = 'global/xml/'.NewsletterProBackupXml::formatName($name);

                if (NewsletterProBackupXml::pathNameExists($path_name, true) && $check_duplicate) {
                    $response->addError($this->l('The backup name already exists.'));
                }

                if ($response->success()) {
                    if (!$backup->save($path_name, false)) {
                        $response->addError($this->l('An error occurred at the backup creation. Please check the CHMOD permissions.'));
                    } else {
                        $response->set('msg', $this->l('The backup has made successfully.'));
                    }
                }
            } else {
                // create a global backup
                $backup = new NewsletterProBackupSql();
                $backup->create($backup_tables);

                $backup->setHeader('configuration_shop', serialize($required_config_shop));
                $backup->setHeader('configuration', serialize(Config::get()));

                $path_name = 'global/sql/'.NewsletterProBackupSql::formatName($name);

                if (NewsletterProBackupSql::pathNameExists($path_name, true) && $check_duplicate) {
                    $response->addError($this->l('The backup name already exists. Try with a different name.'));
                }

                if ($response->success()) {
                    if (!$backup->save($path_name, false)) {
                        $response->addError($this->l('An error occurred at the backup creation. Please check the CHMOD permissions.'));
                    } else {
                        $response->set('msg', $this->l('The backup has made successfully.'));
                    }
                }
            }
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    /**
     * Show backups.
     *
     * @return string
     */
    public function showLoadBackup()
    {
        $tpl = $this->context->smarty->createTemplate(pqnp_template_path($this->dir_location.'views/templates/admin/global_create_backup.tpl'));

        return $tpl->fetch();
    }

    /**
     * Get backups.
     *
     * @return json
     */
    public function ajaxGetBackup()
    {
        $list = [];
        $date = [];

        if (self::BACKUP_TYPE_XML == self::getBackupType()) {
            $index = 1;
            foreach (NewsletterProBackupXml::getList('global/xml') as $item) {
                $item['id'] = $index++;
                $list[$index] = $item;
                $date[$index] = $item['m_date'];
            }
        } else {
            $index = 1;
            foreach (NewsletterProBackupSql::getList('global/sql') as $item) {
                $item['id'] = $index++;
                $list[$index] = $item;
                $date[$index] = $item['m_date'];
            }
        }

        // srot array by last modification
        array_multisort($date, SORT_DESC, $list);

        return Tools::jsonEncode($list);
    }

    /**
     * Delete backup.
     *
     * @param string $basename
     *
     * @return json
     */
    public function ajaxDeleteBackup($basename)
    {
        $response = new NewsletterProAjaxResponse([
            'msg' => '',
        ]);

        if (self::BACKUP_TYPE_XML == self::getBackupType()) {
            $path = NewsletterProBackupXml::path().'/global/xml/'.$basename;
        } else {
            $path = NewsletterProBackupSql::path().'/global/sql/'.$basename;
        }

        if (file_exists($path)) {
            if (false === @unlink($path)) {
                $this->addError($this->l('Cannot delete the record. Please check the CHMOD permissions.'));
            }
        } else {
            $this->addError($this->l('The file does not exists anymore.'));
        }

        return $response->display();
    }

    /**
     * Restore backup.
     *
     * @param string $basename
     *
     * @return json
     */
    public function ajaxLoadBackup($basename)
    {
        @ini_set('max_execution_time', '600');

        $response = new NewsletterProAjaxResponse([
            'msg' => '',
        ]);

        try {
            if (self::BACKUP_TYPE_XML == self::getBackupType()) {
                $backup = new NewsletterProBackupXml();
                $backup->load('/global/xml/'.$basename);
                if (!$backup->execute()) {
                    $response->addError($this->l('The restore process has faild.'));
                } else {
                    $response->set('msg', $this->l('The backup was restored successfully.'));
                }
            } else {
                $backup = new NewsletterProBackupSql();
                $backup->load('/global/sql/'.$basename);
                if (!$backup->execute()) {
                    $response->addError($this->l('The restore process has faild.'));
                } else {
                    $response->set('msg', $this->l('The backup was restored successfully.'));
                }
            }
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    /**
     * Get backup type.
     *
     * @return int
     */
    public static function getBackupType()
    {
        if ('xml' == Tools::strtolower(self::BACKUP_TYPE)) {
            return self::BACKUP_TYPE_XML;
        }

        return self::BACKUP_TYPE_SQL;
    }

    /**
     * Delete the bounced emails.
     *
     * @param array $file
     * @param array $post
     *
     * @return json
     */
    public function deleteBouncedEmails($file)
    {
        @ini_set('max_execution_time', 600);

        $response = new NewsletterProAjaxResponse([
            'msg' => '',
            'lists' => [],
        ]);

        if (!Tools::isSubmit('bounced_customers_list')
            && !Tools::isSubmit('bounced_visitors_list')
            && !Tools::isSubmit('bounced_visitors_np_list')
            && !Tools::isSubmit('bounced_added_list')) {
            $response->addError($this->l('You must select at least a list before to proceed.'));
        }

        if (!$response->success()) {
            return $response->display();
        }

        $bounced_method = (int) Tools::getValue('bounced_method');
        $success = 0;
        $errors = 0;
        $emails_count = 0;

        $tables_list = [];

        if (Tools::isSubmit('bounced_customers_list')) {
            $tables_list[] = 'customers';
        }

        if (Tools::isSubmit('bounced_visitors_list')) {
            $tables_list[] = 'visitors';
        }

        if (Tools::isSubmit('bounced_visitors_np_list')) {
            $tables_list[] = 'visitors_np';
        }

        if (Tools::isSubmit('bounced_added_list')) {
            $tables_list[] = 'added';
        }

        $response->set('lists', $tables_list);

        if (isset($file)) {
            if (preg_match('/\.csv$/i', $file['name'])) {
                $validate = $this->verifyFileErros($file);
                if (true === $validate) {
                    $separator = trim(Tools::getValue('bounced_csv_separator'));

                    if (';' != $separator || ',' != $separator) {
                        $separator = ';';
                    }

                    $rows = $this->csvToArray($file['tmp_name'], $separator, 2, 0, false);

                    foreach ($rows['rows'] as $row_array) {
                        ++$emails_count;
                        $email = trim($row_array[key($row_array)]);

                        if (NewsletterProBounce::execute($email, $tables_list, $bounced_method)) {
                            ++$success;
                        } else {
                            ++$errors;
                        }
                    }
                } else {
                    $response->addError($validate);
                }
            } else {
                $response->addError($this->l('The file extension is not allowed. Only the .csv file extensions are allowed.'));
            }
        } else {
            $response->addError($this->l('You need to select the .CSV file first.'));
        }

        if ($response->success()) {
            $action_msg = -1 == $bounced_method ? $this->l('removed') : $this->l('unsubscribed');
            $response->set('msg', $this->l(sprintf('You have %s %s from %s emails.', $action_msg, $success, $emails_count)));
        }

        return $response->display();
    }

    public function getHistoryExclusion()
    {
        $list = [];

        $send = Db::getInstance()->executeS('
			SELECT h.`id_newsletter_pro_tpl_history`,
					s.`id_newsletter_pro_send`, 
					s.`template`, 
					s.`date`, 
					s.`emails_count`, 
					s.`emails_success`,
					s.`emails_error`
			FROM `'._DB_PREFIX_.'newsletter_pro_tpl_history` h
			LEFT JOIN `'._DB_PREFIX_.'newsletter_pro_send` s ON (s.`id_newsletter_pro_tpl_history` = h.`id_newsletter_pro_tpl_history`)
			WHERE s.`active` = 0
		');

        $i = 0;
        foreach ($send as $value) {
            $value['type'] = 'send';
            $list[$i] = $value;
            ++$i;
        }

        $task = Db::getInstance()->executeS('
			SELECT h.`id_newsletter_pro_tpl_history`,
					t.`id_newsletter_pro_task`, 
					t.`template`, 
					t.`date_start` as `date`, 
					t.`emails_count`, 
					t.`emails_success`,
					t.`emails_error`
			FROM `'._DB_PREFIX_.'newsletter_pro_tpl_history` h
			LEFT JOIN `'._DB_PREFIX_.'newsletter_pro_task` t ON (t.`id_newsletter_pro_tpl_history` = h.`id_newsletter_pro_tpl_history`)
			WHERE t.`done` = 1
		');

        foreach ($task as $value) {
            $value['type'] = 'task';
            $list[$i] = $value;
            ++$i;
        }

        usort($list, [$this, 'sortByDate']);

        foreach ($list as &$value) {
            $value['template'] = Tools::ucfirst(str_replace('_', ' ', pathinfo($value['template'], PATHINFO_FILENAME)));
            $value['date'] = date('Y-m-d', strtotime($value['date']));
        }

        return Tools::jsonEncode($list);
    }

    public function addHistoryEmailsToExclusion($data, $bool_remaining_email, $bool_sent_email)
    {
        $response = NewsletterProAjaxResponse::newInstance([
            'msg' => '',
            'count' => 0,
        ]);

        try {
            if (!$bool_remaining_email && !$bool_sent_email) {
                $response->addError($this->l('One of the option "Remaining email" or "Sent email" should me checked.'));

                return $response->display();
            }

            if (is_array($data) && !empty($data)) {
                $type_send = 'send';

                $ids_send = [];
                $ids_task = [];

                foreach ($data as $value) {
                    if ($value['type'] == $type_send) {
                        $ids_send[] = $value['id'];
                    } else {
                        $ids_task[] = $value['id'];
                    }
                }

                $email_exclusion = NewsletterProEmailExclusion::newInstance();

                $send_emails = $email_exclusion->getEmailsFromSend($ids_send, $bool_remaining_email, $bool_sent_email);
                $task_emails = $email_exclusion->getEmailsFromSend($ids_task, $bool_remaining_email, $bool_sent_email);

                $emails = array_merge($send_emails, $task_emails);

                $result = $email_exclusion->add($emails);
                $response->set('msg', sprintf($this->l('(%s) emails was added to exclusion list and (%s) emails already exists into exclusion list.'), $result[0], $result[1]));
                $response->set('count', $email_exclusion->countList());
            } else {
                $response->addError($this->l('There are not items selected.'));
            }
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    public function clearExclusionEmails()
    {
        $response = NewsletterProAjaxResponse::newInstance([
            'msg' => '',
        ]);

        if (!NewsletterProEmailExclusion::newInstance()->emptyList()) {
            $response->addError($this->l('An error occurred.'));
        } else {
            $response->set('msg', $this->l('The exclusion emails list has been cleared.'));
        }

        return $response->display();
    }

    public function addCsvEmailsToExclusion($file)
    {
        $response = NewsletterProAjaxResponse::newInstance([
            'msg' => '',
            'count' => 0,
        ]);

        try {
            if (isset($file)) {
                if (preg_match('/\.csv$/i', $file['name'])) {
                    $validate = $this->verifyFileErros($file);
                    if (true === $validate) {
                        $separator = trim(Tools::getValue('exclusion_emails_csv_separator'));

                        if (';' != $separator || ',' != $separator) {
                            $separator = ';';
                        }

                        $rows = $this->csvToArray($file['tmp_name'], $separator, 2, 0, false);

                        $email_exclusion = NewsletterProEmailExclusion::newInstance();
                        $emails = [];
                        foreach ($rows['rows'] as $row_array) {
                            $emails[] = trim($row_array[key($row_array)]);
                        }

                        $result = $email_exclusion->add($emails);

                        $response->set('msg', sprintf($this->l('(%s) emails was added to exclusion list and (%s) emails already exists into exclusion list.'), $result[0], $result[1]));
                        $response->set('count', $email_exclusion->countList());
                    } else {
                        $response->addError($validate);
                    }
                } else {
                    $response->addError($this->l('The file extension is not allowed. Only the .csv file extensions are allowed.'));
                }
            } else {
                $response->addError($this->l('You need to select the .CSV file first.'));
            }
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    public function isSendNewsletterInProgress()
    {
        return (int) Db::getInstance()->getValue('
			SELECT `id_newsletter_pro_send`
			FROM `'._DB_PREFIX_.'newsletter_pro_send`
			WHERE `id_newsletter_pro_send` = (SELECT MAX(`id_newsletter_pro_send`) FROM '._DB_PREFIX_.'newsletter_pro_send)
			AND `active` = 1
		');
    }

    public function resendSendHistory($id_history, $left_list = 0, $right_list_undelivered = 0)
    {
        $emails = [];
        $response = NewsletterProAjaxResponse::newInstance([
            'emails' => &$emails,
            'id_newsletter_pro_send' => 0,
            'id_history' => 0,
        ]);

        try {
            if (!$left_list && !$right_list_undelivered) {
                $response->addError($this->l('You must select at least one list before to proceed.'));

                return $response->display();
            }

            $id_history = $this->getHistoryId($id_history);
            $response->set('id_history', (int) $id_history);

            if (!$id_history) {
                $response->addError($this->l('Invalid hisotry id.'));

                return $response->display();
            }

            $id_newsletter_pro_send = (int) Db::getInstance()->getValue('
				SELECT `id_newsletter_pro_send` FROM `'._DB_PREFIX_.'newsletter_pro_send`
				WHERE `id_newsletter_pro_tpl_history` = '.(int) $id_history.'
			');

            $response->set('id_newsletter_pro_send', (int) $id_newsletter_pro_send);

            if (!$id_newsletter_pro_send) {
                $response->addError(sprintf($this->l('Invalid database table "%s" id.'), 'newsletter_pro_send'));

                return $response->display();
            }

            $emails_to_send = [];
            $emails_sent_faild = [];
            $emails_sent_success = [];

            $results = Db::getInstance()->executeS('
				SELECT `emails_to_send`, `emails_sent`
				FROM `'._DB_PREFIX_.'newsletter_pro_send_step`
				WHERE `id_newsletter_pro_send` = '.(int) $id_newsletter_pro_send.'
				ORDER BY `step` ASC
			');

            if (!$results) {
                $response->addError($this->l('There are no records.'));

                return $response->display();
            }

            foreach ($results as $row) {
                foreach (NewsletterProTools::unSerialize($row['emails_to_send']) as $email) {
                    $emails_to_send[] = $email;
                }

                foreach (NewsletterProTools::unSerialize($row['emails_sent']) as $email_result) {
                    if (false == (bool) $email_result['status']) {
                        $emails_sent_faild[] = $email_result['email'];
                    } else {
                        $emails_sent_success[] = $email_result['email'];
                    }
                }
            }

            if ($left_list) {
                $emails = array_merge($emails, $emails_to_send);
            }

            if ($right_list_undelivered) {
                $emails = array_merge($emails, $emails_sent_faild);
            }

            if (empty($emails)) {
                $response->addError($this->l('Cannot find any emails addresses.'));

                return $response->display();
            }
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    private function getHistoryId($id_history)
    {
        return (int) Db::getInstance()->getValue('
			SELECT `id_newsletter_pro_tpl_history`
			FROM `'._DB_PREFIX_.'newsletter_pro_tpl_history`
			WHERE `id_newsletter_pro_tpl_history` = '.(int) $id_history.'
		');
    }

    public function subscribe($email, $id_lang = null, $id_shop = null)
    {
        $errors = [];
        $error_message = $this->l('An error occurred at the subscription.');

        $id_lang = isset($id_lang) ? (int) $id_lang : (int) $this->context->language->id;
        $id_shop = isset($id_shop) ? (int) $id_shop : (int) $this->context->shop->id;

        if ($info = $this->getUserTableByEmail($email)) {
            $write_consent = false;
            foreach ($info as $value) {
                if (!Db::getInstance()->update($value['table'], [
                    $value['newsletter'] => 1,
                ], '`'.$value['email'].'`= "'.pSQL($email).'"')) {
                    $errors[] = $error_message;
                } else {
                    $write_consent = true;
                }
            }

            if ($write_consent) {
                NewsletterProSubscriptionConsent::newInstance()->set($email, true)->add();
            }
        } else {
            // this part will not check for duplicate emails, that check is made on the upper rows
            if ((bool) pqnp_config('SUBSCRIPTION_ACTIVE')) {
                $subscriber = new NewsletterProSubscribers();
                $subscriber->id_shop = (int) $id_shop;
                $subscriber->email = pSQL($email);
                $subscriber->date_add = date('Y-m-d H:i:s');
                $subscriber->ip_registration_newsletter = (string) Tools::getRemoteAddr();
                $subscriber->active = 1;

                if (!$subscriber->add()) {
                    $errors = array_merge($errors, $subscriber->getErrors());
                } else {
                    NewsletterProSubscriptionConsent::newInstance()->set($email, true)->add();
                }
            } else {
                if (NewsletterProTools::blockNewsletterExists()) {
                    if (!Db::getInstance()->insert('newsletter', [
                        'id_shop' => (int) $id_shop,
                        'email' => pSQL($email),
                        'newsletter_date_add' => date('Y-m-d H:i:s'),
                        'ip_registration_newsletter' => (string) Tools::getRemoteAddr(),
                        'active' => 1, ])) {
                        $errors[] = $error_message;
                    } else {
                        NewsletterProSubscriptionConsent::newInstance()->set($email, true)->add();
                    }
                } else {
                    if (!Db::getInstance()->insert('newsletter_pro_email', [
                        'id_shop' => (int) $id_shop,
                        'id_lang' => (int) $id_lang,
                        'email' => pSQL($email),
                        'date_add' => date('Y-m-d H:i:s'),
                        'ip_registration_newsletter' => (string) Tools::getRemoteAddr(),
                        'active' => 1, ])) {
                        $errors[] = $error_message;
                    } else {
                        NewsletterProSubscriptionConsent::newInstance()->set($email, true)->add();
                    }
                }
            }
        }

        // if the emails is in the forward list will be deleted because the alreay subscribed
        Db::getInstance()->delete('newsletter_pro_forward', '`to` = "'.pSQL($email).'"');

        return $errors;
    }

    private function getUserTableByEmail($email)
    {
        $definition = [
            'customer' => ['email' => 'email', 'newsletter' => 'newsletter'],
            'newsletter' => ['email' => 'email', 'newsletter' => 'active'],
            'newsletter_pro_email' => ['email' => 'email', 'newsletter' => 'active'],
            'newsletter_pro_subscribers' => ['email' => 'email', 'newsletter' => 'active'],
        ];

        $info = [];
        foreach ($definition as $table => $fields) {
            if (NewsletterProTools::tableExists($table)) {
                $sql = 'SELECT COUNT(*) FROM `'._DB_PREFIX_.$table.'` WHERE `'.$fields['email'].'` = "'.pSQL($email).'"';
                if (Db::getInstance()->getValue($sql)) {
                    $info[] = [
                        'table' => $table,
                        'email' => $fields['email'],
                        'newsletter' => $fields['newsletter'],
                    ];
                }
            }
        }

        return !empty($info) ? $info : false;
    }

    public function syncNewsletters($id = null, $limit = null, $get_last_id = false)
    {
        return NewsletterProSendNewsletter::newInstance()->sync($id, $limit, $get_last_id);
    }

    public function startSendNewsletters($trigger)
    {
        if ($trigger) {
            NewsletterProSendConnection::clearAll();
        }

        return NewsletterProSendNewsletter::newInstance()->send();
    }

    public function continueSendNewsletters($trigger)
    {
        $continue = $trigger ? true : false;

        return NewsletterProSendNewsletter::newInstance()->send($continue);
    }

    public function stopSendNewsletters()
    {
        return NewsletterProSendNewsletter::newInstance()->stop();
    }

    public function pauseSendNewsletters()
    {
        return NewsletterProSendNewsletter::newInstance()->pause();
    }

    public function dateMonths($id_lang = null)
    {
        if (isset($id_lang)) {
            $language = new Language($id_lang);

            if (Validate::isLoadedObject($language)) {
                if ('fr' == $language->iso_code) {
                    return [
                        '01' => 'Janvier',
                        '02' => 'Février',
                        '03' => 'Mars',
                        '04' => 'Avril',
                        '05' => 'Mai',
                        '06' => 'Juin',
                        '07' => 'Juillet',
                        '08' => 'Août',
                        '09' => 'Septembre',
                        '10' => 'Octobre',
                        '11' => 'Novembre',
                        '12' => 'Décembre',
                    ];
                }
            }
        }

        return [
            '01' => $this->l('January'),
            '02' => $this->l('February'),
            '03' => $this->l('March'),
            '04' => $this->l('April'),
            '05' => $this->l('May'),
            '06' => $this->l('June'),
            '07' => $this->l('July'),
            '08' => $this->l('August'),
            '09' => $this->l('September'),
            '10' => $this->l('October'),
            '11' => $this->l('November'),
            '12' => $this->l('December'),
        ];
    }

    public function jsUpdateConfiguration($name, $value)
    {
        $response = NewsletterProAjaxResponse::newInstance([]);
        $error = sprintf($this->l('Cannot update the configuration "%s".'), $name);

        if (!$name) {
            $response->addError($error);

            return $response->display();
        }

        if (!pqnp_config($name, $value)) {
            $response->addError($error);
        }

        return $response->display();
    }

    public function updateTopShortcuts($name, $value)
    {
        $response = NewsletterProAjaxResponse::newInstance([]);

        $page_header_toolbar = pqnp_config('PAGE_HEADER_TOOLBAR');
        $name = Tools::strtoupper($name);

        if (!isset($page_header_toolbar[$name])) {
            $response->addError(sprintf($this->l('The configuration key %s does not exists.'), $name));
        } else {
            $page_header_toolbar[$name] = $value;
            if (!pqnp_config('PAGE_HEADER_TOOLBAR', $page_header_toolbar)) {
                $response->addError($this->l('An error occurred.'));
            }
        }

        $response->set('page_header_toolbar', $page_header_toolbar);

        return $response->display();
    }

    public function clearSendHistoryDetails()
    {
        $response = NewsletterProAjaxResponse::newInstance([]);

        try {
            $ids = Db::getInstance()->executeS('
				SELECT ss.`id_newsletter_pro_send_step` 
				FROM `'._DB_PREFIX_.'newsletter_pro_send` s
				LEFT JOIN `'._DB_PREFIX_.'newsletter_pro_send_step` ss
					ON (s.`id_newsletter_pro_send` = ss.`id_newsletter_pro_send`)
				WHERE `active` = 0
			');

            if ($ids) {
                foreach ($ids as $row) {
                    $id = (int) $row['id_newsletter_pro_send_step'];
                    $send_step = NewsletterProSendStep::newInstance($id);
                    if (Validate::isLoadedObject($send_step)) {
                        $send_step->updateFields([
                            'emails_to_send' => null,
                            'emails_sent' => null,
                            'error_msg' => null,
                        ]);
                    }
                }
            }
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    public function clearTaskHistoryDetails()
    {
        $response = NewsletterProAjaxResponse::newInstance([]);

        try {
            $ids = Db::getInstance()->executeS('
				SELECT ts.`id_newsletter_pro_task_step`
				FROM `'._DB_PREFIX_.'newsletter_pro_task` t
				LEFT JOIN `'._DB_PREFIX_.'newsletter_pro_task_step` ts
					ON (t.`id_newsletter_pro_task` = ts.`id_newsletter_pro_task`)
				WHERE `active` = 0
			');

            if ($ids) {
                foreach ($ids as $row) {
                    $id = (int) $row['id_newsletter_pro_task_step'];
                    $send_step = NewsletterProTaskStep::newInstance($id);
                    if (Validate::isLoadedObject($send_step)) {
                        $send_step->updateFields([
                            'emails_to_send' => null,
                            'emails_sent' => null,
                        ]);
                    }
                }
            }
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    public function replaceEmbedCallback($matches)
    {
        $path = $matches[3];

        if (preg_match('/data-embed=("|\')0\1/', $matches[0])) {
            return $matches[0];
        }

        $swift_image = NewsletterPro_Swift_Image::fromPath($path);

        $this->embed_images_attachments[] = $swift_image;

        return $matches[1].$this->embed_images_message->embed($swift_image).$matches[4];
    }

    public function embedImages($embed_images_message, $template)
    {
        $this->embed_images_message = $embed_images_message;

        // detach the previous embedded images
        foreach ($this->embed_images_attachments as $attachment) {
            $this->embed_images_message->detach($attachment);
        }

        $this->embed_images_attachments = [];

        // embed images
        // You can embed files from a URL if allow_url_fopen is on in php.ini
        if (pqnp_config('SEND_EMBEDED_IMAGES')) {
            $template = preg_replace_callback('/(<img.*src=("|\'))(http.*?)(\2[^>]+>)/', [$this, 'replaceEmbedCallback'], $template);
        }

        return $template;
    }

    public function addFilterSelection($name, $filters)
    {
        $response = NewsletterProAjaxResponse::newInstance([]);

        try {
            $filter = NewsletterProFiltersSelection::newInstance();
            $filter->name = trim($name);

            if (empty($filters)) {
                $response->addError($this->l('There are no filters selected.'));

                return $response->display();
            }

            $filter->value = Tools::jsonEncode($filters);

            if (empty($filter->name)) {
                $response->addError($this->l('The filename cannot be empty.'));
            } elseif ($filter->nameExists()) {
                $response->addError(sprintf($this->l('The filter name already exists, please use a different name.'), $name));
            } else {
                if (!$filter->save()) {
                    $response->addError($this->l('An error occurred.'));
                }
            }
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        $response->set('filters', NewsletterProFiltersSelection::getFilters());

        return $response->display();
    }

    public function deleteFilterSelection($id)
    {
        $response = NewsletterProAjaxResponse::newInstance([]);

        try {
            if (0 == (int) $id) {
                $this->addError($this->l('You must to select a filter.'));

                return $response->display();
            }

            $filter = NewsletterProFiltersSelection::newInstance((int) $id);
            if (!Validate::isLoadedObject($filter)) {
                $this->addError($this->l('The filter does not exists anymore.'));

                return $response->display();
            }

            if (!$filter->delete()) {
                $this->addError($this->l('An error occurred.'));
            }
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    public function getFilterSelectionById($id)
    {
        $response = NewsletterProAjaxResponse::newInstance();

        try {
            if (0 == (int) $id) {
                $this->addError($this->l('You must to select a filter.'));

                return $response->display();
            }

            $filter = NewsletterProFiltersSelection::newInstance((int) $id);
            $response->set('value', Tools::jsonDecode($filter->value));
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    public function getExportOptions($value)
    {
        $response = NewsletterProAjaxResponse::newInstance();

        try {
            switch ($value) {
                case self::LIST_CUSTOMERS:
                    $columns = NewsletterProTools::getTableColumns('customer');
                    $response->set('columns', $columns);
                    break;

                case self::LIST_VISITORS:
                    if (!($table_name = NewsletterProDefaultNewsletterTable::getTableName())) {
                        throw new Exception($this->l('The table does not exists.'));
                    }

                    $columns = NewsletterProTools::getTableColumns($table_name);
                    $response->set('columns', $columns);
                    break;
                case self::LIST_VISITORS_NP:
                    $columns = NewsletterProTools::getTableColumns('newsletter_pro_subscribers');
                    $response->set('columns', $columns);
                    break;
                case self::LIST_ADDED:
                    $columns = NewsletterProTools::getTableColumns('newsletter_pro_email');
                    $response->set('columns', $columns);
                    break;

                default:
                    throw new Exception($this->l('Invalid list id.'));
                    break;
            }
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    public function openLogFIle($href)
    {
        $response = NewsletterProAjaxResponse::newInstance([
            'content' => '',
        ]);

        try {
            $basename = pathinfo($href, PATHINFO_BASENAME);

            $filename = $this->dir_location.'logs/'.$basename;

            if (!file_exists($filename)) {
                throw new Exception(sprintf($this->l('The filename %s does not exists.'), $basename));
            }

            $content = Tools::file_get_contents($filename);
            $response->set('content', $content);
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    public static function getSearchConstions()
    {
        $module = NewsletterPro::getInstance();

        return [
            self::SEARCH_CONDITION_CONTAINS => $module->l('contains'),
            self::SEARCH_CONDITION_IS => $module->l('is equal'),
            self::SEARCH_CONDITION_IS_NOT => $module->l('is not equal'),
            self::SEARCH_CONDITION_GREATER => $module->l('is equal or grater than'),
            self::SEARCH_CONDITION_LESS => $module->l('is equal or less than'),
        ];
    }

    public static function getSearchConstionsJs()
    {
        return [
            'SEARCH_CONDITION_CONTAINS' => self::SEARCH_CONDITION_CONTAINS,
            'SEARCH_CONDITION_IS' => self::SEARCH_CONDITION_IS,
            'SEARCH_CONDITION_IS_NOT' => self::SEARCH_CONDITION_IS_NOT,
            'SEARCH_CONDITION_GREATER' => self::SEARCH_CONDITION_GREATER,
            'SEARCH_CONDITION_LESS' => self::SEARCH_CONDITION_LESS,
        ];
    }

    public function getMaxTotalSpent()
    {
        return ceil((float) Db::getInstance()->getValue('
			SELECT  MAX((SELECT SUM(`total_paid_real` / `conversion_rate`) FROM `'._DB_PREFIX_.'orders` WHERE `id_customer` = c.`id_customer`)) as `total_spent`
			FROM `'._DB_PREFIX_.'customer` c
			'.((bool) pqnp_config('VIEW_ACTIVE_ONLY') ? ' WHERE c.`newsletter` = 1 ' : '').'
		'));

        /*
        return ceil((float)Db::getInstance()->getValue('
            SELECT MAX(o.`total_paid_real` / o.`conversion_rate`) as total_spent
            FROM `'._DB_PREFIX_.'orders` o
            LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.id_customer = o.id_customer)
            WHERE 1
        '));
        */
    }

    private function getCountriesSql($search = null)
    {
        return '
			SELECT c.`id_country`, cl.`name`, c.`iso_code`, c.`active`
			FROM `'._DB_PREFIX_.'country` c
			INNER JOIN `'._DB_PREFIX_.'country_lang` cl 
				ON (c.id_country = cl.id_country)
			AND cl.`id_lang` = '.(int) $this->context->language->id.'
			'.(isset($search) ? ' AND (cl.`name` LIKE "%'.pSQL($search).'%" OR c.`iso_code` LIKE "%'.pSQL($search).'%")' : '').'
			'.((int) pqnp_ini_config('filter_only_active_countries') ? ' AND c.`active` = 1 ' : '').'
			ORDER BY cl.`name` ASC
		';
    }

    public function getCountries()
    {
        return Tools::jsonEncode(Db::getInstance()->executeS($this->getCountriesSql()));
    }

    public function searchCountries($value)
    {
        return Tools::jsonEncode(Db::getInstance()->executeS($this->getCountriesSql($value)));
    }

    public function deleteProductTemplate($path)
    {
        $response = NewsletterProAjaxResponse::newInstance();

        if ((int) pqnp_ini_config('demo_mode')) {
            $demo_return = NewsletterProDemoMode::deleteProductTemplate($path);

            if ($demo_return) {
                return $demo_return;
            }
        }

        try {
            if (file_exists($path)) {
                if (!unlink($path)) {
                    throw new Exception($this->l('The template cannot be delete. Please checked the CHMOD permissions.'));
                }
            }
        } catch (Exception $e) {
            $response->addError($e->getMessage());
        }

        return $response->display();
    }

    public function hookActionDeleteGDPRCustomer($customer)
    {
        if (!empty($customer['email']) && Validate::isEmail($customer['email'])) {
            $privacy_data = new NewsletterProPrivacyData();

            $response = $privacy_data->hookActionDelete($customer['email']);
            if (true === $response) {
                return Tools::jsonEncode(true);
            }

            return Tools::jsonEncode($this->l('Newsletter Pro : Unable to delete the customer personal data.'));
        }
    }

    public function hookActionExportGDPRData($customer)
    {
        if (!Tools::isEmpty($customer['email']) && Validate::isEmail($customer['email'])) {
            $privacy_data = new NewsletterProPrivacyData();

            $response = $privacy_data->export($customer['email']);

            if (count($response['errors']) > 0) {
                return Tools::jsonEncode($this->l('Newsletter Pro : Unable to export the customer personal data.'));
            }

            if (count($response['data']) > 0) {
                return Tools::jsonEncode($response['data']);
            }
        }
    }

    public function hookDisplayCustomerIdentityForm($params = [])
    {
        return $this->hookDisplayCustomerAccountForm($params);
    }

    public function hookDisplayCustomerAccountForm($params = [])
    {
        $list_of_interest = NewsletterProListOfInterest::getListActiveCustomer((int) $this->context->customer->id);

        $this->context->smarty->assign([
            'list_of_interest' => $list_of_interest,
            'customer_account_subscribe_by_loi_active' => (bool) pqnp_config('CUSTOMER_ACCOUNT_SUBSCRIBE_BY_LOI'),
        ]);

        if (NewsletterProTools::hasTemplatePath($this->dir_location.'views/templates/hook/display_customer_account_form.tpl')) {
            return $this->context->smarty->fetch(NewsletterProTools::loadTemplatePath($this->dir_location.'views/templates/hook/display_customer_account_form.tpl'));
        }

        return $this->context->smarty->fetch($this->dir_location.'views/templates/hook/'.NewsletterProTools::getVersion().'/display_customer_account_form.tpl');
    }

    public function hookActionCustomerAccountAdd($params = [])
    {
        $this->hookCreateAccount($params);
        $this->customerAccountSaveListOfInterest();
    }

    public function hookAdditionalCustomerFormFields($params)
    {
        if (version_compare(_PS_VERSION_, '1.7.0.1', '>=')) {
            $label = $this->trans(
                'Sign up for our newsletter[1][2]%conditions%[/2]',
                [
                    '[1]' => '<br>',
                    '[2]' => '<em>',
                    '%conditions%' => 'You may unsubscribe at any moment. For that purpose, please find our contact info in the legal notice.',
                    '[/2]' => '</em>',
                ],
                'Modules.Newsletterpro.Shop'
            );

            return [
                (new FormField())
                    ->setName('pqnp_newsletter')
                    ->setType('checkbox')
                    ->setLabel($label), ];
        }
    }

    public function hookActionCustomerAccountUpdate($params = [])
    {
        $this->customerAccountSaveListOfInterest();
    }

    private function customerAccountSaveListOfInterest()
    {
        if ((bool) pqnp_config('CUSTOMER_ACCOUNT_SUBSCRIBE_BY_LOI')) {
            $list_of_interest = $this->request->get('pqnp_list_of_interest', []);

            $customer_loi = NewsletterProCustomerListOfInterests::getInstanceByCustomerId((int) $this->context->customer->id);

            if (!empty($list_of_interest)) {
                $customer_loi->setCategories($list_of_interest);
                $customer_loi->id_customer = (int) $this->context->customer->id;

                if (!$customer_loi->save()) {
                    $this->context->controller->errors[] = $this->l('Error on updating the list of interests.');
                } else {
                    $subscriber = NewsletterProSubscribers::getInstanceByEmail($this->context->customer->email, (int) $this->context->shop->id);
                    if (Validate::isLoadedObject($subscriber)) {
                        $subscriber->setListOfInterest($list_of_interest);
                        $subscriber->update();
                    }
                }
            } else {
                if (Validate::isLoadedObject($customer_loi)) {
                    $customer_loi->setCategories([]);
                    $customer_loi->update();
                }
            }
        }
    }
}
