• Créer un contrôleur front (indépendant)

    Il arrive de devoir créer un contrôleur front office pour y implémenter une logique particulière.
    Ici nous allons faire juste cela, totalement indépendant d'un module.

    pré-requis

    accès FTP
    notepad++
    5 minutes


    le contrôleur

    Nous allons créer le contrôleur. Nous pouvons le mettre directement dans le répertoire /controllers/front mais au cas où demain ne devions écraser notre prestashop, nous allons préférer le mettre dans /override/controllers/front. En plus de cette manière il est plus simple d'identifier à posteriori quel code a été ajouté.

    Nous devons impérativement dériver d'un FrontController, mais il est tout a fait possible de dériver un contrôleur qui en dérive. Supposons ici ContactController.
    /override/controllers/front/ContactControllerExtra.php

    <?php
    class ContactControllerExtra extends ContactController
    {
       public $php_self = 'contact-extra'; 
    }
    

    Vidons le cache (avec la gomme) ou supprimons le fichier /cache/class_index.php
    Oui c'est tout faire un contrôleur n'est pas compliqué. Mais bon soyons clair c'est pour l'instant une coquille vide.

    Avant d'aller plus loin nous allons le configurer coté back-office afin que nous puissions lui donner une belle url et un minimum de décence coté SEO.


    SEO & URL

    Dans le BO, rendons nous dans Préférences > SEO&URL
    Cliquer ajouter, et remplir

    • page: choisir contact-extra
    • titre: Contact étendu
    • description: Page de contact avancée
    • mots clé: vide
    • url: contact-avance

    Enregistrer

    Voila, vous pouvez/devez customiser les paramètres en fonction des langues afin qu'ils correspondent à vos besoins


    Le code utile

    Jusque maintenant nous n'avons qu'une coquille vide qui se contente de faire en tout point ce que fait le formulaire de contact. Écrivons un peu de code utile. Nous allons mettre en place un captcha très simple.
    Reprenons notre fichier contrôleur
    /override/controllers/front/ContactControllerExtra.php

    <?php
    class ContactControllerExtra extends ContactController
    {
       public $php_self = 'contact-extra'; 
    
      private function trivial_captcha() { return (int)($this->context->cookie->id_guest); }
    
      public function postProcess() {
        if (Tools::isSubmit('submitMessage')) {
          $captcha = Tools::getValue('captcha',0);
          if (!$captcha || $captcha != $this->trivial_captcha()) {
            $this->errors[] = Tools::displayError('Invalid captcha.');
            return;
          }
        }
        return parent::postProcess();
      }
    }
    

    Nous venons de créer un contrôle lors de la réception du formulaire et de vérifier que ce formulaire contient un captcha et que celui-ci est valide.

    Si nous essayons notre contrôleur maintenant nous obtenons systématiquement une erreur "Invalid captcha" lors de l'envoi du formulaire.


    la vue

    Nous devons bien sûr construire un template permettant la saisie du captcha. Pour cela nous allons "bêtement" copier le template normal, puis nous ajouterons notre code à l'intérieur

    Copions /themes/<votre-theme>/contact-form.tpl => /themes/<votre-theme>/contact-form-extra.tpl
    Éditons maintenant ce fichier et identifions où mettre notre code

                                    <div class="col-xs-12 col-md-9">
                                            <div class="form-group">
                                                    <label for="message">{l s='Message'}</label>
                                                    <textarea class="form-control" id="message" name="message">{if isset($message)}{$message|escape:'html':'UTF-8'|stripslashes}{/if}</textarea>
                                            </div>
    
                                            {* ***** Nous allons mettre notre code ICI ***** *}
                                                                         
                                            {hook h='displayGDPRConsent' moduleName='contactform'}
                                     </div>
                            </div>
                            <div class="submit">
                                    <input type="text" name="url" value="" class="hidden" />
                                    <input type="hidden" name="contactKey" value="{$contactKey}" />
                                    <button type="submit" name="submitMessage" id="submitMessage" class="button btn btn-default button-medium"><span>{l s='Send'}<i class="icon-chevron-right right"></i></span></button>
                            </div>
    

    Voici le code à mettre:

    <div class="form-group">
       <label for="captcha">{l s='Captcha'}</label>
       <input name="captcha" type="text" />
       <span class="captcha-help">{l s='Please enter this number in the captcha field'}: {$captval}</span>
    
    </div>
    

    Pour l'instant cette étape ne change rien.
    En effet nous devons maintenant câbler la vue pour que le contrôleur l'appelle.


    On air

    Enfin la dernière étape.
    Nous devons simplement appeler notre vue (le template) et nous aurons un contrôleur complet.

    Revenons dans le contrôleur.
    /override/controllers/front/ContactControllerExtra.php

    <?php
    class ContactControllerExtra extends ContactController
    {
       public $php_self = 'contact-extra'; 
    
      private function trivial_captcha() { return (int)($this->context->cookie->id_guest); }
    
      public function postProcess() {
        if (Tools::isSubmit('submitMessage')) {
          $captcha = Tools::getValue('captcha',0);
          if (!$captcha || $captcha != $this->trivial_captcha()) {
            $this->errors[] = Tools::displayError('Invalid captcha.');
            return;
          }
        }
        return parent::postProcess();
      }
      public function initContent() {
        parent::initContent();
        $this->context->smarty->assign(array(
           'captval' => $this->trivial_captcha(),
        ));
       $this->setTemplate(_PS_THEME_DIR_.'contact-form-extra.tpl');
      }
    }
    

    Il ne vous reste plus qu'a faire quelques réglages css, les traductions et vous avez terminé.

    Bien joué

    posté dans Tutoriels
  • RE: Fourre-tout de trucs et astuces

    Merci doekia !

    posté dans Tutoriels
  • RE: Fourre-tout de trucs et astuces

    @Thierry
    Bien sûr, sans aucun problème. N'oublie pas d'enlever aussi le </IfModule> du premier snippet

    posté dans Tutoriels
  • RE: Fourre-tout de trucs et astuces

    Salut doekia,

    Merci pour tous ces snippets :)

    J'utilise deux de tes snippets dans mon htaccess:

    • la redirection produit après suppression de la catégorie de l'url
    • la redirection après suppression multi-langue

    Je me posais une question: peut-on les regrouper en un seul ?
    C'est à dire supprimer le :

    <IfModule mod_rewrite.c>
        RewriteEngine On
    

    du 2e snippet ?

    posté dans Tutoriels
  • Fourre-tout de trucs et astuces

    J'ai mis à dispositions de nombreux petits snippets qui peuvent servir.
    C'est ici: https://area51.enter-solutions.com/snippets
    Ci-dessous un petit inventaire (incomplet):


    .htaccess (sauf indication contraire, avant le code prestashop)

    Sécurité

    Divers
    posté dans Tutoriels
  • Copieurs-Colleurs

    L'humanité a évolué au fil du temps, de chasseurs-cueilleurs, nomades, inquisiteurs, esclavagistes, ... à nos jours où nous sommes réseauteurs sociaux et copieurs-colleurs.
    Tout comme vous n'iriez pas non préparé chasser le Mesosaure ou cueillir un essaim d'abeille - l'issue serait funeste (Je sais l'un ne vit pas dans la bonne section de la frise chronologique, l'autre n'est pas un fruit - C'est une métaphore) -, vous devez éviter les pièges de la méthode. Voici une liste non exhaustive de ces pièges et comment les éviter:


    Connaître son ennemi

    Ai-je suffisamment de connaissance pour comprendre ce que fait ce code ? A défaut, se documenter, interroger des experts. Copier un code hermétique à votre compréhension et le meilleur moyen d'injecter une porte dérobée dans votre solution.

    Date de fraicheur

    De quand date ce snippet? Cette question simple vous évitera d'utiliser du code deprecated ou une solution obsolète.

    Est-ce comestible

    En raison de typos, malveillance, maladresses l'extrait peut être dangereux. Assurez-vous d'avoir compris les risques.

    Allergies

    Contrôlez vos versions. Vérifiez toujours que la solution est en adéquation avec votre environnement

    Tests clinique

    Toujours passer la solution au banc d'essai. Toujours. Même une seule ligne!

    Testisnulus

    Exactement comme la citation romaine. Ne tester qu'une fois, c'est comme n'avoir rien testé

    Protections

    Toujours, 10x si besoin, faites des backups

    La pierre de rosette

    Copier-coller depuis certaines sources mène à des déformations de contenu (apostrophes, guillemets, backquotes, ...).

    Intelligence

    Faites honneur à votre cerveau et sa capacité d'analyse.

    Obésité

    Si votre copier-coller ressemble à une recette de Maïté vous risquez l'indigestion. De la même manière si vous accumulez les composants, c'est probablement qu'il existe une solution plus simple et élégante.

    Agence tous-risque

    Partir sans plan d'ensemble c'est obtenir une usine à gaz dans le meilleur des cas, un réacteur RBMK hors de contrôle dans le pire.

    Un petit pas pour l'homme

    Faites des petits pas, simple. Vous pourrez alors réaliser de grandes choses.

    Houston nous avons un problème

    Prenez des notes communiquez, expliquez, documentez, écoutez les experts. Seul moyen d'avoir un réponse adaptée en cas de problème.

    Dilettantisme

    Toujours être attentif. L'esprit en alerte à l'écoute du moindre bruit suspect. A surveiller, le lait, la purée de carotte tout en langeant le petit dernier, on termine dans les fais divers d'une revue criminelle, ou on gagne un Darwin Awards.


    Ces bonnes pratiques exprimées, n’empêcheront pas les aventuriers intrépide de faire preuve de témérité paresseuse. Pour cette raison nous essayons lorsque le code est dangereux de le rendre inactif. La solution est sous vos yeux mais si vous vous contentez de la copier telle quelle, elle sera sans effet ou générera une message d'erreur anodin. Lisez l'erreur, googlez là et vous pourrez alors transformer ce plomb en or.

    posté dans Discussion générale
  • [1.3] Implanter des classes overrides

    Le mécanisme des overrides est très pratique pour maintenir le coeur propre tout en ayant la capacité de faire des changements dans le code natif.
    Ce mécanisme n'est apparu qu'a partir de la version 1.4 de PrestaShop.

    Voici une implantation de ce mécanisme sur version antérieure.

    Il suffit d'injecter une fonction autoloader modifié en début du fichier config.inc.php

    /* Autoload */
    function __autoload($className)
    {
      if (function_exists('smartyAutoload') AND smartyAutoload($className))
        return true;
    
      $className = str_replace(chr(0), '', $className);
      $classDir = dirname(__FILE__).'/../classes/';
      $overrideDir = dirname(__FILE__).'/../override/classes/';
      $file_in_override = file_exists($overrideDir.$className.'.php');
      $file_in_classes = file_exists($classDir.$className.'.php');
    
      // This is a Core class and its name is the same as its declared name
      if (substr($className, -4) == 'Core')
        require_once($classDir.substr($className, 0, -4).'.php');
      else
      {
        if ($file_in_override && $file_in_classes)
        {
          require_once($classDir.str_replace(chr(0), '', $className).'.php');
          require_once($overrideDir.$className.'.php');
        }
        elseif (!$file_in_override && $file_in_classes)
        {
          require_once($classDir.str_replace(chr(0), '', $className).'.php');
          $classInfos = new ReflectionClass($className.((interface_exists($className, false) or class_exists($className, false)) ? '' : 'Core'));
          if (!$classInfos->isInterface() && substr($classInfos->name, -4) == 'Core')
            eval(($classInfos->isAbstract() ? 'abstract ' : '').'class '.$className.' extends '.$className.'Core {}');
        }
        elseif ($file_in_override && !$file_in_classes)
        require_once($overrideDir.$className.'.php');
      }
    }
    spl_autoload_register('__autoload');
    

    Créer ensuite votre répertoire /override/classes

    Pour toute classe que vous souhaitez modifier, changez le nom de la classe native dans son fichier pour la renommer en <class>Core
    Exemple pour la classe "Order":

    class OrderCore extends ObjectModel
    

    Puis créer votre fichier override comme dans les version ultérieures.

    Pratique pour normaliser tranquillement votre code en prévision d'une future migration
    NOTE: Lors de votre upgrade, prenez garde à la structure du répertoire classes (ex: Order.php => order/Order.php)

    posté dans Tutoriels
  • Partagez

    Ce forum à besoin de plus de visibilité pour vivre.
    Si vous aimez qu'il survive faites le connaître, partagez, répondez, questionnez.

    posté dans Annonces
  • [1.6.0.5 - 1.6.1.18] Fatal error lors de la suppression d'image du themeconfigurator

    Lors de la suppression d'image du themeconfigurator vous obtenez l'erreur:
    Call to a member function rowCount() on boolean
    ou
    Table 'presta16.ps_ps_themeconfigurator' doesn't exist avec le mode debug activé


    Modifiez le fichier modules/themeconfigurator/themeconfigurator.php @397
    Remplacer

    Db::getInstance()->delete(_DB_PREFIX_.'themeconfigurator', 'id_item = '.(int)$id_item);
    

    par

    Db::getInstance()->delete('themeconfigurator', 'id_item = '.(int)$id_item);
    
    posté dans BUG connus
  • RE: Comment offrir frais de port à partir d'un certain montant MAIS avec transport facturé au poids ?

    @lionhellll

    Réponse courte:
    règle panier

    Réponse longue:
    Suis les recommandations de la solution numéro 2 - ici
    https://aide.prestashop.click/topic/35/je-veux-offrir-les-frais-de-port-a-partir-d-un-montant-mais-seulement-pour-la-france

    posté dans Discussion générale