benneke1984 Posted November 1, 2017 Share Posted November 1, 2017 Bonjour, J'ai un site Prestashop 1.6.1.13 avec 3 langues installées. J'ai des abonnés à ma newsletter, mais impossible de connaitre la langue de l'abonné. Cette fonctionnalité est prévue ? Si non, comment puis-je l'ajouter? Un module dédié ou une modification du module existant? Merci pour votre aide ! Ben Link to comment Share on other sites More sharing options...
kokoon Posted November 2, 2017 Share Posted November 2, 2017 Bonjour, je ne suis pas expert mais avec le gestionnaire SQL inclus dans Presta il doit y avoir moyen de faire çà. Par exemple en modifiant "simplement" la requête d'export clients. J'espère que quelqu'un de plus calé te répondra dans ce sens. Link to comment Share on other sites More sharing options...
benneke1984 Posted November 2, 2017 Author Share Posted November 2, 2017 Hello Kokoon, Oui effectivement pour les clients ayant un compte il n'y a pas de souci, je sais le faire. Mais pas pour les clients qui se sont "simplement" inscrit à la newsletter via le bloc newsletter sur le site. Je n'ai rien vu dans la DB qui me permette de voir dans quelle langue le client s'est inscrit. Merci pour l'indice en tout cas ! Ben Link to comment Share on other sites More sharing options...
BeComWeb Posted November 3, 2017 Share Posted November 3, 2017 Bonjour, Non en effet il n'y a aucun moyen, si ce n'est l'adresse IP qui peut être géolocalisée mais c'est compliqué et un peu boiteux comme solution (le résultat n'est pas fiable à % pour différente raisons). Donc la seule solution serait de modifier le code et la table SQL du module pour qu'il enregistre la langue en se basant sur le cookie. Link to comment Share on other sites More sharing options...
benneke1984 Posted November 3, 2017 Author Share Posted November 3, 2017 Bonjour, Il me semblait bien que ce serait une chose du genre... L'url ou le cookie peuvent être utiliser effectivement. Quelqu'un a déjà fait une telle modification ? J'i des connaissances en PHP et Mysql mais le système de codage prestashop est parfois... confus ;-) Pour ne pas perdre ses modifications une fois codées, il faut passer par un override si j'ai bien capté ? Il faut aller lui dire qu'il existe un override ou il le découvre tout seul comme un grand ? (j'ai pas envie de recommencer la bidouille à la première mise à jour du bidulle) Link to comment Share on other sites More sharing options...
Matt K. Posted November 3, 2017 Share Posted November 3, 2017 (edited) Salut ! Donc en gros si on parle bien du module blocknewsletter ça me semble pas trop dur. 1) Override du module L'action se passe dans blocknewsletter.php mais comme tu l'as dit, il faut l'override pour faire la modification proprement. Il faut donc créer un fichier blocknewsletter.php dans override/modules/blocknewsletter/ Avec la classe suivante : <?php class BlocknewsletterOverride extends Blocknewsletter { } Ensuite il faut supprimer le fichier class_index.php dans /cache/ sinon Presta verra pas ta nouvelle classe. 2) Modification SQL A. Version simple : Exécuter simplement : ALTER TABLE PREFIX_newsletter add id_lang int(11) not null AFTER id_shop_group; B. Version propre qui demande une réinstallation du module : Il faut override (c'est à dire ajouter la méthode suivante dans la classe créée plus haut) la méthode install en ajoutant la colonne id_lang lors de la création de la table. public function install() { if (!parent::install() || !Configuration::updateValue('PS_NEWSLETTER_RAND', rand().rand()) || !$this->registerHook(array('header', 'footer', 'actionCustomerAccountAdd'))) return false; Configuration::updateValue('NW_SALT', Tools::passwdGen(16)); return Db::getInstance()->execute(' CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'newsletter` ( `id` int(6) NOT NULL AUTO_INCREMENT, `id_shop` INTEGER UNSIGNED NOT NULL DEFAULT \'1\', `id_shop_group` INTEGER UNSIGNED NOT NULL DEFAULT \'1\', `id_lang` int(11) NOT NULL, `email` varchar(255) NOT NULL, `newsletter_date_add` DATETIME NULL, `ip_registration_newsletter` varchar(15) NOT NULL, `http_referer` VARCHAR(255) NULL, `active` TINYINT(1) NOT NULL DEFAULT \'0\', PRIMARY KEY(`id`) ) ENGINE='._MYSQL_ENGINE_.' default CHARSET=utf8'); } 3) Sauvegarde de la langue Cette fois il faut override registerGuest de la sorte (on ajoute la colonne langue) protected function registerGuest($email, $active = true) { $sql = 'INSERT INTO '._DB_PREFIX_.'newsletter (id_shop, id_shop_group, id_lang, email, newsletter_date_add, ip_registration_newsletter, http_referer, active) VALUES ('.$this->context->shop->id.', '.$this->context->shop->id_shop_group.', '.(int)$this->context->cookie->id_lang.', \''.pSQL($email).'\', NOW(), \''.pSQL(Tools::getRemoteAddr()).'\', ( SELECT c.http_referer FROM '._DB_PREFIX_.'connections c WHERE c.id_guest = '.(int)$this->context->customer->id.' ORDER BY c.date_add DESC LIMIT 1 ), '.(int)$active.' )'; return Db::getInstance()->execute($sql); } 4) Afficher la langue dans la liste des emails. On verra plus tard mais je pense que ça se passe dans la méthode renderList. Et voila. Bon j'ai absolument pas testé mais ça me semble être la bonne direction... hésite à me dire si tu as un problème. Bon courage Edited November 3, 2017 by Matt K. (see edit history) Link to comment Share on other sites More sharing options...
benneke1984 Posted November 6, 2017 Author Share Posted November 6, 2017 Bonjour, Voila j'ai réussi à avoir une partie de mon souci... Une partie car j'ai toujours que des id et je voudrais aussi modifier cet id vers la langue, mais je tourne en rond. Par contre pour l'affichage, il y a plus à modifier, car il faut aussi lui dire de récupérer la langue dans la table des clients (ce qu'il ne fait pas de base) et dans la table des inscrits sans compte. J'ai donc écrit ceci (au cas ou cela pourrait servir à quelqu'un d'autre...) public function renderList() { $fields_list = array( 'id' => array( 'title' => $this->l('ID'), 'search' => false, ), 'shop_name' => array( 'title' => $this->l('Shop'), 'search' => false, ), 'gender' => array( 'title' => $this->l('Gender'), 'search' => false, ), 'lastname' => array( 'title' => $this->l('Lastname'), 'search' => false, ), 'firstname' => array( 'title' => $this->l('Firstname'), 'search' => false, ), 'email' => array( 'title' => $this->l('Email'), 'search' => false, ), 'subscribed' => array( 'title' => $this->l('Subscribed'), 'type' => 'bool', 'active' => 'subscribed', 'search' => false, ), 'newsletter_date_add' => array( 'title' => $this->l('Subscribed on'), 'type' => 'date', 'search' => false, ), 'id_lang' => array( 'title' => $this->l('Language'), 'search' => false, ) ); if (!Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE')) unset($fields_list['shop_name']); $helper_list = New HelperList(); $helper_list->module = $this; $helper_list->title = $this->l('Newsletter registrations'); $helper_list->shopLinkType = ''; $helper_list->no_link = true; $helper_list->show_toolbar = true; $helper_list->simple_header = false; $helper_list->identifier = 'id'; $helper_list->table = 'merged'; $helper_list->currentIndex = $this->context->link->getAdminLink('AdminModules', false).'&configure='.$this->name; $helper_list->token = Tools::getAdminTokenLite('AdminModules'); $helper_list->actions = array('viewCustomer'); /* Before 1.6.0.7 displayEnableLink() could not be overridden in Module class we declare another row action instead */ if (version_compare(_PS_VERSION_, '1.6.0.7', '<')) { unset($fields_list['subscribed']); $helper_list->actions = array_merge($helper_list->actions, array('unsubscribe')); } // This is needed for displayEnableLink to avoid code duplication $this->_helperlist = $helper_list; /* Retrieve list data */ $subscribers = $this->getSubscribers(); $helper_list->listTotal = count($subscribers); /* Paginate the result */ $page = ($page = Tools::getValue('submitFilter'.$helper_list->table)) ? $page : 1; $pagination = ($pagination = Tools::getValue($helper_list->table.'_pagination')) ? $pagination : 50; $subscribers = $this->paginateSubscribers($subscribers, $page, $pagination); return $helper_list->generateList($subscribers, $fields_list); } public function getSubscribers() { $dbquery = new DbQuery(); $dbquery->select('c.`id_customer` AS `id`, s.`name` AS `shop_name`, gl.`name` AS `gender`, c.`lastname`, c.`firstname`, c.`email`, c.`newsletter` AS `subscribed`, c.`newsletter_date_add`, c.`id_lang`'); $dbquery->from('customer', 'c'); $dbquery->leftJoin('shop', 's', 's.id_shop = c.id_shop'); $dbquery->leftJoin('gender', 'g', 'g.id_gender = c.id_gender'); $dbquery->leftJoin('gender_lang', 'gl', 'g.id_gender = gl.id_gender AND gl.id_lang = '.(int)$this->context->employee->id_lang); $dbquery->where('c.`newsletter` = 1'); if ($this->_searched_email) $dbquery->where('c.`email` LIKE \'%'.pSQL($this->_searched_email).'%\' '); $customers = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($dbquery->build()); $dbquery = new DbQuery(); $dbquery->select('CONCAT(\'N\', n.`id`) AS `id`, s.`name` AS `shop_name`, NULL AS `gender`, NULL AS `lastname`, NULL AS `firstname`, n.`email`, n.`active` AS `subscribed`, n.`newsletter_date_add`, n.`id_lang`'); $dbquery->from('newsletter', 'n'); $dbquery->leftJoin('shop', 's', 's.id_shop = n.id_shop'); $dbquery->where('n.`active` = 1'); if ($this->_searched_email) $dbquery->where('n.`email` LIKE \'%'.pSQL($this->_searched_email).'%\' '); $non_customers = Db::getInstance()->executeS($dbquery->build()); $subscribers = array_merge($customers, $non_customers); return $subscribers; } Ici j'ai bien entendu tout remis, mais il y a moyen de juste écrire ce qui change ou il vaut mieux avoir un "doublon" complet ? Maintenant je vais m'attarder à l'export en CSV car si je ne sais pas exporter, cette belle liste ne me sert pas à grand chose ;-) Merci pour les retours et/ou les améliorations à ajouter ! Link to comment Share on other sites More sharing options...
benneke1984 Posted November 7, 2017 Author Share Posted November 7, 2017 Rebonjour à tous. J'ai continuer de bosser un peu sur mon export en CSV et le fichier généré est bon, pas de souci de ce côté la. Par contre, je ne sais pas y accéder sans passer par mon ftp. Pour expliquer, voici mon code qui se trouve dans le override : public function export_csv() { if (!isset($this->context)) $this->context = Context::getContext(); $result = $this->getCustomers(); if ($result) { if (!$nb = count($result)) $this->_html .= $this->displayError($this->l('No customers found with these filters!')); elseif ($fd = @fopen(dirname(__FILE__).'/'.strval(preg_replace('#\.{2,}#', '.', Tools::getValue('action'))).'_'.$this->file, 'w')) { $header = array('id', 'shop_name', 'gender', 'lastname', 'firstname', 'email', 'subscribed', 'subscribed_on','id_lang'); $array_to_export = array_merge(array($header), $result); // pr($array_to_export); // die(); foreach ($array_to_export as $tab) $this->myFputCsv($fd, $tab); fclose($fd); $this->_html .= $this->displayConfirmation( sprintf($this->l('The .CSV file has been successfully exported: %d customers found.'), $nb).'<br /> <a href="'.$this->context->shop->getBaseURI().'modules/blocknewsletter/'.Tools::safeOutput(strval(Tools::getValue('action'))).'_'.$this->file.'"> <b>'.$this->l('Download the file').' '.$this->file.'</b> </a> <br /> <ol style="margin-top: 10px;"> <li style="color: red;">'. $this->l('WARNING: When opening this .csv file with Excel, choose UTF-8 encoding to avoid strange characters.'). '</li> </ol>'); } else $this->_html .= $this->displayError($this->l('Error: Write access limited').' '.dirname(__FILE__).'/'.strval(Tools::getValue('action')).'_'.$this->file.' !'); } else $this->_html .= $this->displayError($this->l('No result found!')); } Le souci se situe au niveau du fichier créé. Selon la ligne $fd = @fopen(dirname(__FILE__).'/'.strval(preg_replace('#\.{2,}#', '.', Tools::getValue('action'))).'_'.$this->file, 'w') Ce fichier se trouve dans mon dossier override. Mais selon cette ligne <a href="'.$this->context->shop->getBaseURI().'modules/blocknewsletter/'.Tools::safeOutput(strval(Tools::getValue('action'))).'_'.$this->file.'"> il va créer le lien vers l'emplacement du module natif. Le souci est que : 1. je ne peux pas écrire dans un autre répertoire que dans celui ou est excécuté le script 2. je n'ai pas les droits d'accéder à n fichier se trouvant dans un dossier override dans l'état actuel des choses... Ai-je raté une manipulation ? La seule modification que j'ai fait dans cette fonction est le contenu de la variable $header = array('id', 'shop_name', 'gender', 'lastname', 'firstname', 'email', 'subscribed', 'subscribed_on','id_lang'); ou j'ai ajouté en dernière entrée id_lang. Il y a moyen de lui dire que c'est juste cette variable qui change et qu'il doit excécuter le rester de la fonction tel qu'indiqué dans le module en natif ? Merci pour vos réponses ! Ben Link to comment Share on other sites More sharing options...
benneke1984 Posted November 7, 2017 Author Share Posted November 7, 2017 Rebonjour, J'ai encore changé des choses et j'ai le comportement que je souhaite, mais je n'aime pas beaucoup ma solution, donc si quelqu'un a une meilleure solution, elle est la bienvenue ! Dans mon override : public function renderExportForm() { // Getting data... $countries = Country::getCountries($this->context->language->id); // ...formatting array $countries_list = array(array('id' => 0, 'name' => $this->l('All countries'))); foreach ($countries as $country) $countries_list[] = array('id' => $country['id_country'], 'name' => $country['name']); $fields_form = array( 'form' => array( 'legend' => array( 'title' => $this->l('Export customers\' addresses'), 'icon' => 'icon-envelope' ), 'input' => array( array( 'type' => 'select', 'label' => $this->l('Customers\' country'), 'desc' => $this->l('Filter customers by country.'), 'name' => 'COUNTRY', 'required' => false, 'default_value' => (int)$this->context->country->id, 'options' => array( 'query' => $countries_list, 'id' => 'id', 'name' => 'name', ) ), array( 'type' => 'select', 'label' => $this->l('Newsletter subscribers'), 'desc' => $this->l('Filter customers who have subscribed to the newsletter or not, and who have an account or not.'), 'hint' => $this->l('Customers can subscribe to your newsletter when registering, or by entering their email in the newsletter block.'), 'name' => 'SUSCRIBERS', 'required' => false, 'default_value' => (int)$this->context->country->id, 'options' => array( 'query' => array( array('id' => 0, 'name' => $this->l('All subscribers')), array('id' => 1, 'name' => $this->l('Subscribers with account')), array('id' => 2, 'name' => $this->l('Subscribers without account')), array('id' => 3, 'name' => $this->l('Non-subscribers')) ), 'id' => 'id', 'name' => 'name', ) ), array( 'type' => 'select', 'label' => $this->l('Opt-in subscribers'), 'desc' => $this->l('Filter customers who have agreed to receive your partners\' offers or not.'), 'hint' => $this->l('Opt-in subscribers have agreed to receive your partners\' offers.'), 'name' => 'OPTIN', 'required' => false, 'default_value' => (int)$this->context->country->id, 'options' => array( 'query' => array( array('id' => 0, 'name' => $this->l('All customers')), array('id' => 2, 'name' => $this->l('Opt-in subscribers')), array('id' => 1, 'name' => $this->l('Opt-in non-subscribers')) ), 'id' => 'id', 'name' => 'name', ) ), array( 'type' => 'select', 'label' => 'Language', 'desc' => 'Language', 'name' => 'LANG', 'required' => false, 'default_value' => (int)$this->context->country->id, 'options' => array( 'query' => array( array('id' => 1, 'name' => $this->l('French')), array('id' => 3, 'name' => $this->l('Dutch')), array('id' => 4, 'name' => $this->l('English')) ), 'id' => 'id', 'name' => 'name', ) ), array( 'type' => 'hidden', 'name' => 'action', ) ), 'submit' => array( 'title' => $this->l('Export .CSV file'), 'class' => 'btn btn-default pull-right', 'name' => 'submitExport', ) ), ); $helper = new HelperForm(); $helper->show_toolbar = false; $helper->table = $this->table; $lang = new Language((int)Configuration::get('PS_LANG_DEFAULT')); $helper->default_form_language = $lang->id; $helper->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') ? Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG') : 0; $helper->id = (int)Tools::getValue('id_carrier'); $helper->identifier = $this->identifier; $helper->submit_action = 'btnSubmit'; $helper->currentIndex = $this->context->link->getAdminLink('AdminModules', false).'&configure='.$this->name.'&tab_module='.$this->tab.'&module_name='.$this->name; $helper->token = Tools::getAdminTokenLite('AdminModules'); $helper->tpl_vars = array( 'fields_value' => $this->getConfigFieldsValues(), 'languages' => $this->context->controller->getLanguages(), 'id_language' => $this->context->language->id ); return $helper->generateForm(array($fields_form)); } public function getCustomers() { $id_shop = false; // Get the value to know with subscrib I need to take 1 with account 2 without 0 both 3 not subscrib $who = (int)Tools::getValue('SUSCRIBERS'); // get optin 0 for all 1 no optin 2 with optin $optin = (int)Tools::getValue('OPTIN'); $country = (int)Tools::getValue('COUNTRY'); $id_lang = (int)Tools::getValue('LANG'); if (Context::getContext()->cookie->shopContext) $id_shop = (int)Context::getContext()->shop->id; $customers = array(); if ($who == 1 || $who == 0 || $who == 3) { $dbquery = new DbQuery(); $dbquery->select('c.`id_customer` AS `id`, s.`name` AS `shop_name`, gl.`name` AS `gender`, c.`lastname`, c.`firstname`, c.`email`, c.`newsletter` AS `subscribed`, c.`newsletter_date_add`'); $dbquery->from('customer', 'c'); $dbquery->leftJoin('shop', 's', 's.id_shop = c.id_shop'); $dbquery->leftJoin('gender', 'g', 'g.id_gender = c.id_gender'); $dbquery->leftJoin('gender_lang', 'gl', 'g.id_gender = gl.id_gender AND gl.id_lang = '.$this->context->employee->id_lang); $dbquery->where('c.`newsletter` = '.($who == 3 ? 0 : 1)); if ($optin == 2 || $optin == 1) $dbquery->where('c.`optin` = '.($optin == 1 ? 0 : 1)); if ($country) $dbquery->where('(SELECT COUNT(a.`id_address`) as nb_country FROM `'._DB_PREFIX_.'address` a WHERE a.deleted = 0 AND a.`id_customer` = c.`id_customer` AND a.`id_country` = '.$country.') >= 1'); if ($id_shop) $dbquery->where('c.`id_shop` = '.$id_shop); if ($id_lang) $dbquery->where('c.`id_lang` = '.$id_lang); $customers = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($dbquery->build()); } $non_customers = array(); if (($who == 0 || $who == 2) && (!$optin || $optin == 2) && !$country) { $dbquery = new DbQuery(); $dbquery->select('CONCAT(\'N\', n.`id`) AS `id`, s.`name` AS `shop_name`, NULL AS `gender`, NULL AS `lastname`, NULL AS `firstname`, n.`email`, n.`active` AS `subscribed`, n.`newsletter_date_add`'); $dbquery->from('newsletter', 'n'); $dbquery->leftJoin('shop', 's', 's.id_shop = n.id_shop'); $dbquery->where('n.`active` = 1'); if ($id_shop) $dbquery->where('n.`id_shop` = '.$id_shop); if ($id_lang) $dbquery->where('n.`id_lang` = '.$id_lang); $non_customers = Db::getInstance()->executeS($dbquery->build()); } $subscribers = array_merge($customers, $non_customers); return $subscribers; } Et dans le module d'origine j'ai "bidouillé" la function getCustomers() en fonction public au lieu de private. Cela ouvre certainement la porte à des bugs ou des erreurs, mais je n'ai pas trouvé d'autres possibilités. Merci à tous pour l'aide apportée et pour l'éventuelle solution à ma bidouille... Link to comment Share on other sites More sharing options...
Matt K. Posted November 7, 2017 Share Posted November 7, 2017 Bonjour, Je pense pas que ça va te créer des bugs. Il faudra juste faire attention lors des mises à jour de ce module à remettre "public" (d'ailleurs l'accesseur "protected" me semble être plus approprié mais bon c'est du détail). Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now