KrISS feed 8.7 - Ein einfacher und schlauer (oder dummer) Feed-Reader. Von Tontof
  • Sunday 28 June 2015 - 11:33

    Un nouveau billet pense bête ...

    A l'installation de Mysql selon la version ou la façon de l'installer, les Time Zone nommées ne sont pas toujours ajoutées dans la base de données mysql. Voilà la petite ligne de commande à exécuter (sous linux) pour y remédier ( les ajouter )

    mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u <utilisateur admin> mysql
    

    Et voilà .. vous pouvez utiliser les requête

    SET time_zone = timezonename;
    

    Restez informé

    inscrivez vous à la newsletter pour recevoir les nouveaux billets par mail. ( formulaire en haut à droite )

  • Sunday 28 June 2015 - 11:33

    logo.png Un petit truc tout bête bien pratique, à tel point que je ne comprends pas pourquoi ce n'est pas en place dans les fonctionnalités de base de prestashop.

    Le besoin

    Prestashop 1.6

    Lorsqu'un employé crée un compte utilisateur depuis le backoffice de prestashop, aucun mail n'est envoyé au client final ( le mail de bienvenue contenant ses identifiants pour se connecter ), de plus l'employé doit entrer lui-même un mot de passe, ce qui n'est pas des plus pratique, surtout lorsque l'employé est en manque d'imagination, on peut se retrouver alors dans une problématique de sécurité non négligeable.

    La solution

    Nous allons simplement permettre à l'employé de laisser le champs "passwd" vide, il sera alors automatiquement généré de façon aléatoire.
    Enfin, nous allons ajouter un bouton "On/Off" dans le formulaire de création de compte qui va permettre à l'employé de décider si oui ou non les identifiants seront envoyés au client.

    Le code

    Bien commençons par créer la classe de surcharge

    override/controllers/admin/AdminCustomersController.php
    

    Nous allons donc surcharger les méthodes suivantes :

    1. processAdd()
    2. renderForm()

    Et créer une fonction

    1. sendConfirmationMail()

    Voici donc le code complet de notre fichier AdminCustomersController.php

    <?php
    /*
    *  05-2015
    *
    *  @author Christophe De Saint Leger 
    *  @Description Surcharge Formulaire création compte depuis le BackEnd
    */
    class AdminCustomersController extends AdminCustomersControllerCore
    {
    
    
            public function processAdd()
            {
                if (Tools::getValue('submitFormAjax'))
                    $this->redirect_after = false;
                // Check that the new email is not already in use
                $customer_email = strval(Tools::getValue('email'));
                $customer = new Customer();
                if (Validate::isEmail($customer_email))
                    $customer->getByEmail($customer_email);
                if ($customer->id)
                {
                    $this->errors[] = Tools::displayError('An account already exists for this email address:').' '.$customer_email;
                    $this->display = 'edit';
                    return $customer;
                }
                elseif (trim(Tools::getValue('passwd')) == '')
                {
                    $_POST['passwd'] = Tools::passwdGen();
                }
                if ($customer = parent::processAdd())
                {
                    $this->context->smarty->assign('new_customer', $customer);
                    if( Tools::getValue('sendWelcomeEmail') ){
                        $this->sendConfirmationMail($customer);
                    }
                    return $customer;
                }
                return false;
            }
    
    
    
    
            public function renderForm()
            {
    
                if (!($obj = $this->loadObject(true)))
                    return;
                
                $genders = Gender::getGenders();
                $list_genders = array();
                foreach ($genders as $key => $gender)
                {
                    $list_genders[$key]['id'] = 'gender_'.$gender->id;
                    $list_genders[$key]['value'] = $gender->id;
                    $list_genders[$key]['label'] = $gender->name;
                }
    
                $years = Tools::dateYears();
                $months = Tools::dateMonths();
                $days = Tools::dateDays();
    
                $groups = Group::getGroups($this->default_form_language, true);
                $this->fields_form = array(
                    'legend' => array(
                        'title' => $this->l('Customer'),
                        'icon' => 'icon-user'
                    ),
                    'input' => array(
                        array(
                            'type' => 'radio',
                            'label' => $this->l('Social title'),
                            'name' => 'id_gender',
                            'required' => false,
                            'class' => 't',
                            'values' => $list_genders
                        ),
                        array(
                            'type' => 'text',
                            'label' => $this->l('First name'),
                            'name' => 'firstname',
                            'required' => true,
                            'col' => '4',
                            'hint' => $this->l('Invalid characters:').' 0-9!&lt;&gt;,;?=+()@#"°{}_$%:'
                        ),
                        array(
                            'type' => 'text',
                            'label' => $this->l('Last name'),
                            'name' => 'lastname',
                            'required' => true,
                            'col' => '4',
                            'hint' => $this->l('Invalid characters:').' 0-9!&lt;&gt;,;?=+()@#"°{}_$%:'
                        ),
                        array(
                            'type' => 'text',
                            'prefix' => '<i class="icon-envelope-o"></i>',
                            'label' => $this->l('Email address'),
                            'name' => 'email',
                            'col' => '4',
                            'required' => true,
                            'autocomplete' => false
                        ),
                        array(
                            'type' => 'password',
                            'label' => $this->l('Password'),
                            'name' => 'passwd',
                            'required' => ($obj->id ? false : true),
                            'col' => '4',
                            'hint' => ($obj->id ? $this->l('Leave this field blank if there\'s no change.') :
                                sprintf($this->l('Password should be at least %s characters long. or void for automatic generation'), Validate::PASSWORD_LENGTH))
                        ), 
                        array(
                            'type' => 'switch',
                            'label' => $this->l('Send Welcome Email'),
                            'name' => 'sendWelcomeEmail',
                            'required' => false,
                            'class' => 't',
                            'is_bool' => true,
                            'values' => array(
                                array(
                                    'id' => 'sendWelcomeEmail_on',
                                    'value' => 1,
                                    'label' => $this->l('Enabled')
                                ),
                                array(
                                    'id' => 'sendWelcomeEmail_off',
                                    'value' => 0,
                                    'label' => $this->l('Disabled')
                                )
                            ),
                            'hint' => $this->l('Send the credentials to the client')
                        ),
                        array(
                            'type' => 'birthday',
                            'label' => $this->l('Birthday'),
                            'name' => 'birthday',
                            'options' => array(
                                'days' => $days,
                                'months' => $months,
                                'years' => $years
                            )
                        ),
                        array(
                            'type' => 'switch',
                            'label' => $this->l('Enabled'),
                            'name' => 'active',
                            'required' => false,
                            'class' => 't',
                            'is_bool' => true,
                            'values' => array(
                                array(
                                    'id' => 'active_on',
                                    'value' => 1,
                                    'label' => $this->l('Enabled')
                                ),
                                array(
                                    'id' => 'active_off',
                                    'value' => 0,
                                    'label' => $this->l('Disabled')
                                )
                            ),
                            'hint' => $this->l('Enable or disable customer login.')
                        ),
                        array(
                            'type' => 'switch',
                            'label' => $this->l('Newsletter'),
                            'name' => 'newsletter',
                            'required' => false,
                            'class' => 't',
                            'is_bool' => true,
                            'values' => array(
                                array(
                                    'id' => 'newsletter_on',
                                    'value' => 1,
                                    'label' => $this->l('Enabled')
                                ),
                                array(
                                    'id' => 'newsletter_off',
                                    'value' => 0,
                                    'label' => $this->l('Disabled')
                                )
                            ),
                            'disabled' =>  (bool)!Configuration::get('PS_CUSTOMER_NWSL'),
                            'hint' => $this->l('This customer will receive your newsletter via email.')
                        ),
                        array(
                            'type' => 'switch',
                            'label' => $this->l('Opt-in'),
                            'name' => 'optin',
                            'required' => false,
                            'class' => 't',
                            'is_bool' => true,
                            'values' => array(
                                array(
                                    'id' => 'optin_on',
                                    'value' => 1,
                                    'label' => $this->l('Enabled')
                                ),
                                array(
                                    'id' => 'optin_off',
                                    'value' => 0,
                                    'label' => $this->l('Disabled')
                                )
                            ),
                            'disabled' =>  (bool)!Configuration::get('PS_CUSTOMER_OPTIN'),
                            'hint' => $this->l('This customer will receive your ads via email.')
                        ),
                    )
                );
                
                // if we add a customer via fancybox (ajax), it's a customer and he doesn't need to be added to the visitor and guest groups
                if (Tools::isSubmit('addcustomer') && Tools::isSubmit('submitFormAjax'))
                {
                    $visitor_group = Configuration::get('PS_UNIDENTIFIED_GROUP');
                    $guest_group = Configuration::get('PS_GUEST_GROUP');
                    foreach ($groups as $key => $g)
                        if (in_array($g['id_group'], array($visitor_group, $guest_group)))
                            unset($groups[$key]);
                }
    
                $this->fields_form['input'] = array_merge(
                    $this->fields_form['input'],
                    array(
                        array(
                            'type' => 'group',
                            'label' => $this->l('Group access'),
                            'name' => 'groupBox',
                            'values' => $groups,
                            'required' => true,
                            'col' => '6',
                            'hint' => $this->l('Select all the groups that you would like to apply to this customer.')
                        ),
                        array(
                            'type' => 'select',
                            'label' => $this->l('Default customer group'),
                            'name' => 'id_default_group',
                            'options' => array(
                                'query' => $groups,
                                'id' => 'id_group',
                                'name' => 'name'
                            ),
                            'col' => '4',
                            'hint' => array(
                                $this->l('This group will be the user\'s default group.'),
                                $this->l('Only the discount for the selected group will be applied to this customer.')
                            )
                        )
                    )
                );
    
                // if customer is a guest customer, password hasn't to be there
                if ($obj->id && ($obj->is_guest && $obj->id_default_group == Configuration::get('PS_GUEST_GROUP')))
                {
                    foreach ($this->fields_form['input'] as $k => $field)
                        if ($field['type'] == 'password')
                            array_splice($this->fields_form['input'], $k, 1);
                }
    
                if (Configuration::get('PS_B2B_ENABLE'))
                {
                    $risks = Risk::getRisks();
    
                    $list_risks = array();
                    foreach ($risks as $key => $risk)
                    {
                        $list_risks[$key]['id_risk'] = (int)$risk->id;
                        $list_risks[$key]['name'] = $risk->name;
                    }
    
                    $this->fields_form['input'][] = array(
                        'type' => 'text',
                        'label' => $this->l('Company'),
                        'name' => 'company'
                    );
                    $this->fields_form['input'][] = array(
                        'type' => 'text',
                        'label' => $this->l('SIRET'),
                        'name' => 'siret'
                    );
                    $this->fields_form['input'][] = array(
                        'type' => 'text',
                        'label' => $this->l('APE'),
                        'name' => 'ape'
                    );
                    $this->fields_form['input'][] = array(
                        'type' => 'text',
                        'label' => $this->l('Website'),
                        'name' => 'website'
                    );
                    $this->fields_form['input'][] = array(
                        'type' => 'text',
                        'label' => $this->l('Allowed outstanding amount'),
                        'name' => 'outstanding_allow_amount',
                        'hint' => $this->l('Valid characters:').' 0-9',
                        'suffix' => $this->context->currency->sign
                    );
                    $this->fields_form['input'][] = array(
                        'type' => 'text',
                        'label' => $this->l('Maximum number of payment days'),
                        'name' => 'max_payment_days',
                        'hint' => $this->l('Valid characters:').' 0-9'
                    );
                    $this->fields_form['input'][] = array(
                        'type' => 'select',
                        'label' => $this->l('Risk rating'),
                        'name' => 'id_risk',
                        'required' => false,
                        'class' => 't',
                        'options' => array(
                            'query' => $list_risks,
                            'id' => 'id_risk',
                            'name' => 'name'
                        ),
                    );
                }
    
                $this->fields_form['submit'] = array(
                    'title' => $this->l('Save'),
                );
    
                $birthday = explode('-', $this->getFieldValue($obj, 'birthday'));
    
                $this->fields_value = array(
                    'years' => $this->getFieldValue($obj, 'birthday') ? $birthday[0] : 0,
                    'months' => $this->getFieldValue($obj, 'birthday') ? $birthday[1] : 0,
                    'days' => $this->getFieldValue($obj, 'birthday') ? $birthday[2] : 0,
                );
    
                // Added values of object Group
                if (!Validate::isUnsignedId($obj->id))
                    $customer_groups = array();
                else
                    $customer_groups = $obj->getGroups();
                $customer_groups_ids = array();
                if (is_array($customer_groups))
                    foreach ($customer_groups as $customer_group)
                        $customer_groups_ids[] = $customer_group;
    
                // if empty $carrier_groups_ids : object creation : we set the default groups
                if (empty($customer_groups_ids))
                {
                    $preselected = array(Configuration::get('PS_UNIDENTIFIED_GROUP'), Configuration::get('PS_GUEST_GROUP'), Configuration::get('PS_CUSTOMER_GROUP'));
                    $customer_groups_ids = array_merge($customer_groups_ids, $preselected);
                }
    
                foreach ($groups as $group)
                    $this->fields_value['groupBox_'.$group['id_group']] =
                        Tools::getValue('groupBox_'.$group['id_group'], in_array($group['id_group'], $customer_groups_ids));
    
                return AdminController::renderForm();
            }
    
    
    
            /**
             * sendConfirmationMail
             * @param Customer $customer
             * @return bool
             */
            protected function sendConfirmationMail(Customer $customer)
            {
                if (!Configuration::get('PS_CUSTOMER_CREATION_EMAIL'))
                    return true;
    
                return Mail::Send(
                    $this->context->language->id,
                    'account',
                    Mail::l('Welcome!'),
                    array(
                        '{firstname}' => $customer->firstname,
                        '{lastname}' => $customer->lastname,
                        '{email}' => $customer->email,
                        '{passwd}' => Tools::getValue('passwd')),
                    $customer->email,
                    $customer->firstname.' '.$customer->lastname
                );
            }
    
    
    
    }
    

    Attention à ne pas oublier

    Pour toute nouvelle surcharge, il vous faut supprimer le fichier de cache suivant

    cache/class_index.php
    

    Résultat

    Et voilà le résultat au niveau du formulaire

    Capture_d_e_cran_2015-05-16_a__14.27.06.png

    Bonne journée,

    Ch.

    Restez informé

    inscrivez vous à la newsletter pour recevoir les nouveaux billets par mail. ( formulaire en haut à droite )

  • Sunday 28 June 2015 - 11:33

    J'ai eu quelques soucis récemment avec une instance Amazon qui héberge une base de données Mysql très sollicitée par période.
    En effet les données de la base étaient sur la même partition que le système ( déjà c'est mauvais ), celle-ci étant fortement sollicitée en IO, la charge système se met à grimper, les temps d'attente d’accès au disque étant de plus en plus grand, jusqu'au crash complet .

    La solution simple et rapide est de déplacer les données vers une partition dédiée, avec des performances en IO plus importantes (tant qu'à faire). C'est dans ce contexte que je vais présenter, dans ce billet, la mise en place d'une partition en Raid0 pour augmenter significativement les IO/s et surtout éviter de surcharger le système.

    Ce que l'on va voir

    • Création de système de fichiers XFS
    • Création d'un Raid logiciel
    • Monitoring avec atop
    • Ajout d'un disque au Raid
    • Extension d'une partition XFS

    Etat des lieux

    Je vais utiliser une instance micro ( gratuite ) sous Debian Jessie en HVM ( virtualisation totale ). je vais y attacher 4 disques.

    Capture_d_e_cran_2015-05-17_a__10.28.54.png

    • /dev/xvda pour le système
    • /dev/sdb pour tester les perfs. sur un seul disque
    • /dev/sdc premier disque du Raid
    • /dev/sdb deuxième disque du Raid

    Installation des outils

    Le système étant vierge nous allons installer les paquets nécessaires pour les manipulations ci-dessous.

    sudo apt-get install xfsprogs atop mdadm
    

    Premier test : Disque seul

    Pour pouvoir comparer, nous allons commencer avec un test sur un disque "seul" possédant une partition XFS

    Voici comment sont perçus les disques à ce moment

    Capture_d_e_cran_2015-05-17_a__10.35.40.png

    Commençons par créer la partition :

    sudo cfdisk /dev/xvdb
    

    Une fois la partition créée, voici comment sont perçus les disques

    Capture_d_e_cran_2015-05-17_a__10.37.39.png

    Nous allons maintenant créer le système de fichiers sur cette nouvelle partition

    mkfs.xfs /dev/xvdb1
    

    Enfin il ne nous reste plus qu'à monter cette partition sur un répertoire que nous allons également créer maintenant

    mkdir /home/admin/diskLocal
    mount -t xfs /dev/xvdb1 /home/admin/diskLocal/
    

    Dirigeons nous dans ce répertoire et effectuons le premier test basique, grâce à l'outil dd

    cd /home/admin/diskLocal
    dd if=/dev/zero of=bench.dat bs=1M count=1024 oflag=direct
    

    Pendant le test on voit bien grâce à la commande atop, l'utilisation du disque qui est à son maximum

    Capture_d_e_cran_2015-05-17_a__10.45.21.png

    Et voici le résultat

    Capture_d_e_cran_2015-05-17_a__10.45.39.png

    Deuxième test : 2 Disques en Raid0

    Commençons par construire notre raid0 "software" ( via la commande mdadm )

    mdadm --create --verbose /dev/md0 --level=stripe --raid-devices=2 /dev/xvdc /dev/xvdd
    

    Ce qui nous donne :

    Capture_d_e_cran_2015-05-17_a__10.48.43.png

    Un petit check :

    mdadm -D /dev/md0
    

    Maintenant, créons notre système de fichiers sur md0

    mkfs.xfs /dev/md0
    

    Enfin il ne nous reste plus qu'à monter cette partition sur un répertoire que nous allons créer: diskRaid

    mkdir /home/admin/diskRaid
    mount -t xfs /dev/md0 /home/admin/diskRaid/
    

    Dirigeons nous dans ce répertoire et effectuons le second test sur ce disque Raid0

    cd /home/admin/diskRaid
    dd if=/dev/zero of=bench.dat bs=1M count=1024 oflag=direct
    

    Regardons ce qui se passe pendant le test

    Capture_d_e_cran_2015-05-17_a__10.52.27.png

    Les deux disques sont en pleine charge, et quelques secondes plus tard ...

    Capture_d_e_cran_2015-05-17_a__10.52.37.png

    On passe donc de 30 secondes à 18 sec. pour effectuer ce test et un débit d’écriture de 35.5MB/s à 51MB/s

    Conclusion

    Il est plus intéressant de mettre en place 2 disques en Raid0 qu'avoir un gros disque pour améliorer les IO/s.

    Ajoutons un disque au Raid0

    Si nous ajoutons un disque à notre Raid0, nous passerons ainsi à 3 disques

    Voici le disque à ajouter

    • /dev/xvde

    Capture_d_e_cran_2015-05-17_a__14.21.34.png

    Ajoutons le à notre raid md0

    sudo mdadm --grow /dev/md0 --raid-devices=3 --level=0 --add /dev/xvde
    sudo xfs_growfs /dev/md0
    

    Regardons si tout est ok

    lsblk
    

    Capture_d_e_cran_2015-05-17_a__14.59.08.png

    Vérifions que la partition fasse bien 15G

    df -h
    

    Capture_d_e_cran_2015-05-17_a__15.00.30.png

    Bien ça me semble parfait, lançons le troisième test

    cd /home/admin/diskRaid
    dd if=/dev/zero of=bench.dat bs=1M count=1024 oflag=direct
    

    Capture_d_e_cran_2015-05-17_a__15.02.34.png

    Résultat :

    Capture_d_e_cran_2015-05-17_a__15.03.51.png

    Les performances restent les mêmes, certainement limitées par la virtualisation ou simplement physiquement, cependant la charge étant répartie sur 4 disques, l'occupation ne dépasse pas les 65%, ce qui permet d'avoir d'autres processus en parallèles.

    L'ajout de disques reste donc très intéressante pour pouvoir encaisser une forte charge d'IO/s .

    Bon tests ,

    Ch.

    Restez informé

    inscrivez vous à la newsletter pour recevoir les nouveaux billets par mail. ( formulaire en haut à droite )

  • Sunday 28 June 2015 - 11:32

    Ce billet va présenter deux outils Amazon autour d'un exemple concret, la gestion des Bounces en utilisant Amazon en relais pour vos mails.

    Bounces

    Lorsque vous envoyez une "newsletter" à de nombreuses adresses mails, il y a ce que l'on appel les "BOUNCES" , qui traduit donne "rebonds". Il s'agit en fait du retour des mails qui partent vers une adresse qui ne fonctionne pas. Il y a plusieurs raisons qui peuvent engendrer un tel rebond, certaines raisons sont temporaires, d'autres définitives. Par exemple :

    • Le destinataire n’existe pas ( définitif )
    • Boite pleine ( temporaire )
    • Erreur Serveur ( temporaire )
    • Plainte ( définitif )
    • Pas de réponse ( temporaire )
    • Pas de résolution DNS ( définitif )

    Pourquoi traiter ces rebonds ?

    Tout est une histoire de réputation, en effet ne pas traiter ces rebonds et les laisser s'augmenter à chaque newsletter, peut tout doucement vous faire passer pour un "spammeur", d'autant que nous utilisons le relais mails Amazon ( service SES ) qui va réduire votre capacité à envoyer des mails si le nombre de bounces augmente sans action de votre part.

    Deuxième raison, financière, pourquoi continuer à envoyer et payer des mails à des adresses qui n’existent pas ! ( ok le cout du mail est ridicule avec Amazon, mais c'est une question de principe )

    Comment les gérer ?

    Souvent, vous définissez une adresse de retour via ue entête spécifique, dédiée au retour "système" ( Entête : return-path ), ainsi les bounces atterrissement dans cette boite mail dédiée et c'est à vous de les traiter. ( les outils des mailing intègrent souvent une fonction de récupération des bounces par une lecture de boite mail en pop ).

    Cependant, Amazon permet d'aller un peu plus loin grâce à son service de notification SNS.
    Le principe est simple, lorsque Amazon reçoit un "Bounce" il va transmettre un message de notification dans le topic associé . A ce "topic" nous allons lui associer une "inscription" ( qui est en fait une action à lancer lorsque qu'un "message" arrive sur ce topic ). Dans notre cas, ce sera une simple requête HTTP avec dans son corps les informations du Bounce en format JSON.

    Création du Topic

    Depuis l'interface Amazon SNS, vous créez un nouveau "topic"

    Capture_d_e_cran_2015-05-24_a__11.04.03.png

    On va le nommer Bounces

    Capture_d_e_cran_2015-05-24_a__11.04.33.png

    Puis nous allons créer une "subscription"

    Capture_d_e_cran_2015-05-24_a__11.04.57.png

    On va donc choisir le protocole HTTP et dans le champ "endpoint", nous mettons l'url qui pointe sur notre script décrit plus bas ( exemple : http://lindev.fr/sns.php )

    Capture_d_e_cran_2015-05-24_a__11.05.20.png

    Pour le moment, voici notre script sns.php :

    <?php
    ob_start();
    var_dump( $_POST );
    $content = ob_get_clean();
    $fp = fopen('sns_validation.txt', a+);
    fwrite($fp,$content);
    fclose($fp);
    

    Le but étant d'enregistrer dans un fichier texte, ce que nous envoi Amazon . NÉCESSAIRE AU DÉBUT POUR LA VALIDATION DE LE SOUSCRIPTION

    Vous pouvez maintenant valider votre formulaire d'ajout de "subscription", vous allez alors voir apparaitre dans le fichier sns_validation.txt une url pour valider définitivement l'enregistrement.
    Il vous suffit alors d'ouvrir cette url pour terminer l'enregistrement.

    SES et SNS, le lien

    Maintenant nous allons faire en sorte que tout les bounces liés à un domaine géré par le service SES d'amazon, utilisent le système de notification SNS et surtout le "topic" fraichement créé.

    Pour cela, nous allons dans la section SES d'amazon, ( le domaine doit auparavant être validé pour pouvoir relayer des mails, pour cela il suffit d'ajouter des champs TXT dans la zone DNS. La documentation d'amazon est très bien faite à ce sujet ).

    Nous allons donc dans la section Notifications, et l'on édite la configuration

    Capture_d_e_cran_2015-05-24_a__11.06.36.png

    Et là nous allons choisir les options adéquats

    Capture_d_e_cran_2015-05-24_a__11.06.52.png

    On sélectionne donc notre souscription dans les menus "Bounces" et "Complaints" uniquement, car ce sont ces deux cas que nous souhaitons traiter automatiquement.

    Et voilà, après validation, dés qu'un "bounce" montre le bout de son nez, un message est transmit au service SNS et une requête HTTP avec toutes les informations nécessaires dans un format JSON est lancée en POST.

    Tests

    Pour tester le service, Amazon nous met à disposition des adresses spécifiques, qui simulent chaque cas.

    • bounce@simulator.amazonses.com
    • complaint@simulator.amazonses.com

    Ne vous reste donc plus qu'à envoyer un mail à ces deux adresses, et regarder le contenu des données POST qui vont s'ajouter dans votre fichier sns_validation.txt

    Cas simple

    Imaginons, que nous souhaitons, simplement lister les mails des Bounces et Complaints dans un fichier TXT avec trois champs

    1. Date de réception du Bounce
    2. Adresse mail
    3. Type de Bounce ( temporaire ou définitif ou complaint ).

    Nous allons donc extraire ces données du JSON, notre script sns.php devient :

    <?php
    
    // Fetch the raw POST body containing the message
    $postBody = file_get_contents('php://input');
    
    // JSON decode the body to an array of message data
    $msg = json_decode($postBody, true);
    
    if ($msg) {
    
        //Ouverture du fichier de log
        $fp = fopen('sns_bounces.txt', 'a+');
    
        $data = json_decode($msg['Message'],true);
        $typeMsg = $data['notificationType'];//A utiliser pour séparer plaintes et Bounces
    
        switch( $typeMsg ){
            case 'Bounce':
                $bounceType = $data['bounce']['bounceType'];
                foreach( $data['mail']['destination'] AS &$mail ){
                    //Ecriture dans la liste txt
                    fwrite($fp, date('Y-m-d H:i:s').";".$mail.";". $bounceType . "\n");
                }
            break;
            case 'Complaint':
                foreach( $data['mail']['destination'] AS &$mail ){
                    //Ecriture dans la liste txt
                    fwrite($fp, date('Y-m-d H:i:s').";".$mail.";complaint" . "\n");
                }
            break;
        }
        fclose($fp);
    
    }
    

    Vous allez alors vous retrouver avec une liste exploitable comme un CSV .

    L'idée, c'est d'améliorer ce script de démo pour une désinscription automatique dans votre outil de mailing ( très simple avec phplist )

    Restez informé

    inscrivez vous à la newsletter pour recevoir les nouveaux billets par mail. ( formulaire en haut à droite )

  • Wednesday 24 June 2015 - 22:15
    Nokia LUMIAAujourd'hui, j'ai décidé de partager avec vous une offre de remboursement proposée par Microsoft sur le Nokia Lumia 1320, appareil 4G au format "Phablette" 6 pouces. Je précise que je n'ai aucun intérêt chez Microsoft, mais simplement que j'apprécie Windows Phone 8 et que je pense que cette offre de remboursement est intéressante. D'ailleurs je viens de commander cet appareil pour en bénéficier.
    L'offre de remboursement est de 100€ en cas d'achat dans une des boutiques participantes.
    Bien sûr, avec ce type d'offres il faut être hyper rigoureux pour que le dossier soit validé. Mais j'ai déjà bénéficié d'une telle offre sans souci lors de l'achat de mon Lumia 625.
    Cet appareil n'est pas tellement plus puissant que mon Lumia 625. Mais je vois là l'occasion de tester un appareil de très grand format pour un coût modéré car je ne suis pas vraiment certain de me faire un appareil de cette dimension.
    Le formulaire de l'offre est téléchargeable ici: http://www.microsoft.com/fr-fr/mobile/les-offres-promotionnelles-lumia/
    Il y a aussi des offres sur les Lumia 530 et 635 plus puissants, mais qui ne m'intéressent pas en raison de l'absence de flash sur ces appareils. Ce n'est pas que je prenne beaucoup de photo, mais je m'en sert régulièrement comme lampe de poche de secour.
  • Wednesday 24 June 2015 - 22:13
    On lit régulièrement des news, relayées sur tout les sites généralistes et indiquant les parts de marchés des différents systèmes d'exploitations pour mobiles. Les chiffres les plus souvent cités sont issus de deux cabinets d'études.

    Gartner (http://www.gartner.com/newsroom/archive/)
    et
    kantar (http://www.kantarworldpanel.com/fr/smartphone-os-market-share/)

    Pour ma part, j'ai voulu faire un historique des parts de marché. J'ai utilisé les chiffres de Gartner qui remontent jusqu'en 2009 et j'ai essayé de reconstituer des statistiques trimestrielles à partir de leurs communiqués de presse. Ces chiffres concernent le nombre d'appareils vendu chaque trimestre, et pas le nombre d'appareils en cours d'utilisation.

    J'ai trouvé assez amusant de pouvoir revivre les grand événements du marché des smartphones à travers ces chiffres:
    Le déclin de Nokia entraînant Symbian dans son sillage, la naissance et mort de Bada, la constante mais anecdotique présence de Microsoft, la lente agonie de RIM, le pic à la sortie de chaque génération d'iPhone et l'arrivée de l’écrasant rouleau compresseur Google.
  • Tuesday 23 June 2015 - 09:09
    Asciinema est un script Go et service en ligne permettant d'enregistrer et partager vos sessions de terminal / console sous forme de vidéo.
  • Friday 19 June 2015 - 13:34
    Au début de ce blog, j'avais en particulier posté des messages concernant l'alimentation et la consommation électrique des Raspberry Pi. Voici qu'un petit incident sur mon Raspberry Pi de test me rappelle qu'il ne faut pas faire n'importe quoi avec l'électricité.
    Le site officiel annonce que les besoins du Raspberry Pi sont entre 700-1000mA. Il y est conseillé d'utiliser une alimentation de 1200mA issu d'un fabricant reconnu.
    J'ai alimenté pendant 2 semaines non stop mon Raspberry Pi de test avec une alimentation générique sans marque de 500mA. Le système est resté stable pendant la durée mais elle a fini par griller.

    Je n'ai heureusement pas eu d'autres dégâts (de l'extérieur l'alimentation semblait intacte) et le Raspberry Pi fonctionne toujours.
    L'alimentation Bluestork que j'avais choisi en mai 2013 n'a quand à elle toujours pas lâché. L'alimentation Apple fonctionne elle aussi toujours (mais je m'en suis moins servi)
  • Monday 15 June 2015 - 22:55
    Windows InsiderJe teste Windows 10 sur PC depuis quelques temps déjà, mais je n'avait pas d'appareil disponible pour tester la version pour téléphone. Venant de changer mon Lumia 625 pour un Lumia 1320, je n'ai pas pu résister à l'envie de le tester sur le Lumia 625.

    Comment faire pour le tester:

     
     
     
    • Exécuter l'application "Windows Insider". Deux modes de fonctionnement sont proposés sur cette application : Slow = version plus ou moins stable ou Fast = dernière version avec plus de bugs. Pour mon Lumia 625 seule l'option Fast proposait de mises à jour
     
    • Passer ensuite dans le menu de mise à jour "Paramètres/Mise à jour du téléphone/vérifier la disponibilité des mises à jour"
     
    • Installer la mise à jour
     
    • Le téléphone redémarre pendant une éternité en installant la mise à jour.
    Notez bien qu'il s'agit d'une mise à jour et pas d'une réinstallation complète. Les paramètres et programmes devraient normalement être préservés. Malheureusement, dans mon cas, le menu principal était totalement instable. J'ai pu le rendre utilisable en le réinitialisant depuis le menu "Paramètres/Système/A propos de/réinitialiser votre téléphone". Il s'est alors réinstallé avec un système propre tout en restant en Windows 10.
     
    Honnêtement, ce système ne bouleverse pas grand chose par rapport à Windows 8.1. Les changements les plus remarquables sont dans les menus de paramètres qui son bien plus ordonnés et  compréhensible que les anciens.





  • Monday 15 June 2015 - 22:54
    J'en ai déjà parlé sur ce blog, mon passage d'Android à Windows Phone 8.1 m'a permis d'apprécier à sa juste valeur le travail de Microsoft sur Windows 8.1. L'interface en tuile, qui m'avait semblé ridicule au départ, est devenue pertinente après un passage par le tactile.

    C'est pourquoi je suis avec intérêt des évolutions proposées par Microsoft dans le système Windows 10. Je suis en particulier enthousiasmé par l'interface graphique à mi-chemin entre celle de Windows 7 et celle de Windows 8, le nouveau navigateur Edge (anciennement Project Spartan) et les applications universelles (Windows Universal Apps) compatibles sur smartphone comme sur PC.

    La version définitive devrait être gratuite en mise à jour depuis Windows 7 et Windows 8 à partir de  la fin du mois de juillet, mais ce n'est pas parce que c'est gratuit qu'il ne faut pas essayer avant d'acheter!

    Pour télécharger l'ISO du dvd d'installation de la dernière version Technical Preview ca se passe ici:


    Il  a encore des bugs, donc installez la plutôt sur une machine de test ou une machine virtuelle. Je l'ai utilisé sur un vieux PC en core2duo, et ca reste plutôt réactif.