React Native init

React Native init

Petit guide de démarrage ultra rapide React Native et React Navigation. Un premier passage sur https://facebook.github.io/react-native/docs/getting-started afin d’installer les outils necessaire (xcode, nodejs, python, java, android studio). Puis ensuite voici les commandes pour un nouveau projet :

npm install -g react-native-cli
react-native init MyProject
cd MyProject
npm install --save react-navigation
npm install --save react-native-gesture-handler
react-native link

Si vous êtes sous Mac avec xcode vous pouvez lancer l’émulateur iOS avec:

react-native run-ios

Pour Android il faut utiliser :

react-native run-android

Afin de s’y retrouver dans le code j’utilise l’arborescence suivante:

src
- components
- screens

La suite bientôt.

Un plugin Gutenberg pour ajouter une carte Google statique

Un plugin Gutenberg pour ajouter une carte Google statique

Gutenberg est le nouvel éditeur de WordPress, il est dans le même type que celui de Medium. Tout est bloc, texte, image, galerie, titre, liste. J’ai voulu voir comment rajouter un nouveau bloc. Il est basé sur React, c’est donc du JavaScript.

La première partie du plugin est un fichier PHP afin de charger notre fichier JavaScript:

function dayo_gutenberg_map_enqueue_block_editor_assets() {
	wp_enqueue_script(
		'dayo_gutenberg_map',
		plugins_url( 'block.js', __FILE__ ),
		array( 'wp-blocks', 'wp-element' )
	);
}

add_action( 'enqueue_block_editor_assets', 'dayo_gutenberg_map_enqueue_block_editor_assets' );

La partie JavaScript fessant dans les 100 lignes, je vais la décomposer pour bien l’expliquer.

Dans la première partie nous déclarons des variables afin d’alléger la suite du code. La première ligne fait référence a une fonction de React. Comme vous pouvez le voir, nous utilisons wp.element et wp.blocks que nous avions déclaré en dépendance dans le script PHP.

var el = wp.element.createElement,
  registerBlockType = wp.blocks.registerBlockType,
  InspectorControls = wp.blocks.InspectorControls,
  TextControl = wp.blocks.InspectorControls.TextControl,
  SelectControl = wp.blocks.InspectorControls.SelectControl,
  RangeControl = wp.blocks.InspectorControls.RangeControl,
  google_api_key = 'YOUR_GOOGLE_API_KEY_HERE';

const linkOptions = [
  {value: 'roadmap', label: 'roadmap'},
  {value: 'satellite', label: 'satellite'},
  {value: 'hybrid', label: 'hybrid'},
  {value: 'terrain', label: 'terrain'},
];

L’étape suivante est de déclarer notre nouveau bloc. Nous utilisons la fonction registerBlockType que nous avons déclaré au dessus.

Le premier argument est le nom de notre bloc, il faut qu’il soit sous la forme « vendeur/nom-du-bloc ».

Le deuxième argument est un objet. Dans un premier temps nous lui donnons un titre, une icône (peut être une icône Dashicons), une catégorie (common, formatting, layout, widgets, embed) et des attributs (ce sont des informations persisté en base qui servent à l’affichage).

