Prestashop 1.7 : Utilisation des formfields

La version 1.7 rajoute une nouvelle notion pour gérer les champs sur certains formulaires front ( Client et Adresse entre autre )
Ceux-ci sont géré via la classe FormField et leur affichage est géré dans le template themes/themeName/templates/_partials/form-fields.tpl

Les méthodes utiles pour gérer ces champs sont les suivantes :

  • setName: Défini le nom du champ
  • setType: Défini le type du champ ( text/checkbox .. ) voir liste ci-dessous
  • setRequired : Défini si le champ est obligatoire ou non
  • setLabel : Défini le label du champ
  • setValue : Défini la valeur du champ
  • setAvailableValues : Défini les valeurs disponibles pour le champ, pour les select entre autre
  • addAvailableValue : Ajout une valeur disponible pour le champ
  • setMaxLength: Longueur maximum du champ
  • setConstraint : Ajoute une contrainte de validation au champ , les méthodes appelables sont celles de la classe Validation ( ex isEmail )

Pour l’exemple j’ai ajouté des nouveaux champs au formulaire de création de compte client via le hook hookAdditionalCustomerFormFields

Voici l’ensemble des  types de champs possibles :

return [
    //Champ texte standard
    (new FormField)
        ->setName('professionnal_id')
        ->setType('text')
        ->setRequired(true) //Décommenter pour rendre obligatoire
        ->setValue("TEST")
        ->setMaxLength("10")
        ->setLabel($this->l('Professionnal id')),
    //Champ File
    (new FormField)
        ->setName('justificatif_upload')
        ->setType('file')
        ->setLabel($this->l('document ID')),
    //Select
    (new FormField)
        ->setName('select_field')
        ->setType('select')
        ->setAvailableValues(array('key' => 'value 1', 'key2' => 'value2'))
        ->setLabel($this->l('Select type')),
    //countrySelect ( idem select mais rajoute une classe js js-country
    (new FormField)
        ->setName('country_field')
        ->setType('countrySelect')
        ->setAvailableValues(['key' => 'value 1', 'key2' => 'value2'])
        ->setLabel($this->l('Country Select')),
    //Checkbox
    (new FormField)
        ->setName('checkbox_field[]')
        ->setType('checkbox')
        ->setValue(1)
        ->setLabel($this->l('Checkbox type')),
    //radio-buttons
    (new FormField)
        ->setName('radio_field')
        ->setType('radio-buttons')
        ->setAvailableValues(array('key' => 'value 1', 'key2' => 'value2'))
        ->setLabel($this->l('Radio buttons type')),
    //date
    (new FormField)
        ->setName('date_field')
        ->setType('date')
        ->setLabel($this->l('Date')),
    //birthday
    (new FormField)
        ->setName('birthday_field')
        ->setType('birthday')
        ->setLabel($this->l('birthday')),
    //password
    (new FormField)
        ->setName('password_field')
        ->setType('password_')
        ->setLabel($this->l('password_')),
    //Champ email
    (new FormField)
        ->setName('email_field')
        ->setType('email')
        ->setLabel($this->l('email type')),
    //Champ tel
    (new FormField)
        ->setName('phone_field')
        ->setType('phone')
        ->setLabel($this->l('Phone type')),
    //Champ caché
    (new FormField)
        ->setName('hidden_field')
        ->setType('hidden')
        ->setValue('My hidden value')
];

Et leur visualisation :

Form fields prestashop

Pas de commentaires

Prestashop 1.7 : Récupérer des informations personnalisées dans le panier

J’ai récemment fait face à un problème, car je souhaitais remontée un champ produit spécifique dans les informations du panier sur prestashop 1.7
( La modification décrite fonctionne également sur prestashop 1.6.1.x )

Après analyse, il s’avère que les informations disponibles sont remontées depuis la fonction getProducts de la classe Cart.

L’ensemble des champs remontés sont listé dans la requête sql de remontée des produits, et il n’est pas possible de récupérer nos nouveaux champs.

// Build query
$sql = new DbQuery();
 