registerBlockType('dayo/gutenberg-map', {
  title: 'Google Map Static',

  icon: 'location',

  category: 'layout',

  attributes: {
    location: {type: 'string', default: 'Brooklyn+Bridge,New+York,NY'},
    mapType: {type: 'string', default: 'roadmap'},
    zoom: {type: 'int', default: 13}
  },

Ce qui donnera:

Toujours dans le deuxième paramètre nous définissons une fonctions save , sa valeur de retour est le code HTML qui sera visible coté utilisateur.

Dans notre cas nous récupérons les trois valeurs que nous avons déclaré dans les attributs, puis nous créons une image avec ces valeurs. Une classe CSS est automatiquement créé pour notre bloque, nous l’ajoutons avec className: props.className .

save: function (props) {
    var location = props.attributes.location,
      mapType = props.attributes.mapType,
      zoom = props.attributes.zoom;

    return el('img', {
      className: props.className,
      src: 'https://maps.googleapis.com/maps/api/staticmap?center=' + location + '&zoom=' + zoom + '&size=600x300&maptype=' + mapType + '&key='+google_api_key
    });
  },

Le gros morceau arrive, la partie de l’éditeur. Elle consiste en une fonction edit , sa valeur de retour est un tableau qui contient ce qui seras affiché, mais aussi d’autre informations tels que la barre d’outils et des informations l’inspecteur (la partie à droite de Gutenberg).

Dans un premier temps nous récupérons les valeurs des attributs ou nous les initialisons et nous récupérons le focus. Ensuite viennent trois fonctions pour mettre à jour nos attribut, il faut bien utiliser la fonction props.setAttributes()  pour que les changements soit visible a l’écran.

edit: function (props) {
    var location = props.attributes.location || 'Brooklyn+Bridge,New+York,NY',
      mapType = props.attributes.mapType || 'roadmap',
      zoom = props.attributes.zoom || 13,
      focus = props.focus;

    function onBlurType(mapType) {
      props.setAttributes({mapType: mapType});
    }

    function onChangeZoom(zoom) {
      props.setAttributes({zoom: zoom});
    }

    function onChangeLocation(loc) {
      props.setAttributes({location: loc});
    }

Viens ensuite la valeur de retour.

Le premier élément est ce qui doit être affiché dans l’éditeur, vous remarquerez que c’est la même chose que le retour de la fonction save , nous voulons afficher la carte.

Le deuxième élément commence par !!focus  afin de n’afficher la suite que si nous avons sélectionné notre bloc. Nous définissons ici la partie inspection avec un titre et trois éléments de contrôle, rien de bien compliqué ici.

return [
      el('img', {
        className: props.className,
        src: 'https://maps.googleapis.com/maps/api/staticmap?center=' + location + '&zoom=' + zoom + '&size=600x300&maptype=' + mapType + '&key='+google_api_key
      }),
      !!focus && el(
        InspectorControls,
        {key: 'inspector'},
        el('h3', null, 'Map Settings'),
        el(
          SelectControl,
          {
            label: 'Map Type',
            select: mapType,
            onBlur: onBlurType,
            options: linkOptions
          }
        ),
        el(
          TextControl,
          {
            label: 'Location Name',
            value: location,
            onChange: onChangeLocation
          }
        ),
        el(
          RangeControl,
          {
            label: 'Zoom Level',
            value: zoom,
            onChange: onChangeZoom,
            min: '1',
            max: '20'
          }
        )
      )
      ,
    ];

Voici a quoi ressemble l’interface d’édition:

Une documentation est accessible sur ce site ainsi que sur le Github du projet.

Ajouter une option dans WordPress puis la récupérer avec l’API REST

Ajouter une option dans WordPress puis la récupérer avec l’API REST

Dans cette article nous verrons comment ajouter une option dans les réglages de WordPress, puis comment la récupérer avec une route REST personnalisé.

La première étape est donc d’ajouter une nouvelle option dans l’onglet Général:

add_action( 'admin_init',
	function () {
		add_settings_field(
			'dayo_map_key',
			'Google Map Static API Key',
			function () {
				echo '<input name="dayo_map_key" id="dayo_map_key" type="text" value="' . get_option( 'dayo_map_key' ) . '" class="code" />';
			},
			'general'
		);

		register_setting( 'general', 'dayo_map_key' );
	} );

Ce qui donnera :

L’étape suivante est d’ajouter une route personnalisé a l’API REST de WordPress:

add_action( 'rest_api_init',
	function () {
		register_rest_route( 'dayo/v1',
			'/map/key',
			array(
				'methods'  => 'GET',
				'callback' => function () {
					get_option( 'dayo_map_key' );
				},
			) );
	} );

La route sera donc « dayo/v1/map/key » et renverra la valeur de l’option « dayo_map_key ».

La dernière partie de code PHP est celle qui fournira au JavaScript les informations pour la requête :

add_action( 'enqueue_block_editor_assets',
	function () {
		wp_localize_script( 'dayo_gutenberg_map',
			'DAYO',
			array(
				'api' => array(
					'url'   => esc_url_raw( rest_url( 'dayo/v1/map/key' ) ),
					'nonce' => wp_create_nonce( 'wp_rest' ),
				),
			) );
	} );

Dans le code JavaScript, nous utilisons la variable DAYO que nous avons fournit en PHP afin d’effectuer une requête

jQuery(function ($) {

  $.ajax({
    method: 'GET',
    url: DAYO.api.url,
    beforeSend: function (xhr) {
      xhr.setRequestHeader('X-WP-Nonce', DAYO.api.nonce);
    }
  }).then(function (r) {
    console.log(r)
  })

});

Et voila comment ajouter une option dans les réglages de WordPress et la récupérer avec l’API REST.

Docker et PHP

Docker et PHP

Après un petit bout de temps à maintenir et mettre a jours php, apache et mysql sur mon par diverse moyen j’ai décidé de passer a docker. Il m’a fallut plusieurs essaie et tâtonnement pour arriver à une configuration qui me convient pour un environnement de développement.

C’est disponible sur ce dépôt github. Il y a deux conteneurs.

Le premier et le conteneur mysql de base avec un seul utilisateur root et comme mot de passe root.

Le second est un conteneur php avec apache plus quelques extensions ainsi que composer accessible par la commande composer :

  • apcu
  • xdebuh
  • intl
  • opcache
  • gd
  • mysqli
  • pdo
  • zip

Apache est configuré avec ssl et rewrite.

Pour utiliser xdebug, mettez à jour le fichier php.ini avec votre IP pour xdebug.remote_host=192.168.1.10 puis dans l’url ajoutez « ?XDEBUG_SESSION_START= »

 

API Platform + FOSUserBundle + LexikJWTAuthenticationBundle

API Platform + FOSUserBundle + LexikJWTAuthenticationBundle

  • Symfony: 3.2.2
  • API Platform: 2.0.3
  • FOSUserBundle: 2.0.0-beta2
  • LexikJWTAuthenticationBundle: 2.1.1

En premier lieu nous allons cloner le dépôt API Plaftorm:

composer create-project api-platform/api-platform bookshop-api

Une fois fait, nous installons FOSUserBundle et LexikJWTAuthenticationBundle via composer:

composer require friendsofsymfony/user-bundle:2.0.0-beta-2
composer require lexik/jwt-authentication-bundle

Puis nous activons les bundles fraichement installé dans AppKernel.php:

public function registerBundles()
{
    return array(
        // ...
        new FOS\UserBundle\FOSUserBundle(),
        new Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle(),
    );
}

Nous créons notre entité User.php:

<?php

namespace AppBundle\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * @ORM\Entity
 * @ApiResource(attributes={
 *     "normalization_context"={"groups"={"user", "user-read"}},
 *     "denormalization_context"={"groups"={"user", "user-write"}}
 * })
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @Groups({"user"})
     */
    protected $email;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     * @Groups({"user"})
     */
    protected $fullname;

    /**
     * @Groups({"user-write"})
     */
    protected $plainPassword;

    /**
     * @Groups({"user"})
     */
    protected $username;

    public function setFullname($fullname)
    {
        $this->fullname = $fullname;

        return $this;
    }
    public function getFullname()
    {
        return $this->fullname;
    }

    public function isUser(UserInterface $user = null)
    {
        return $user instanceof self && $user->id === $this->id;
    }
}

Nous poursuivons par la génération des clés, notez bien la pass phrase qu’il faudra copié dans le fichier parameters.yml

mkdir -p var/jwt # For Symfony3+, no need of the -p option
openssl genrsa -out var/jwt/private.pem -aes256 4096
openssl rsa -pubout -in var/jwt/private.pem -out var/jwt/public.pem

Nous mettons a jours les fichier suivants:

config.yml:

framework:
    translator:      { fallbacks: ["%locale%"] }

fos_user:
    db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'
    firewall_name: main
    user_class: AppBundle\Entity\User

lexik_jwt_authentication:
    private_key_path: '%jwt_private_key_path%'
    public_key_path:  '%jwt_public_key_path%'
    pass_phrase:      '%jwt_key_pass_phrase%'
    token_ttl:        '%jwt_token_ttl%'

routing.yml

api:
    resource: '.'
    type:     'api_platform'

app:
    resource: '@AppBundle/Action/'
    type:     'annotation'

api_login_check:
    path: /api/login_check

fos_user_security:
    resource: "@FOSUserBundle/Resources/config/routing/security.xml"

fos_user_profile:
    resource: "@FOSUserBundle/Resources/config/routing/profile.xml"
    prefix: /profile

fos_user_register:
    resource: "@FOSUserBundle/Resources/config/routing/registration.xml"
    prefix: /register

fos_user_resetting:
    resource: "@FOSUserBundle/Resources/config/routing/resetting.xml"
    prefix: /resetting

fos_user_change_password:
    resource: "@FOSUserBundle/Resources/config/routing/change_password.xml"
    prefix: /profile

security.yml

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt

    role_hierarchy:
        ROLE_READER: ROLE_USER
        ROLE_ADMIN: ROLE_READER

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email

    firewalls:
        login:
            pattern:  ^/login
            stateless: true
            anonymous: true
            provider: fos_userbundle
            form_login:
                check_path: /login_check
                username_parameter: _username
                password_parameter: _password
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure
                require_previous_session: false

        main:
            pattern: ^/
            provider: fos_userbundle
            stateless: true
            anonymous: true
            lexik_jwt: ~

        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

    access_control:
        - { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/books, roles: [ ROLE_READER ] }
        - { path: ^/, roles: [ ROLE_READER ] }

parameters.yml.dist et parameters.yml, n’oubliez pas la pass phrase pour les clés

parameters:
    # ...
    jwt_private_key_path: '%kernel.root_dir%/../var/jwt/private.pem' # ssh private key path
    jwt_public_key_path:  '%kernel.root_dir%/../var/jwt/public.pem'  # ssh public key path
    jwt_key_pass_phrase:  ''                                         # ssh key pass phrase
    jwt_token_ttl:        3600

Puis nous mettons à jour la base de données et créons notre premier utilisateur:

php bin/console doctrine:schema:update --force
php bin/console fos:user:create testuser test@example.com p@ssword
php bin/console fos:user:promote testuser ROLE_READER

Pour tester ca un petit coup de Curl:

curl -X POST http://127.0.0.1:8000/login_check -d _username=test@example.com -d _password=p@ssword

Et comme réponse nous avons:

{"token":"eyJhbGciOiJSUzI1NiJ9.eyJyb2xlcyI6WyJST0xFX1JFQURFUiIsIlJPTEVfVVNFUiJdLCJ1c2VybmFtZSI6InRlc3R1c2VyIiwiZXhwIjoxNDg2NDk5MjczLCJpYXQiOjE0ODY0OTU2NzN9.RL8VS8MuKhvg0uW23TPnlxsZusUIE86YuE_cg44F4rToe8c7zV6s-2lmuOhP5CyeB50IVfLlb4-RC1frKQFTIrEA1LJ2FewxKFO2Mb2sIQwQonAew3MTwnxbnPRWrS0sQCsb1NbXTxohc5WH-BU1U3IkuAjuTVLcB3g7JlPor0yedUU766BRXjIhklN7IuILKsjhteqGK6F9Ilg0zmLyjQV3-BUg1nnWygQ-O4BLrU_InvhpHZVOnPfikMftcKVAhn_Z4GKGVVORQxplhc4i0lJgzdV83AaImYqOzn3WBJxKnzmitb6rnOJ-SKXjdAYRix_rFwqIzzWNdcAg1-C731b08I3qcfWXmmadI2GsygVpemlagH8v6tcFjwVsyazblqFCA8oy37N0CfSVt7av8GIbenWtIDUS5Tl3a0SMxmcwg6rlTL_1pE0-E8mPa8ZbLho-nON2hKoQcG1LhIeuatzIi7lUSLCQv_uzy29Hpq7PPJ_43cZZDdex-vk3BVHbjZCOcggVxp-mDDTcU7VxZFwmi8AiP9ZAg8AFXviKcfM7D5Er2tB2k3HHsnCqTSGRJe9czNs-GaW55748BK_u0UiEBvBdDHUgmbGw4UTRw8aNTY8p8M_tavezC36p5zilyd2CB6knsLtm0aRkqyZaiK27uvajYMt65BpxqsmXzLw"}

Et voila !!!

Disponible sur github.