// Build SELECT
$sql->select('cp.`id_product_attribute`, cp.`id_product`, cp.`quantity` AS cart_quantity, cp.id_shop, cp.`id_customization`, pl.`name`, p.`is_virtual`,
pl.`description_short`, pl.`available_now`, pl.`available_later`, product_shop.`id_category_default`, p.`id_supplier`,
p.`id_manufacturer`, m.`name` AS manufacturer_name, product_shop.`on_sale`, product_shop.`ecotax`, product_shop.`additional_shipping_cost`,
product_shop.`available_for_order`, product_shop.`show_price`, product_shop.`price`, product_shop.`active`, product_shop.`unity`, product_shop.`unit_price_ratio`,
stock.`quantity` AS quantity_available, p.`width`, p.`height`, p.`depth`, stock.`out_of_stock`, p.`weight`,
p.`available_date`, p.`date_add`, p.`date_upd`, IFNULL(stock.quantity, 0) as quantity, pl.`link_rewrite`, cl.`link_rewrite` AS category,
CONCAT(LPAD(cp.`id_product`, 10, 0), LPAD(IFNULL(cp.`id_product_attribute`, 0), 10, 0), IFNULL(cp.`id_address_delivery`, 0), IFNULL(cp.`id_customization`, 0)) AS unique_id, cp.id_address_delivery,
product_shop.advanced_stock_management, ps.product_supplier_reference supplier_reference');
 
// Build FROM
$sql->from('cart_product', 'cp');
 
...

Vous pouvez consulter le fichier complet ici : https://github.com/PrestaShop/PrestaShop/blob/1.7.3.x/classes/Cart.php#605

La bonne nouvelle c’est que la requête est effectuée via un objet Dbquery, ce qui va nous permettre d’interférer assez facilement avec la requête.

Pour ajouter une fonctionnalité de modification de cette requête nous allons donc créer une surcharge de la classe Cart qui va reprendre la function getProducts de la classe parente et rajouter le contenu suivant :

// Nouveau hook pour pouvoir modifier la requête de récupération des produits
Hook::exec('actionCartGetProductsSqlQuery',['query' => &$sql]);

juste avant l’exécution de la requête dans la ligne

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

Via ce hook nouvellement créé auquel l’instance de l’objet DqQuery est passé en paramètre, il est à présent possible de faire un peu prêt tout sur cette requête.
La variable étant passée par référence, les modifications sur la requête sont appliquées directement.

Pour ajouter un champ il suffira donc de créer un module et de le greffer sur le hook actionCartGetProductsSqlQuery

/**
 * Nouveau Hook pour modifier la requête de sélection des produits du panier
 * Cf. override du cart
 * @param array $params => 'query' => &DbQuery
 */
public function hookActionCartGetProductsSqlQuery($params)
{
    $params['query']->select('p.marque');
}
Pas de commentaires

Prestashop : Modifier les listings dans l’administration

En complément d’un de mes précédents articles sur comment ajouter des nouveaux champs dans le listing des produits ( pour prestashop 1.7 ), nous allons voir comment effectuer cette action sur les autres controllers de l’administration.

Les listing des controllers concernés sont ceux qui utilisent encore l’ancienne infrastructure et les anciennes méthodes ( Clients, adresses, commandes , employés … )

Ce tutoriel fonctionne à partir de la version 1.6.0.2 de Prestashop et utilise le hook dynamique action.$this->controller_name.ListingFieldsModifier

Pour les versions inférieures il sera nécessaire de faire un override du controller concerné et d’ajouter les informations directement à la suite des paramètres de classe
$this->_select,
$this->_join,
$this->_where

Exécuté par via le code suivant dans la fonction getList de la classe AdminController

Hook::exec('action'.$this->controller_name.'ListingFieldsModifier', array(
'select' => &$this->_select,
'join' => &$this->_join,
'where' => &$this->_where,
'group_by' => &$this->_group,
'order_by' => &$this->_orderBy,
'order_way' => &$this->_orderWay,
'fields' => &$this->fields_list,
));

Les paramètres sont passés par référence au hook ce qui permets de modifier directement les variables qui seront appellées depuis un module.

Les éléments select, join, where, group_by, order_by , order_way permettent de modifier la requête de récupération de la liste des entités.
Alors que le champ fields est un tableau qui regroupe l’ensemble des colonnes qui seront affichées dans la liste.

Pour tester tout cela nous allons faire un module hh_admin dont le but sera le suivant :

– Ajouter 2 nouvelles colonnes dans le listing des clients et supprimer 2 existants :

Le module devra donc implémenter le hook suivant :

  • actionAdminCustomersListingFieldsModifier

Pour modifier le listing client le contenu sera le suivant :

    /**
     * Ajout de champs dans le listing des clients
     * @param type $params
     */
    public function hookActionAdminCustomersListingFieldsModifier($params)
    {
        //Pour l'exemple on va ajouter 2 champs dans le listing
 
        // champ simple texte : date d'inscription à la newsletter ( newsletter_date_add dans la table customer )
        $params['fields']['newsletter_date_add'] = array(
            'title' => $this->l('Date souscription Newsletter')
        );
 
        //Champ un peu plus "complexe" groupe par défaut  select ( id_default_group dans la table customer et récupération des noms des groupes via une jointure )
        $params['select'] .= ' ,grl.name as default_group_name '; //Ajout du champ dans la requête select
        $params['join'] .= 'LEFT JOIN '._DB_PREFIX_.'group_lang grl ON (a.id_default_group = grl.id_group )'; //Jointure avec la table des groupes
 
	//Récupération de la liste des groupes via la méthode prestashop + création d'un tableau sous la forme id_group => name 
	$groups = Group::getGroups($this->context->language->id); 
        $groupsList = array();
        foreach( $groups as $group)
            $groupsList[$group['id_group']] = $group['name'];
 
        //Ajout du nouveau champ à afficher dans le listing
        $params['fields']['default_group_name'] = array(
            'title' => $this->l('Groupe par défaut'),
            'type' => 'select',
            'list' => $groupsList,
            'filter_key' => 'a!id_default_group' //Clé de filtrage dans la requête sous la forme "NomTable!champ"  
        );
 
	//Suppression des champs "Titre","Optin"
        unset($params['fields']['title']);
        unset($params['fields']['optin']);
    }

Une fois le code implémenté nous voyons bien nos 2 nouvelles colonnes dans la liste et les 2 anciennes ont bien disparu.
Tout est fonctionnel sans la moindre surcharge 🙂



Bonus :
Une demande récurrente est également de pouvoir placer les nouveaux champs à un autre endroit qu’a la fin.

Pour cela j’ai créé la fonction suivante :

    /**
     * Ajout du nouveau champ après un autre 
     * @param type $fieldParams
     * @param type $afertName
     * @param type $fieldsArray
     */
    public function addNewFieldAfter($fieldParams,$afterName,array &$fieldsArray)
    {
 
        if (array_key_exists($afterName, $fieldsArray)) {
            $pos = array_search($afterName, array_keys($fieldsArray));
            $before = array_slice($fieldsArray,0,$pos+1);
            $after = array_slice($fieldsArray, $pos); 
            $fieldsArray = array_merge($before,$fieldParams,$after);
 
        } else {
            $fieldsArray = array_merge($fieldsArray,$fieldParams);
        }
    }

Il est possible de l’utiliser de cette manière :

 /**
     * Ajout de champs dans le listing des clients
     * @param type $params
     */
    public function hookActionAdminCustomersListingFieldsModifier($params)
    {
 
        $newField = array( 'default_group_name' => 
		array(
            'title' => $this->l('Groupe par défaut'),
            'type' => 'select',
            'list' => $groupsList,
            'filter_key' => 'a!id_default_group' //Clé de filtrage dans la requête sous la forme "NomTable!champ" 
            )
        );
 
        $this->addNewFieldAfter($newField, 'lastname', $params['fields']);
 
    }

Voici le code code complet du module :

<?php
class HhAdmin extends Module {
 
    public function __construct() {
        $this->name = 'hhadmin';
        $this->tab = 'others';
        $this->author = 'hhennes';
        $this->version = '0.1.0';
        $this->need_instance = 0;
        $this->bootstrap = true;
 
        parent::__construct();
 
        $this->displayName = $this->l('hhadmin');
        $this->description = $this->l('hhadmin description');
        $this->ps_versions_compliancy = array('min' => '1.7.0', 'max' => _PS_VERSION_);
    }
 
    /**
     * @return boolean
     */
    public function install() {
        if (!parent::install() 
                || !$this->registerHook('actionAdminCustomersListingFieldsModifier')      
        ) {
            return false;
        }
 
        return true;
    }
 
    /**
     * Ajout de champs dans le listing des clients
     * @param type $params
     */
    public function hookActionAdminCustomersListingFieldsModifier($params)
    {
        //Pour l'exemple on va ajouter 2 champs dans le listing
 
 
        // champ simple texte : date d'inscription à la newsletter ( dans la table customer )
        $params['fields']['newsletter_date_add'] = array(
            'title' => $this->l('Newsletter subscribe date')
        );
 
        //Champ un peu plus "complexe" groupe par défaut  select ( id_default_group dans la table customer 
        //et récupération des noms des groupes via une jointure )
        $params['select'] .= ' ,grl.name as default_group_name '; //Ajout du champ dans la requête select
        $params['join'] .= 'LEFT JOIN '._DB_PREFIX_.'group_lang grl ON (a.id_default_group = grl.id_group )'; //Jointure avec la table des groupes
        $groups = Group::getGroups($this->context->language->id); //Récupération de la liste des groupes
 
        $groupsList = array();
        foreach( $groups as $group)
            $groupsList[$group['id_group']] = $group['name'];
 
       //Affichage du nouveau champ ( méthode standard)
        /*$params['fields']['default_group_name'] = array(
            'title' => $this->l('Groupe par défaut'),
            'type' => 'select',
            'list' => $groupsList,
            'filter_key' => 'a!id_default_group' //Clé de filtrage dans la requête sous la forme "NomTable!champ"  
        );*/
 
		//Ajout du nouveau champ dans une certaine position
        $newField = array( 'default_group_name' => array(
            'title' => $this->l('Groupe par défaut'),
            'type' => 'select',
            'list' => $groupsList,
            'filter_key' => 'a!id_default_group' //Clé de filtrage dans la requête sous la forme "NomTable!champ" 
            )
        );
        $this->addNewFieldAfter($newField, 'lastname', $params['fields']);
    }
 
	 /**
     * Ajout du nouveau champ après
     * @param type $fieldParams
     * @param type $afertName
     * @param type $fieldsArray
     */
    public function addNewFieldAfter($fieldParams,$afterName,array &$fieldsArray)
    {
 
        if (array_key_exists($afterName, $fieldsArray)) {
            $pos = array_search($afterName, array_keys($fieldsArray));
            $before = array_slice($fieldsArray,0,$pos+1);
            $after = array_slice($fieldsArray, $pos); 
            $fieldsArray = array_merge($before,$fieldParams,$after);
 
        } else {
            $fieldsArray = array_merge($fieldsArray,$fieldParams);
        }
    }
}
Pas de commentaires

Prestashop 1.7 : Dynamisez vos contenus wysiwyg

La problématique n’est pas nouvelle sur Prestashop, et c’est un point qui me frustre assez souvent et pour lequel j’avais déjà fait un module en 2014 ( cf. https://www.h-hennes.fr/blog/2014/01/18/prestashop-liens-dynamiques-vers-les-pages-cms-dans-lediteur-tinymce/ )
Ce module n’est malheureusement plus fonctionnel sur Prestashop 1.7.

Dans les éditeurs de contenus, il n’est pas possible  des mettre des liens ou des contenus dynamiques.
La bonne nouvelle cependant c’est que la version 1.7 de Prestashop apporte de nouveaux hooks qui vont permettre de modifier ces contenus avant l’affichage des éléments, et donc sans surcharge  🙂

Les éléments dont les contenus sont modifiables sont les suivants :

  • Pages cms
  • Catégories cms
  • Contenu produit
  • Contenu catégorie
  • Contenu Fabriquants
  • Contenu fournisseurs

Pour l’exemple on va partir sur le besoin suivant :

Je souhaite afficher un élément de configuration  dans l’ensemble des contenus noté ci-dessous.
Ce sera la configuration PS_SHOP_EMAIL , qui correspond à l’émail par défaut de la boutique.

Pour afficher cette variable il faudra intégrer dans les zones de contenus le code suivant :

{{configuration name=PS_SHOP_EMAIL}}

Le but du module sera donc de remplacer cet élément par sa valeur de configuration.

Voici le contenu qui sera saisi dans l’admin :

Cms placeholder

Et le résultat :

Cms placeholder result

Vous trouverez ci-dessous le code complet du module qui permets de gérer cela

<?php
class HhContentVars extends Module {
 
    public function __construct() {
        $this->name = 'hhcontentvars';
        $this->tab = 'others';
        $this->author = 'hhennes';
        $this->version = '1.0.0';
        $this->need_instance = 0;
        $this->bootstrap = true;
 
        parent::__construct();
 
        $this->displayName = $this->l('Hh content var');
        $this->description = $this->l('Add content var in wysiwyg editors');
    }
 
    public function install() {
        if (!parent::install() 
                //Hooks Fronts d'affichage
                || !$this->registerHook('filterCmsContent') 
                || !$this->registerHook('filterCmsCategoryContent') 
                || !$this->registerHook('filterProductContent') 
                || !$this->registerHook('filterCategoryContent') 
                || !$this->registerHook('filterManufacturerContent') 
                || !$this->registerHook('filterSupplierContent')
        )
            return false;
 
        return true;
    }
 
 
    /**
     * Filtre des contenus Cms
     * @param $params
     * @return array
     */
    public function hookFilterCmsContent($params) {
 
        $params['object']['content'] = $this->_updateContentVars($params['object']['content']);
 
        return [
            'object' => $params['object']
        ];
    }
 
    /**
     * Filtre des contenus des catégories cms
     * @param $params
     * @return array
     */
    public function hookFilterCmsCategoryContent($params) {
 
        $params['object']['description'] = $this->_updateContentVars($params['object']['description']);
 
        return [
            'object' => $params['object']
        ];
    }
 
    /**
     * Filtre des contenu des produits
     * @param $params
     */
    public function hookFilterProductContent($params) {
        $params['object']['description'] = $this->_updateContentVars($params['object']['description']);
        return [
            'object' => $params['object']
        ];
    }
 
    /**
     * Filtre des contenus des catégories produits
     * @param $params
     * @return array
     */
    public function hookFilterCategoryContent($params) {
 
        $params['object']['description'] = $this->_updateContentVars($params['object']['description']);
 
        return [
            'object' => $params['object']
        ];
    }
 
    /**
     * Filtre des contenus des Marques
     * @param $params
     * @return mixed
     */
    public function hookFilterManufacturerContent($params) {
 
        return $this->_updateContentVars($params['filtered_content']);
    }
 
    /**
     * Filtre des contenus des fournisseurs
     * @param $params
     * @return array
     */
    public function hookFilterSupplierContent($params) {
        $params['object']['description'] = $this->_updateContentVars($params['object']['description']);
 
        return [
            'object' => $params['object']
        ];
    }
 
    /**
     * Mise à jour du contenu
     * @param string : contenu ou il faut remplacer les variables
     * @return string : contenu avec les variables remplacées
     */
    protected function _updateContentVars($content) {
 
        $content = urldecode($content);
 
        //Récupération des éléments de configuration
        preg_match_all('#{{configuration name=(.*)}}#i', $content, $configurations);
        if (isset($configurations[1]) && sizeof($configurations[1])) {
            foreach ($configurations[1] as $conf) {
                if ( $value = Configuration::get($conf) ) {
                    $content = preg_replace('#{{configuration name=' . $conf . '}}#',$value, $content);
                }
            }
        }
 
        return $content;
    }
}

Ceci n’est qu’un exemple, il donc possible de mettre en place autant de tags que souhaités.
Si j’ai le temps durant les prochaines semaines je verrais peu être pour mettre à jour mon module précédent uniquement PS 1.7

2 commentaires

Monitorez vos serveurs avec netdata

J’ai réinstallé mon serveur dédié sous ubuntu 16.04 récement, j’en ai profité donc pour chercher des nouveaux outils de monitoring, et je suis tombé sur « netdata » que j’avais déjà croisé il y’a pas mal de temps mais que je n’avais pas encore installé.

La solution est disponible sur github avec une documentation très complète à l’adresse suivante :
https://github.com/firehol/netdata/wiki/Installation

Il faut reconnaitre que cette solution est réussie graphiquement et très complète ( ça ressemble à un htop très très amélioré )
Nous allons voir comment installer et configurer basiquement cette solution.

Installer via la ligne de commande suivante :

bash <(curl -Ss https://my-netdata.io/kickstart.sh)

( Si curl n’est pas intallé apt-get install curl )

La durée du script d’installation dure environ 5 minutes :

Dans un premier temps il vérifie les prérequis et identifie les librairies manquantes sur votre serveur.
Et vous propose ensuite de les installer.

net data install 1

Vous pouvez ensuite paramétrer l’installation de la solution.
Pour ma part j’ai tout laissé par défaut.

net data install 2

Vous pouvez ensuite accéder à vos statistiques depuis l’ip de votre serveur sur le port 19999, voici une capture d’un des paramètre présent, mais il y’en a des 100 aines disponibles !!

Pour finaliser l’installation vous pouvez rajouter un filtrage ip via la configuration en éditant le fichier
/etc/netdata/netdata.conf et en supprimant le commentaire et en ajoutant votre ip sur la ligne

# allow connections from = localhost

Rechargez ensuite le service pour appliquer ce changement

sudo service netdata restart
Pas de commentaires

Eicaptcha : Ajout d’une configuration avancées des sélecteurs

J’ai eut beaucoup de retours ces dernières semaines sur le non-fonctionnement de mon module de captcha sur les thèmes différents de default-boostrap ( celui par défaut ).
Ceci était du au fait que j’avais stocké directement dans le code des sélecteurs css spécifiques à ce thème.

Vous pouvez le voir sur l’extrait de code suivant :

<script type="text/javascript">
 $(document).ready(function(){
 //Add div where the captcha will be displayed
 $(".submit").before("<div id=\"captcha-box\"></div>"); // <= sélecteur stocké en dur
 
 //Manage form submit 
 $("#submitMessage").click(function(){ // <= sélecteur stocké en dur
 ...
 </script>

La nouvelle version qui sortira dans les prochains jours corrigera ce problème, en permettant d’éditer ces informations directement depuis l’administration.
Dans un nouvel onglet « Paramètres avancés »

captcha-configuration-avancee

Récupération des sélecteurs de votre thème

Attention car la récupération des éléments est assez technique.
Si la notion vous est totalement étrangère je vous invite à consulter la documentation sur ce sujet.

Le plus pratique est d’utiliser les options de développement de votre navigateur  qui s’activent via la touche F12
Une fois ceux-ci activés.

Sélecteur du bouton de soumission du formulaire :
Faites un clic droit sur le bouton de soumission du formulaire et cliquer sur « Examiner l’élément » ( Firefox ) ou « Inspecter » (chrome)
Vous devrez obtenir une capture de ce genre :

Inspecteur bouton formulaire

Dans le cas de cet capture le sélecteur à saisir est #submitMessage car le bouton de soumission a l’id égal à submitMessage.

 

Sélecteur insertion bloc captcha :

Celui-ci est encore un peu plus complexe, car il faut définir l’élément css AVANT LEQUEL nous souhaitons ajouter le bloc de captcha.
Il faut également s’assurer que le bloc sera bien entre les balises <form> et </form> pour que les données soient bien envoyées.

Cela correspond à cette partie du javascript :

$(".submit").before("<div id=\"captcha-box\"></div>");

Dans la configuration par défaut le sélecteur est .submit le bloc du captcha et donc ajouté avant la div « submit » comme vous pouvez voir sur la capture :

Captcha inspecteur container

Pour ceux qui ne sont pas trop à l’aise avec cet élément, il est également possible de rajouter à l’emplacement ou vous souhaitez afficher le captcha directement le code :

<div id="captcha-box"></div>

Pour tous les autres détails de ce module merci de consulter la page spécifique : Module captcha Prestashop

3 commentaires

Prestashop : Modules gratuits pour optimiser votre seo

Dans l’ensemble de mes derniers projets prestashop, j’ai été confronté à la mise en place de redirections et la nécessiter d’édition du fichier robots.txt

La bonne nouvelle c’est qu’il existe des modules gratuits pour cela et qu’il font très bien le job pour toutes les versions de prestashop.
Il est directement possible d’éditer ces fichiers depuis l’administration, ce qui est rapide et  donne la main au webmaster pour les gérer directement.

C’est l’occasion pour moi de les partager :-), je les inclus dorénavant dans  mon installation de base.

Module édition de .htaccess :

htaccess editor

https://dh42.com/free-prestashop-modules/prestashop-htaccess-module/

Module édition du fichier robots.txt :

Robots.txt editor

https://dh42.com/free-prestashop-modules/prestashop-robots-txt-module/

Pas de commentaires

Publier des releases github via un script

Pour une fois un article qui n’est pas lié directement à une technologie en particulier , mais à la plateforme de sources github.
J’ai plusieurs modules qui sont disponibles sur cette plateforme et pour lequel un clone du dépôt ne permets pas d’obtenir un module fonctionnel.

Pour  cette raison il est nécessaire de créer des « releases » qui sont disponibles sur une page spécifique du dépot ( ex:  https://github.com/nenes25/eicaptcha/releases ) et sur lequel on peut mettre à disposition une archive ( ou tout autre fichier ) associé à la release à télécharger :

Ei captcha releases

Il est possible de faire cela manuellement directement depuis l’interface github, mais par principe manuellement c’est plus lent et plus souvent source d’erreurs qu’un script automatique.
Nous allons donc voir comment scripter tout ça en utilisant l’api v3 de github et curl.

Sachant que dans mon cas j’ai les prérequis suivants :

Je pense qu’il est possible de faire l’ensemble de la procédure ci-dessous via un script bash, mais pour moi c’était plus facile de le faire en php.

Le script nécessitera 3 fichiers situés dans un même emplacement :

  • config.php //(optionnel)
  • create_release.sh //Fichier shell
  • create_release.php // Fichier php

Le fichier config.php contiendra uniquement les identifiants d’accès à github.
( Dans le cas d’un script privé vous pouvez tout à fait les intégrer directement dans le script )

<?php 
$github_user = 'githubuser'; 
$github_password = 'githubpassword';

Le fichier create_release.sh est très basique :
il créé le tag git et le pousse sur github puis lance le script php

#! /bin/bash
 
if [ -z "$1" ]
  then
    echo "Please provide version number"
fi
 
version=$1
 
#Création du tag git et on le pousse en ligne 
git tag $version
git push --tags
 
#Lancement du script php qui se charge des autres tâches
php create_release.php $version

Tout la logique de livraison de la release est réalisée dans le fichier create_release.php

<?php
require_once dirname(__FILE__).'/config.php';
 
$baseApiUrl = 'https://api.github.com/repos/userName/RepoName/'; //Url de base du dépot
 
//On vérifie qu'un numéro de release est passé au script
if ($argc < 2) { exit("Please give a release number \n"); } //On vérifie que le numéro $release = $argv[1]; echo "Check if the release exists \n"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $baseApiUrl.'releases/tags/'.$release); $curlGlobalOptions = array( CURLOPT_USERAGENT => $github_user,
    CURLOPT_USERNAME => $github_user,
    CURLOPT_PASSWORD => $github_password,
    CURLOPT_RETURNTRANSFER => true, //Response in variable
);
 
//Curl options
curl_setopt_array($ch, $curlGlobalOptions);
 
$content = curl_exec($ch);
$info    = curl_getinfo($ch);
curl_close($ch);
 
//Si le tag existe déjà inutile de continuer le script
if ($info['http_code'] == 200) {
    echo "this tag already exists \n";
    exit('end of the script');
}
 
//Sinon création de la release
echo "Creation of the release \n";
$releaseDatas = array(
    "tag_name" => $release,
    "target_commit" => 'master', // Mettre ici la branche cible
    "name" => $release,
    "body" => "Description de la release ".$relase.' see changelog',
    //Passer à true pour debug
    "draft" => false,
    "prerelease" => false,
);
 
$curlDraft = curl_init();
curl_setopt_array($curlDraft, $curlGlobalOptions);
curl_setopt($curlDraft, CURLOPT_URL, $baseApiUrl.'releases');
curl_setopt($curlDraft, CURLOPT_POSTFIELDS, json_encode($releaseDatas));
 
$draftExec = curl_exec($curlDraft);
$draftInfo = curl_getinfo($curlDraft);
 
if ($draftInfo['http_code'] == '201') {
    echo "Release created with success \n";
} else {
    exit("Error during the creation of the release \n");
}
curl_close($curlDraft);
 
//Traitement de la réponse
$draftResponse  = json_decode($draftExec);
$assetUploadUrl = str_replace('{?name,label}', '', $draftResponse->upload_url);
 
//Logique de création du fichier joint à implémenter
 
//Ajout de la pièce jointe à la release
$archive = 'test.zip';
echo "Add zip archive to release \n";
 
$curlUpload = curl_init();
curl_setopt_array($curlUpload, $curlGlobalOptions);
curl_setopt($curlUpload, CURLOPT_URL,$assetUploadUrl.'?name='.urlencode($archive));
curl_setopt($curlUpload, CURLOPT_HTTPHEADER,
    array(
    'Content-Type: application/zip' //
    )
);
curl_setopt($curlUpload, CURLOPT_POSTFIELDS, file_get_contents($archive));
$uploadExec = curl_exec($curlUpload);
$uploadInfo = curl_getinfo($curlUpload);
curl_close($curlUpload);
 
echo "The relase is published on github \n";

Pour publier la release il suffit ensuite d’appeler le script via la commande

 ./create_release.sh 2.0.2
Pas de commentaires

Mise en place d’un bouton « Faire un don » via Paypal

Un petit article non technique pour une fois pour vous signaler que comme vous l’aurez peut-être remarqué ( ou pas il n’est pas ultra-visible ^^ ),  un nouveau bouton « Faire un don » est apparu en bas de la colonne de droite.

Celui-ci vous permets à l’envie de faire un don via Paypal pour que m’encourager à continuer à publier des articles explicatifs sur les dernières versions de Prestashop et Magento 🙂
Je n’en attends pas grand chose mais si ça peut me financer une partie de mes hébergements webs ça serait top.

Promis c’est le dernier article non technique avant encore un bon moment 😉

Pas de commentaires

Prestashop 1.7 : Nettoyer la liste des modules

Dans le listing des modules Prestashop, un point m’a toujours dérangé.
Des modules partenaires et payants remontent et se mélangent aux modules installés sur le site, ce qui fait que c’est souvent le bordel pour s’y retrouver.
J’avais déjà cherché un peu comment remédier à ce point sur la version 1.6 sans trouver de solution satisfaisante …

La bonne nouvelle sur cette version 1.7, c’est que ça devient relativement facile à faire  !  ( Par contre je n’ai pas dit que c’était propre 😉 )

La modification doit être réalisée dans une partie de Prestashop qui n’est pas suchargeable, il est donc nécessaire de modifier le fichier coeur

src/Core/Addon/Module/ModuleRepository.php

Dans la fonction getList() , mettez le contenu suivant :

public function getList()
    {
        //On ne renvoie que la liste des modules sur le disque ( c'est à dire physiquement présents sur le serveur)
        return $this->getModulesOnDisk();
        /*
         Ancien Code
        return array_merge(
            $this->getAddonsCatalogModules(),
            $this->getModulesOnDisk()
        );*/
    }

Voici un aperçu de la page des modules avant  la modification

Liste des modules avant modification

Et après 🙂

Liste modules après filtre

Seuls les modules physiquement présents sur le serveur sont listés, ce qui est beaucoup plus clair et plus propre de mon point de vue.

2 commentaires
Magento certified developper
Modules Prestashop
Compte Github