Blog Liip https://www.liip.ch/fr/blog Kirby Thu, 18 Apr 2019 00:00:00 +0200 Les derniers articles du blog Liip fr Writing a wrapper block for Gutenberg in WordPress https://www.liip.ch/fr/blog/writing-a-wrapper-block-for-gutenberg-in-wordpress https://www.liip.ch/fr/blog/writing-a-wrapper-block-for-gutenberg-in-wordpress Thu, 18 Apr 2019 00:00:00 +0200 Since Gutenberg blocks are built as independent components they can be used in every layout and can be combinend however it fits your design.

This is a very common practice nowadays. But sometimes you would like to group a bunch of blocks and change the appearance or behaviour of them at once.

In this tutorial we're going to write a wrapper block which adds a background color around its child blocks.

This is how it will look like when it's finished:

Register new block type

To begin with we need to register a new block type. If you haven't done this before you can use the create-guten-block npm script which helps you bootstrap a new block and sets up an asset build pipeline with Webpack.

const { __ } = wp.i18n; // Import __() from wp.i18n
const { registerBlockType } = wp.blocks; // Import registerBlockType() from wp.blocks

registerBlockType( 'wrapper-block-example/wrapper-block', {
    // Block name. Block names must be string that contains a namespace prefix. Example: my-plugin/my-custom-block.
    title: __( 'Background', 'wrapper-block-example' ), // Block title.
    icon: 'editor-table', // Block icon from Dashicons → https://developer.wordpress.org/resource/dashicons/.
    category: 'layout', // Block category — Group blocks together based on common traits E.g. common, formatting, layout widgets, embed.
    keywords: [
        __( 'Background', 'wrapper-block-example' ),
        __( 'Wrapper Block', 'wrapper-block-example' ),
    ],

    attributes: {
        // Register bgColor attribute to save the chosen color
        bgColor: {
            type: 'string',
        },
    },

    // Will be implemented afterwards
    edit() {
        return null;
    },

    // Will be implemented afterwards
    save() {
        return null;
    },
} );

We register our new block wrapper-block-example/wrapper-block with the registerBlockType function and pass in the block configuration (eg. title, description, etc.).

Additionally we register a block attribute called bgColor. In this attribute we will save the chosen background color when our block is used.

With this our block can already be inserted in the editor:

Edit function of our block

In the edit function of our block we create a SelectControl where the background color can be chosen. This control saves its value to the bgColor attribute when it's changed.

First we need to load the dependencies (at the top of the file):

import classNames from 'classnames'; // Used to to join classes together

const {
    Fragment, // Used to wrap our edit component and only have one root element
} = wp.element;
const {
    InnerBlocks, // Allows it to place child blocks inside our block
    InspectorControls, // We place our select control inside the inspector contorls which show up on the right of the editor
} = wp.editor;
const {
    PanelBody, // A panel where we place our select control in (creates a colapsable element)
    SelectControl, // Our select control to choose the background color
} = wp.components;

Now we can implement the edit function:

edit( { attributes, setAttributes, className } ) {
    const {
        bgColor = '',
    } = attributes;

    return (
        <Fragment>
            <InspectorControls>
                <PanelBody
                    title={ __( 'Background Color', 'wrapper-block-example' ) }
                    initialOpen={ true }
                >
                    <SelectControl
                        label={ __( 'Background Color', 'wrapper-block-example' ) }
                        value={ bgColor }
                        options={ [
                            {
                                value: '',
                                label: __( 'No Background Color', 'wrapper-block-example' ),
                            },
                            {
                                value: 'paleturquoise',
                                label: __( 'Light Blue', 'wrapper-block-example' ),
                            },
                            {
                                value: 'orange',
                                label: __( 'Orange', 'wrapper-block-example' ),
                            },
                        ] }
                        onChange={ ( selectedOption ) => setAttributes( { bgColor: selectedOption } ) }
                    />
                </PanelBody>
            </InspectorControls>
            <div
                className={ className }
                style={ { backgroundColor: bgColor } }
            >
                <InnerBlocks />
            </div>
        </Fragment>
    );
},

We add the background selection inside the inspector controls of the block. With this implementation we're now able to select a background color of our block:

The most important part (next to the SelectControl of course) is the <InnerBlocks /> component which we add at the end of the edit function. This component allows us to place child blocks inside our block. Read everything about InnerBlocks here: https://github.com/WordPress/gutenberg/tree/master/packages/block-editor/src/components/inner-blocks

Save function

In the save function of our block we need to add the <InnerBlocks.Content /> component which passes through the content of its child blocks.

Additionally we add a background-color style property with the chosen color to its wrapper div and set a bg-<chosen-color> class to be able to style the child blocks via CSS (eg. change text color when a dark background is selected). We only render these attributes when a background color is selected.

save( { attributes, className } ) {
    const {
        bgColor = '',
    } = attributes;

    let styles = {};
    let classes = className;

    // Only set attributes when background color is chosen
    if ( '' !== bgColor ) {
        styles = { backgroundColor: bgColor, padding: '10px' };
        // Use classnames library to join all classes together
        classes = classNames( `bg-${ bgColor }`, classes );
    }

    return (
        <div
            className={ classes }
            style={ styles }
        >
            <InnerBlocks.Content />
        </div>
    );
},

Wrap up

As you see writing a wrapper block in Gutenberg is quite simple. All you need to do is using the <InnerBlocks> feature of Gutenberg.

You'll find the complete code of this example packed as a fully functional WordPress plugin here: https://github.com/liip/wrapper-block-example-wp-plugin

]]>
Cognitive UXD: Motivation https://www.liip.ch/fr/blog/cognitive-uxd-motivation https://www.liip.ch/fr/blog/cognitive-uxd-motivation Thu, 11 Apr 2019 00:00:00 +0200 Based on my last blog post Cognitive User Experience Design - Where Psychology meets UX Design I will focus now on Motivation and describe which role the Hierarchy of Needs plays in design. This raises the following question.

What is the Hierarchy of Needs about?

The most prominent contribution in psychology is the Hierarchy of Needs of Abraham Maslow. The psychologist describes that the human motivation consists of five distinct groups of needs, namely Physiological, Safety, Love/ Belonging, Esteem and Self-actualization. The basic level of needs are at the bottom of the hierarchy and must be ensured first before focusing on higher level needs.

Source: “Hierarchy of Needs” proposed by Abraham Maslow in his paper “A Theory of Human Motivation” (1943).

As soon as the Physiological needs (with breathing, food, water, sex, homeostasis and excretion) are fulfilled, the next higher need Safety (with security of body, of empowerment, of resources, of morality, of the family, of health and of property) should be focused. The Safety level is followed by Love/ Belonging and characterized by friendship, family and sexual intimacy. Achieving this level of needs is already good and the chasm to the next two needs is often difficult to cross. The level of Esteem needs (with its self-esteem, confidence, achievement, respect of others as well as respect by others) defines the fourth level of the hierarchy. On top of the Hierarchy of Needs is the Self-actualization where morality, creativity, problem solving lack of prejudice and acceptance of facts are in the focus.
Maslow argued that if the needs of a prior level are not met, the hierarchy won’t be stable and leads to feelings of stress and anxiety. For example, a good performance in a team at the job (need 3) is not possible when you are starving and your hunger is overwhelming (need 1). That’s why we seek to stabilize the lower-level needs before trying to focus on higher-level needs.

What are the critical comments about the Hierarchy of Needs?

Despite this intuitive perspective, only little evidence for the hierarchical order is given. For instance, in some cultures the social need is the first and most important need. Another interesting extension point of Maslow’s theory is that selfless acts, bravery, charity or the spiritual beliefs should be taken into account too.
Another approach is Maslow’s own expansion of his Hierarchy of Needs in 1970. Accordingly, the Esteem need is followed by Cognitive need, which consists of knowledge and understanding, curiosity, exploration, need for meaning as well as predictability. The next need is Aesthetic where beauty, balance and form are appreciated. Followed by Self-Actualization, which was already part of the first version of Maslow’s Hierarchy of Needs, the top need is Transcendence. This need is characterized by encouraging others in achieving Self-actualization.

How can we apply this psychological knowledge to UX Design?

Applying this knowledge to UX Design, the hierarchy levels are Functionality, Reliability and Usability, followed by Proficiency and on top Creativity.

Source: “Design Hierarchy of Needs” was created by Steven Bradley (2010).

The bottom of Bradley’s Design Hierarchy of Needs corresponds with the Functionality and consists of working as programmed and meet basic functionalities. Functional designs are perceived to be of little value for design. For example, a website loads within a reasonable time. The Functionality needs are followed by the Reliability which is defined by stable and consistent performance. Reliable designs are perceived to be of low value of design. Accordingly, a website that worked one week before should work today too, even when new sections have been deleted or added. The third level Usability is ensured as soon as the design is easy to use and works like we think. Usability design is perceived to be of moderate value, which means that some basic expectations work. For example, a website is easy understandable and has a navigation that works. Proficiency and Creativity form the top of the UX Design Hierarchy of Needs. Thereby, Proficiency contains memorable experience, empowers users and meets a high level of design. It provides suggestions that refer to favorites or advanced search entries. Proficiency is followed by Creativity, which is characterized by personal significance, innovative interaction and aesthetic beauty. A creative design is perceived to be the highest level because it satisfies users and leads to loyal users.

What are the critical comments about the Design Hierarchy of Needs?

Similar as for Maslow’s pyramid, the Design Hierarchy of Needs makes intuitively sense. Nevertheless, the hierarchic order must not be followed strictly. The user might like an aesthetically beautiful website more that doesn’t work reliable than a boring but reliable one. It should, however, be noted, that when a website that doesn’t load, the website itself fails. Therefore, to use the design hierarchy as a guideline to first fulfill needs of the lower-level before focusing on higher-levels is recommended.
As mentioned before, a UX designer needs to consider the different levels of needs and should strive for proficiency and creativity in order to surprise users with delightful services with a great user experience.

Because the topic Motivation is so comprehensive and has such an intensive impact on design I will take you on a short journey in my next blog post and characterize what kind of impact Flow has on design.

]]>
Cognitive User Experience Design - Where Psychology meets UX Design https://www.liip.ch/fr/blog/cognitive-user-experience-design-where-psychology-meets-ux-design https://www.liip.ch/fr/blog/cognitive-user-experience-design-where-psychology-meets-ux-design Thu, 04 Apr 2019 00:00:00 +0200 Psychology and User Experience Design have a lot in common. The combination of both disciplines is known as Cognitive User Experience Design (short Cognitive UXD). Thereby, the psychology provides valuable concepts that can be applied to design, the way we think, how we design experiences and how we experience designs. The most important concepts are Motivation, Cognition, Perception & Awareness, Emotion and Volition. They all have in common that they are primary human psychological functions.

In the following blog posts I will underline their impact on design.
But before starting, I will describe each concept briefly and their role in design.

Motivation is characterized as the reason why people take actions. It is goal-directed behavior, which requires self-regulatory efforts and differs in duration and intensity. It’s role in design is to meet individual’s needs and to strive for proficiency and creativity in order to surprise users with delightful services with a great experience.

Cognition is the mental effort that is used in the working memory. Cognitive overload leads to more time and effort to complete a task. As a designer we have several strategies to reduce the cognitive overload for users.

Perception & Awareness contain to identify, organize and interpret sensory information. They are heavily affected by Biases and Persuasion, which in turn have an influence on our perception. It’s role in design is also well known as Gestalt Psychology. This knowledge about Gestalt Psychology, Biases and Persuasion combined with the capacities and characteristics of human perception enable us to design better experiences.

Emotion is a mental state that is often linked with mood, temperament, motivation, personality and disposition. Because we take decisions based on our emotional state, the goal is to trigger positive experiences with design. The communication and creation of emotions is a challenge for designers that is worth to give more attention.

Volition, or “willpower”, contains the process from intention to actual behavior. It encompasses a process of forming a purpose (setting a goal and planning) to implementing decisions (organisation and control). Same as for motivation, volition is goal-directed and requires self-regulatory efforts. Nowadays, new approaches indicate that it is a conscious action control which turns into automatized processes.

All those short outlooks in further blog posts illustrate the huge importance Cognitive User Experience Design has. By the end of each blog post you will

  • Understand how Cognitive UXD and each focused topic itself has an impact on User Experience
  • Be able to adopt and analyse Cognitive UXD-driven approach to design
  • Be equipped with knowledge and recommendations of Cognitive UXD to improve the User Experience
  • Have a profound understanding of the Cognitive UXD-driven approach

My next blog post is about Motivation. There, I describe which role the Hierarchy of Needs plays in design.

]]>
La bibliothèque centrale: bien plus qu’une bibliothèque dotée d’une nouvelle plateforme https://www.liip.ch/fr/blog/zentralbibliothek-ist-live https://www.liip.ch/fr/blog/zentralbibliothek-ist-live Wed, 03 Apr 2019 00:00:00 +0200 De la conception à la mise en œuvre

Le projet de relooking de la bibliothèque centrale (ZB) de Zurich nous a énormément plu. Du début jusqu’à la fin, il a été instructif, enrichissant et intéressant. La phase de conception a débuté par un atelier collectif. Il a permis de définir la stratégie du projet et les exigences concernant les fonctionnalités du site web, mais aussi d’esquisser une première ébauche de l’architecture des informations. Les enseignements tirés de cet atelier ont marqué le coup d’envoi de la phase de conseil, avec la mise en place d’un design offrant une grande simplicité d’utilisation. De cette démarche est né un concept de navigation destiné à améliorer l’expérience d’utilisation. Grâce aux différents niveaux d’accès, tous les groupes cibles peuvent atteindre les contenus dont ils ont besoin. La conception des contenus du site a elle aussi été entièrement revue et les textes complètement reformulés.

Contenu et développement: l’interaction parfaite

Le développement d’une stratégie de contenu, et du guide de contenu en découlant, a permis la conception d'un nouveau site web centré sur l'expérience d'utilisation. Les utilisateurs et utilisatrices veulent «vivre» le contenu du site. Des spécialistes du contenu, du design et du développement web ont ainsi uni leurs forces pour créer l’interface idéale. Les collaboratrices et collaborateurs de la ZB ont bénéficié d’une aide à la rédaction et ont été initiés aux nouveaux processus rédactionnels. L’idée centrale était de mettre à disposition des utilisateurs des fonctionnalités de recherche avancées et faciles d’accès. Le portail de recherche, avec sa mise en œuvre technique via le CMS October, constitue la fonctionnalité principale du nouveau site. La démarche agile adoptée a permis de tirer le meilleur du système.

Intégration des bons outils d’analyse

Google Tag Manager et Google Analytics permettent d’analyser de manière approfondie le site de la ZB. Ces outils aident par ailleurs les collaboratrices et collaborateurs du service de communication à définir des mesures pour optimiser leurs activités marketing. La première étape était d'assurer le respect des normes techniques en matière de SEO. Par le biais de programmes de coaching et de formation, les collaboratrices et collaborateurs de la ZB sont devenus des spécialistes de l’utilisation de ces outils d’analyse.

Open over closed

Nous avons collaboré de manière efficace tout au long du projet grâce à une communication ouverte et de nombreux échanges. De nouvelles approches ont permis de repenser et de refondre les structures existantes. Elles ont aussi rendu possible l'implémentation d'une exprérience centrée sur l'utilisateur, tant sur le plan technique que sur le plan du contenu. Notre philosophie de travail est open over closed. Nous avons donc misé exclusivement sur l’open source. En ayant recours à la plateforme CMS October ainsi qu'à l’infrastructure Google Analytics notamment.
La gestion technique du nouveau site sera assurée par le service informatique de la ZB. Ce qui a nécessité dans un premier temps un transfert de savoir-faire. Dans un second temps, les collaboratrices et collaborateurs du service de communication ont eux aussi été coaché·e·s pour apprendre à préserver l’ouverture des contenus du site et à en concevoir de nouveaux.

Liip nous a très bien aidés à penser moins "de l'intérieur", de la logique de notre organisation, et plus du point de vue de nos clients. Cela nous a ouvert une nouvelle perspective et nous avons réussi à communiquer notre contenu d'une manière fraîche et directe.
Dr. Christian Oesterheld, Directeur, ZB Zürich

Grâce à notre collaboration avec l’agence Liip, notre nouveau site web est parfaitement adapté aux besoins de nos utilisateurs et utilisatrices. C’est exactement ce que nous recherchions !
Natascha Branscheidt, responsable de projet, ZB Zürich

Entourés d’une équipe compétente, nous avons relevé un véritable défi et sommes fiers de présenter le travail effectué pour la ZB.
Miriam Pretzlaff, Product Owner, Liip

]]>
Scrum rencontra Holacracy <3 Et elles vécurent heureuses… https://www.liip.ch/fr/blog/scrum-met-holacracy https://www.liip.ch/fr/blog/scrum-met-holacracy Fri, 29 Mar 2019 00:00:00 +0100 Un peu d’histoire

Avant l’adoption d’Holacracy en janvier 2016, nous avions déjà développé notre propre système de gouvernance d’entreprise. Celui-ci était basé sur la méthode de gestion de projets agile Scrum, que nous “respirions” depuis 2010 déjà. En d’autres termes, nous avions répliqué la structure de rôles et l’organisation du travail à la Scrum à toute l’entreprise. Cela concernait la gestion de nos projets clients, mais aussi la gestion de notre administration. Je me rappelle avoir eu le rôle de Scrum Master du bureau de Lausanne. Ma responsabilité principale était de maintenir un “cadre” dans lequel les Liipers lausannois pouvaient évoluer sans obstacle. Je maintenais un Backlog de “User Stories” liées au développement de notre bureau et nous organisions le traitement de ces stories en “sprints”. Je me souviens également que notre équipe d’administration tenait un Scrum Board physique qui contenait en continu toutes les tâches en cours et à faire.

Ce système de gouvernance agile “à la Liip” a bien fonctionné pendant quelques années. Jusqu’à ce qu’il devienne trop contraignant. Il était destiné à la gestion de projets, pas aux processus opérationnels de l’entreprise . Nous devions constamment le “tordre” afin qu’il continue à fonctionner. Passé 100 collaborateurs (dont 6 managers, car nous avions toujours un niveau hiérarchique à ce moment-là), nous avons dû nous rendre à l’évidence : cette version de Scrum pour la gouvernance de Liip n’était plus suffisamment efficiente.

Comment pouvions-nous alors faire évoluer notre organisation d’entreprise de manière à absorber durablement notre croissance (~20% par an en termes de collaborateurs) ?
Deux chemins s’ouvraient à nous :

  1. Augmenter la hiérarchie : soit agrandir l’équipe de direction, soit insérer des couches de gestion intermédiaires.
  2. Effectuer des recherches afin de trouver un système radicalement différent.

Ayant toujours pensé que les systèmes fortement hiérarchisés sont voués à l’échec, nous avons pris le chemin numéro 2. sans trop hésiter.

Et nous avons rencontré Holacracy

Ces recherches nous ont menées, entre autres, au livre “Reinventing Organizations” de Frédéric Laloux, puis à Holacracy. Ce qui nous a tout de suite plu, c’est le fait que Holacracy véhicule un système de règles très explicites, décrites clairement dans une constitution, open source et évolutive. Après plus de dix ans d’existence, Holacracy était aussi au bénéfice de références intéressantes (dont Zappos et ses 1500 employés).

Mais est-ce que Holacracy impacte le travail que je fais avec mes clients?

Quand nous coachons d’autres entreprises désireuses d’adopter Holacracy dans leur organisation, une question revient souvent :

Est-ce que je vais devoir apprendre le fonctionnement d’Holacracy à mes clients?

Je vous rassure tout de suite, la réponse est non (et heureusement ;-) ). Je crois d’ailleurs que la plupart de nos clients ne se sont pas rendus compte du changement. Nous gérons tous nos projets, depuis 2010, avec Scrum. Holacracy est un outil que nous utilisons en interne uniquement, pour opérer notre entreprise.

Comment Scrum et Holacracy s’unissent

L’utilisation de deux méthodes de travail fortement intégrées dans une seule entreprise peut sembler dangereux. Avec du recul, je constate qu’elles se marient très bien. Notre organigramme montre que toutes les équipes de “production” ont créé les rôles de base de Scrum : Scrum Master (SM), Product Owner (PO). La raison d’être et les redevabilités du SM et du PO ont été reprises du Scrum guide. Les équipes ont créé les rôles supplémentaires qui leur étaient nécessaires : Frontend/Backend/Fullstack developer, DevOps, UX Designer, etc. Tous ces rôles continuent à fonctionner avec Scrum sur les projets clients. On les retrouve également dans la structure Holacracy.

Comparaison de l’ensemble des processus itératifs des deux méthodes :

Processus Raison d’être Qui est convié Périmètre
Holacracy
Gouvernance Adresser les tensions d’ordre structurel Membres du cercle (Liipers) Structure du cercle / de l’entreprise
Triage (tactical) Adresser les tensions d’ordre opérationnel Membres du cercle (Liipers) Projets internes au cercle / à l’entreprise
Scrum
Raffinement du Backlog Prendre soin du backlog de produit, afin qu’il soit prêt pour les prochains sprints L’équipe Scrum (Liipers & client) Projet client
Sprint planning Définir ce qui va être fait lors du sprint à venir L’équipe Scrum (Liipers & client) Projet client
Sprint review Démontrer ce qui a été fait lors du sprint qui se termine L’équipe Scrum (Liipers & client) Projet client
Sprint retrospective Inspecter et améliorer le processus / la structure du projet L’équipe Scrum (Liipers & client) Structure / fonctionnement de l’équipe Scrum

Chacun de ces processus a une raison d’être spécifique. Elle est nécessaire au bon déroulement des projets ou à l’amélioration continue de la structure. Nous constatons, qu’il pourrait y avoir un overlap entre les processus Holacracy et la rétrospective Scrum.

Les équipes (ou cercles) chez Liip gèrent cette relation en maintenant les rétrospectives dans leur forme. Tous les sujets (ou “tensions”) qui émergent de la rétrospective n’y sont pas tous traités. Les tensions concernant des problématiques internes sont rapportées lors de la prochaine réunion de triage Holacracy de l’équipe en question. Cette réunion est souvent agendée peu après la rétrospective en question.

Continuez à faire des rétrospectives!

Après trois ans de pratique, je trouve qu’il reste utile de maintenir les rétrospectives “à la Scrum” : à travers une interaction de groupe, elles permettent de faire monter à la surface des sujets à traiter, que l’on n’aurait pas forcément identifié seul.

Ma recommandation est donc la suivante : si vous vous lancez dans Holacracy tout en pratiquant Scrum, continuez à faire des rétrospectives. Et assurez vous d’utiliser les processus solides de Holacracy pour en adresser les sujets à traiter!

Quelle est votre expérience ? Votre avis ? Je me réjouis de lire votre commentaire ci-dessous !

]]>
How to extend existing Gutenberg blocks in WordPress https://www.liip.ch/fr/blog/how-to-extend-existing-gutenberg-blocks-in-wordpress https://www.liip.ch/fr/blog/how-to-extend-existing-gutenberg-blocks-in-wordpress Thu, 28 Mar 2019 00:00:00 +0100 The new WordPress editor (Gutenberg) comes with lots of pre built blocks (Paragraph, List, Image, etc.). Each block has its own markup which wraps the content of it. This markup is generally generic enough that it works with most of the themes. But there are cases where you would like to change the appearance of these blocks in some way.

Let's say we would like to add a spacing control to the existing image block (core/image) where we can choose between a small, medium or large margin which should be added below the image.

1. Add spacing attribute

Before we can add a custom control to the image block we need a place where we can save the chosen value. In Gutenberg this is done with block attributes.

To add a custom attribute to an existing block we can use the blocks.registerBlockType filter like this:

import assign from 'lodash.assign';
const { addFilter } = wp.hooks;
const { __ } = wp.i18n;

// Enable spacing control on the following blocks
const enableSpacingControlOnBlocks = [
    'core/image',
];

// Available spacing control options
const spacingControlOptions = [
    {
        label: __( 'None' ),
        value: '',
    },
    {
        label: __( 'Small' ),
        value: 'small',
    },
    {
        label: __( 'Medium' ),
        value: 'medium',
    },
    {
        label: __( 'Large' ),
        value: 'large',
    },
];

/**
 * Add spacing control attribute to block.
 *
 * @param {object} settings Current block settings.
 * @param {string} name Name of block.
 *
 * @returns {object} Modified block settings.
 */
const addSpacingControlAttribute = ( settings, name ) => {
    // Do nothing if it's another block than our defined ones.
    if ( ! enableSpacingControlOnBlocks.includes( name ) ) {
        return settings;
    }

    // Use Lodash's assign to gracefully handle if attributes are undefined
    settings.attributes = assign( settings.attributes, {
        spacing: {
            type: 'string',
            default: spacingControlOptions[ 0 ].value,
        },
    } );

    return settings;
};

addFilter( 'blocks.registerBlockType', 'extend-block-example/attribute/spacing', addSpacingControlAttribute );

Let me explain this code step by step:

First we need to define some variables which are used at multiple places in our example.

// Enable spacing control on the following blocks
const enableSpacingControlOnBlocks = [
    'core/image',
];

In the enableSpacingControlOnBlocks array we can list all existing blocks where the spacing control should be available. In our example we just add the core/image block.

// Available spacing control options
const spacingControlOptions = [
    {
        label: __( 'None' ),
        value: '',
    },
    {
        label: __( 'Small' ),
        value: 'small',
    },
    {
        label: __( 'Medium' ),
        value: 'medium',
    },
    {
        label: __( 'Large' ),
        value: 'large',
    },
];

The spacingControlOptions array lists all available options which can be chosen in our spacing control element.

const addSpacingControlAttribute = ( settings, name ) => {
    // Do nothing if it's another block than our defined ones.
    if ( ! enableSpacingControlOnBlocks.includes( name ) ) {
        return settings;
    }

    // Use Lodash's assign to gracefully handle if attributes are undefined
    settings.attributes = assign( settings.attributes, {
        spacing: {
            type: 'string',
            default: spacingControlOptions[ 0 ].value,
        },
    } );

    return settings;
};

This is our callback function where the custom attribute gets registered. We first check if the block is one of our defined blocks in the enableSpacingControlOnBlocks array. If that's the case we add our new attribute spacing to the already existing blocks attributes.

addFilter( 'blocks.registerBlockType', 'extend-block-example/attribute/spacing', addSpacingControlAttribute );

Last but not least we need to add this function to the blocks.registerBlockType filter.

The core/image block now has a new attribute spacing which we can use to save our chosen value.

2. Add spacing control

As a next step we need to create the spacing control element to the blocks InspectorControl.

The result should look like this:

Spacing Control in Inspector controls

Gutenberg provides the editor.BlockEdit hook to extend the blocks edit component (which includes the InspectorControl part).

const { createHigherOrderComponent } = wp.compose;
const { Fragment } = wp.element;
const { InspectorControls } = wp.editor;
const { PanelBody, SelectControl } = wp.components;

/**
 * Create HOC to add spacing control to inspector controls of block.
 */
const withSpacingControl = createHigherOrderComponent( ( BlockEdit ) => {
    return ( props ) => {
        // Do nothing if it's another block than our defined ones.
        if ( ! enableSpacingControlOnBlocks.includes( props.name ) ) {
            return (
                <BlockEdit { ...props } />
            );
        }

        const { spacing } = props.attributes;

        return (
            <Fragment>
                <BlockEdit { ...props } />
                <InspectorControls>
                    <PanelBody
                        title={ __( 'My Spacing Control' ) }
                        initialOpen={ true }
                    >
                        <SelectControl
                            label={ __( 'Spacing' ) }
                            value={ spacing }
                            options={ spacingControlOptions }
                            onChange={ ( selectedSpacingOption ) => {
                                props.setAttributes( {
                                    spacing: selectedSpacingOption,
                                } );
                            } }
                        />
                    </PanelBody>
                </InspectorControls>
            </Fragment>
        );
    };
}, 'withSpacingControl' );

addFilter( 'editor.BlockEdit', 'extend-block-example/with-spacing-control', withSpacingControl );

We first create a Higher Order Component (HOC) withSpacingControl. It receives the original block BlockEdit component which we can extend.

if ( ! enableSpacingControlOnBlocks.includes( props.name ) ) {
    return (
        <BlockEdit { ...props } />
    );
}

We again check if the block is one of our defined blocks in the enableSpacingControlOnBlocks array. If this isn't the case we return the original BlockEdit component.

const { spacing } = props.attributes;

return (
    <Fragment>
        <BlockEdit { ...props } />
        <InspectorControls>
            <PanelBody
                title={ __( 'My Spacing Control' ) }
                initialOpen={ true }
            >
                <SelectControl
                    label={ __( 'Spacing' ) }
                    value={ spacing }
                    options={ spacingControlOptions }
                    onChange={ ( selectedSpacingOption ) => {
                        props.setAttributes( {
                            spacing: selectedSpacingOption,
                        } );
                    } }
                />
            </PanelBody>
        </InspectorControls>
    </Fragment>
);

Otherwise we wrap the original BlockEdit component in a Fragment and add a SelectControl inside the InspectorControls of the block where the spacing can be chosen.

addFilter( 'editor.BlockEdit', 'extend-block-example/with-spacing-control', withSpacingControl );

To make it work we add our withSpacingControl component to the editor.BlockEdit filter.

The spacing control should now be visible in the WordPress editor as soon as you add an image block.

3. Add margin to the saved markup

We can already select a spacing value in the backend but the change is not yet visible in the frontend. We need to use our spacing attribute to render a specific margin below the image block. We can use the blocks.getSaveContent.extraProps filter to do exactly that. With this filter we can modify the properties of the blocks save element. What we will do is map our chosen spacing to a pixel value which we add as margin-bottom to the image block.

/**
 * Add margin style attribute to save element of block.
 *
 * @param {object} saveElementProps Props of save element.
 * @param {Object} blockType Block type information.
 * @param {Object} attributes Attributes of block.
 *
 * @returns {object} Modified props of save element.
 */
const addSpacingExtraProps = ( saveElementProps, blockType, attributes ) => {
    // Do nothing if it's another block than our defined ones.
    if ( ! enableSpacingControlOnBlocks.includes( blockType.name ) ) {
        return saveElementProps;
    }

    const margins = {
        small: '5px',
        medium: '15px',
        large: '30px',
    };

    if ( attributes.spacing in margins ) {
        // Use Lodash's assign to gracefully handle if attributes are undefined
        assign( saveElementProps, { style: { 'margin-bottom': margins[ attributes.spacing ] } } );
    }

    return saveElementProps;
};

addFilter( 'blocks.getSaveContent.extraProps', 'extend-block-example/get-save-content/extra-props', addSpacingExtraProps );

In the addSpacingExtraProps function we create a little mapping object where which we can use to map our selected spacing value (small, medium or large) to an appropriate pixel value.
Afterwards we add this value as margin-bottom to the style attribute of the block.

That's already it! Our selected spacing value gets mapped to an appropriate margin-bottom value and is added to the saved markup of the block.

Wrap up

It's not that hard to extend an existing Gutenberg block. So before creating a new block think about if it would make more sense to extend an existing block to your own needs.

This tutorial is also available as a WordPress plugin on GitHub: https://github.com/liip/extend-block-example-wp-plugin. Feel free to use it as a starting point to extend Gutenberg blocks in your WordPress site.

]]>
How API first CMS disrupts copywriting https://www.liip.ch/fr/blog/api-first-cms-disrupts-copywriting https://www.liip.ch/fr/blog/api-first-cms-disrupts-copywriting Thu, 28 Mar 2019 00:00:00 +0100 Samuel was our speaker at the Content Strategy Lausanne meetup on March 12th.
Join our community and learn about Content Strategy!

Today content is accessible on various platforms: website, apps, television, social media. With the increasing development of IoT objects, the headless CMS trend is raising. From a copywriting perspective, it is a nightmare to provide custom content for all these channels. Is API first CMS the solution?

When Samuel Pouyt chose to use a headless CMS for the European Respiratory Society, he did not know where he was going. He took the risk of trying a new approach to content management. The API first approach changed his way of working while simplifying it. He made many learnings, some of them because of implementation errors.

Liip: What is an API first CMS?
Samuel: It is a CMS that does not have a front-end and only exposes an API. API stands for Application Programming Interface. An API is a standardized and documented way for applications to communicate. Read here to understand better what an API is.
An API first CMS that only offers an API and no frontend is called a “pure” API first CMS. It is opposed to other mixed forms of API first CMS that offers a little bit of frontend.
In a “pure” API first CMS, content is not mixed with html or context dependent formatting. In a “pure” API first CMS, the CMS does not influence how and where the content is presented. The medium formats the content according to its needs. The medium can be a device, a connected object, a website (a phone, a web application, a social media, a newsletter, a TV, etc.)

Why did you choose this type of CMS for the European Respiratory Society?
Samuel: Content management became unmanageable. We had up to 26 instances of a CMS and a few other unrelated CMS’s. We needed to find a solution. It was difficult to manage updates, redistribution and aggregation of content dispatched in different systems. We also had to handle technical migrations, security updates and patches. We spent an increasing amount of time for maintenance instead of developing new features and updates.
When we started thinking about developing an app, it was my opportunity to try an innovative solution. How could we distribute the content? Write an API on top of our existing CMS’s? I made the case for API first CMS’s. I showed how we would save time in maintenance, development (backend and frontend) and how easy it would be to redistribute content. My manager understood the issue. He agreed to go ahead with my solution of API first CMS.

Liip: What were your challenges during the implementation?
Samuel: Challenges with API first CMS’s are very subtle and not obvious at first sight. First of all, menu management is trivial in a classic CMS, in an API first CMS it is not…Secondly, the front-end needs to be truly decoupled from the API. It is really easy to implement some logic in the frontend that somehow transforms the content. But that transformation will not be available for other systems when they call the content API. Finally, caching is another area that needs extra care as the frontend needs to be notified when content changes in a completely separate system.

If you are further interested in menu management, read my tutorial How to manage menus with Cloud CMS about the solution we implemented at the European Respiratory Society.

With an API first CMS comes flexibility big time: how do you ensure that the content edition team has a clear view on what’s important?
Samuel: It depends who controls flexibility. In my opinion, flexibility is not in the hands of the content edition team. The flexibility here is for the the developer. For the content edition team, nothing changes. The content edition team has a UI where they can manage and organise content. On the other hand developers have total flexibility in what technology they use, how they implement it, how they query content etc. Let’s say that the content edition team has access to the configuration of the CMS, in the worst case, the frontend will not display anything… as changes will have to be implemented in code too… Therefore I would recommend to have a clear understanding of what the content edition team wants to do, and how they want to do it, then everything is straight forward.

Liip: How does an API first CMS change the work and process of the copywriting team?
Samuel: Copywriters need to be trained to think differently about content. Typically, copywriters think about pages, and how the text and images will look like in the context of a page. Copywriters are often used to create content in CSM called WYSIWYG (What You See Is What You Get).
In a CMS that does not have frontend and in a world where your content can end up on so many media (website, apps, social media, newsletters, tv, etc.) copywriters have to come back to the essence: the copy. Sometimes images will be used, sometime not, or a video will be added etc. the copywriter does not control that. Of course this is not always possible, and previews can be implemented, even a website can be rendered “in a future state” to let copywriters verify that it it looks how they wish. With an API first CMS this is extra development, it is not a preview button somewhere on the editors’ UI.

Liip: How did your development team work with the copywriting team to help them create content suitable to be used in an API first CMS? I expect they had to change their writing process?
Samuel: The change has been gradual, we have introduced our API First CMS among the other CMS’s we were using. At first the change impacted only a few users. Gradually the change impacted the whole company. This gave us a chance to train first the most technical members of our copywriting team. It allowed us to fix a lot of small issues and miscomprehension. Eventually, we organized a training for all content editors. In collaboration with the communication department, we created an “onboarding” kit, that included: a style guide (specific formatting of text for the ERS), a general guide on how to write on the web, and a technical guide on markdown (a text format) and how to create/edit/publish/schedule/etc. articles.

Liip: What did you do with the content of the website when you changed the CMS? Did you migrate or did you rewrite all the content?
Samuel: Both… We introduced the API first CMS at the same time as a long overdue migration of the main website. The communication team had reorganised the content and menus for some page. We migrated other pages. With an API first CMS the ingestion of content is really easy as normally you have API endpoints that let you write content. Migration scripts are therefore easier to write.

An API first CMS can also mean that you have to build lots of standard stuff (such as SEO meta information) from scratch. How did you for now ensure these kind functionality for content editors were still available w/out going the extra mile and re-develop it?
The European Respiratory Society’s frontend are developed with Vuejs and Laravel. These open source ecosystems are really rich and many developers have already contributed useful plugins.

For this reason, I used “plugins” that allowed to generated all the meta tags. I also went the extra mile by developing exactly what we needed to improve our content discovery: a search engine and Natural Language Processing “modules” to extract information and classify our content. The search engine and Natural Language Processing “modules” benefited from the API first approach. Content can easily be modified by search engine and Natural Language Processing “modules” by calling the correct endpoints.

Today, there are a lot of misconceptions with API first CMS. It is correct that you have to build a whole frontend. It is often wrong that building a whole frontend takes more time than developping or adapting a Wordpress theme. For example, making sure that all outputs of that CMS are correctly overwritten is time-consuming.

Liip: When would you recommend an API first CMS? For what kind of company or what kinds of needs?
Samuel: If you manage more than one website, or if you need to publish content across different systems, I would recommend an API first CMS. If you are for example a publishing house or a fashion house and that you manage a lot of media and you have the budget, checkout big name CMSs such as Arc Publishing or Adobe. If you need a simple website or blog go for a Wordpress like website, or even simpler solution such as Wix.

The API first CMS landscape is changing very fast. There is a convergence happening. On the one hand, some providers propose easy ways to have a frontend on top of their APIs and, on the other hand, Non-API first CMS start proposing APIs. There are very few API first CMS that have a level of maturity that make them enterprise grade content CMS.

Liip: What do you mean by fully integrated ecosystem?
Samuel: For example, Adobe CMS, the Arc Publishing ecosystem or Chorus Publishing suite (Vox Media) are not API first CMS but fully integrated ecosystem. They are expensive and come with complex workflows management capabilities and many publishing pipelines. For example, they integrate the photographers’ camera into complex workflows and publishing pipelines.
For example, let’s say that you want to create a printed fashion catalogue, a magazine and a website to sell you content. The pictures do not go directly to the website, but are integrate in a publishing workflows. You organise a photo shooting, and the pictures are taken are right away named and uploaded in the DAM of the CMS , ready for review and selection. Copywriters ad text and all this content gets published on the online, shop, magazine and catalogue. Copywriter work on Indesign for the catalogue, and share the text for the website, and other media.

Liip: Do you think API first CMS can compete with such integrated ecosystem?
Samuel: No API first CMS that I have heard of can compete (yet?) with integrated ecosystems like Adobe CMS, the Arc Publishing ecosystem or Chorus Publishing suite (Vox Media). The only API first CMS that has similar advanced features is Cloud CMS. We chose Cloud CMS at the European Respiratory Society after checking different API first CMS. Many of the CMS in this list are very young and do not have the feature that you can expect for enterprise grade CMS such as content versioning, publishing workflows, right managements, etc.

If you want to compare headless CMS, read A List of Content Management Systems for JAMstack Sites.

Liip: Let’s summarise, integrated ecosystems and API first...
Samuel: So if you are working in media, maybe it makes sense to go for a CMS that can integrate with your publishing software such as InDesign. If you just have to build a small website, maybe go for wordpress. But if you think about content as a service in your infrastructure, go for an API first CMS. For me the trigger would be questions such as “How do we get the content from our CMS in the App?” as soon as content as to appear in different places, and API of some sort will be needed, considering an API first CMS can save you a lot of time and money.

Liip: Thank you for the interview Samuel!

]]>
Try out Scrawl, our new multiplayer game! https://www.liip.ch/fr/blog/try-scrawl-multiplayer-game-ios https://www.liip.ch/fr/blog/try-scrawl-multiplayer-game-ios Mon, 25 Mar 2019 00:00:00 +0100 TL;DR: Download Scrawl from the iOS App Store and challenge your friends or strangers!

At Liip we are lucky to be able to take time to experiment new technologies and have fun with it. When we start what we call an "inno project", we always set some rules as if it was a client project:

  • Time budget
  • Technologies we want to test
  • Deadline

As we often work on inno projects when we don’t have client work, setting those rules help us making sure that the project has a worthwhile outcome.

We wanted to dig deeper into Firebase (Firestore, Cloud Function, Authentication) and add Machine Learning on mobile. We came up with a simple but fun game that includes all those technologies:

  • Create a game against another player
  • Make a drawing of a word that has been given to you randomly
  • Our custom AI™ tries to recognize the two drawings
  • The player with the most legible drawing wins !

We teamed-up with three teams at Liip: both Mobile teams in Fribourg and Lausanne, and the Data Services team in Zürich. After a day trying different possibilities with the data coming from Quick, Draw!, we were already able to have a working prototype. A few days of work after that, we are proud to release Scrawl to the world!

Try it out !

Checkout the Scrawl website and download the iOS app!

We wanted to be GDPR-compliant. We don't store anything personal except the email used to connect. Each user has a random username. You can find your own name on the application’s home page. Use this to challenge other people you know.

Tell us what you think

Keep in mind that this is the result of work in our spare time so the game has a few rought edges. This is why we would love to get your feedback. Leave us a comment below or tweet at us!

]]>
Customizing your Breadcrumbs https://www.liip.ch/fr/blog/customizing-your-breadcrumbs https://www.liip.ch/fr/blog/customizing-your-breadcrumbs Fri, 22 Mar 2019 00:00:00 +0100 Drupal core provides us with builder services ordered by a priority. More builders can be found in contrib modules and you always can create custom builders yourself. Builder are services - if you aren’t familiar with them - this might be helpful.

The core path based builder

In case your URL patterns exactly represent your breadcrumb, you struck gold. Drupal core provides us with a PathBasedBreadcrumbBuilder which is enabled by default and therefore doesn’t need a set up.
However, in case you want some configurable options - such as editing or hiding the home link and excluding pages - the “Easy Breadcrumb” (easy_breadcrumb) module could come in handy. After the installation, the mentioned configuration page in the backend can be found under “example.com/admin/config/user-interface/easy-breadcrumb”.

The contrib menu based builder

Path based breadcrumbs covers most use cases. However, sometimes the content manager needs more configuration control. In this case it could make sense to have the breadcrumb follow the exact structure of the menu defined by the core menu without regarding the URL path.
To achieve this, one can utilize the “Menu Breadcurmb” (menu_breadcrumb) module. This builder service relies on the active trail provided by the core service “@menu.active_trail”. Configuration for that module can be found under the URL “examle.com/admin/config/user-interface/menu-breadcrumb”, You can choose the menus to be represented, set whether or not the home link shall be included and the current page needs to be appended.

Manually adding each node to the menu in order to show up in the menu-based breadcrumb is not necessary. The “Menu Position” (menu_position) module allows you to dynamically add nodes to the active trail based on conditional rules, provided by the Condition plugin type.
There are several plugins out of the box by Drupal core. The UserRole plugin, the Language plugin, the CurrentTheme plugin and most notably the NodeType plugin, which allows you to set rules based on the node bundle.
There are many more plugins to be found in core and in contrib modules. One module to be highlighted would be the “Term Condition” (term_condition) contrib module, thanks to whom you are able to evaluate whether a taxonomy terms is referenced on one of the nodes fields now. Last but not least, in case the requirements are outstandingly peculiar you always have the option to create plugins yourself.

Disable an unwanted builder

Having created an awesome breadcrumb mechanic that covers every necessary use case, the low priority builders may be redundant. The PathBasedBreadcrumbBuilder is always able to create a breadcrumb and the only way of getting rid of it is to replace it with a non applying builder.

services:
    system.breadcrumb.default:
        class: Drupal\zoo_customizations\Builder\NoBreadcrumbBuilder
        tags:
            - { name: breadcrumb_builder, priority: 0 }

In the services.yml one can override any service including the PathBasedBreadcrumbBuilder. The replacement breadcrumb builder must implement the BreadcrumbBuilderInterface and therefore the two functions applies() and build(). Unless applies() returns true in some cases build doesn't need an implementation.

/**
 * This service overrides the PathBasedBreadcrumbBuilder effectively disabling it.
 *
 * Class NoBreadcrumbBuilder.
 *
 * @package Drupal\zoo_customizations\Builder
 */
class NoBreadcrumbBuilder implements BreadcrumbBuilderInterface {

  /**
   * @inheritdoc
   */
  public function applies(RouteMatchInterface $route_match) {
    return FALSE;
  }

  /**
   * @inheritdoc
   */
  public function build(RouteMatchInterface $route_match) {
    // Wont be implemented.
  }

}

Custom rendering

The frontend inclusion is done via block, in particular the “system_breadcrumb_block”, which should be included by default after installing Drupal. To get familiar with blocks, this might be helpful. The Block can also be added anywhere by using the contrib module TwigTeaks 2.x. Add the following line anywhere in your Twig templates.
{{ drupal_block('system_breadcrumb_block') }}

No matter which implementation is used, the block itself is being rendered and utilizes the breadcrumb.html.twig template.

]]>
Easily save your Android ViewModel state https://www.liip.ch/fr/blog/easily-save-android-viewmodel-state https://www.liip.ch/fr/blog/easily-save-android-viewmodel-state Tue, 19 Mar 2019 00:00:00 +0100 Last week, Google released ViewModel Savedstate 1.0.0. Its documentation states:

ViewModel objects can handle configuration changes so you don't need to worry about state in rotations or other cases. However, if you need to handle system-initiated process death, you may want to use onSaveInstanceState() as backup.

When an Activity is destroyed because of memory pressure, the state of the ViewModel is not saved. When the Activity is recreated, the ViewModel will have lost data that was only stored in memory. This is a problem that we often encounter, so we came up with a custom solution. But now Google released its official library to handle this case and I was happy to play with it.

There is already a nice deep-dive article about it, but I wanted to take a step further. I wanted to make it so easy that you don’t even have to think about saving your state.

Define ViewModel variables like you are used to

With my collection of ViewModel SavedState Helpers, you can define your ViewModel like if it was a local variable.

// Import the library
import ch.liip.viewmodelsavedstatehelpers.*

// Define a ViewModel that takes a SavedStateHandle in argument
class MainViewModel(handle: SavedStateHandle) : ViewModel() {
    // Simple string that is saved in the SavedState
    var manualText by handle.delegate<String?>()

    // MutableLiveData that is saved in the SavedState
    val liveDataText by handle.livedata<String?>()
}

You can then use the ViewModel like you would do usually. Your data is saved and restored automatically!

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // Obtain the ViewModel with SavedStateVMFactory
    viewModel = ViewModelProviders.of(this, SavedStateVMFactory(this)).get(MainViewModel::class.java)

    // Observe the livedata
    viewModel.liveDataText.observe(this, Observer {
        liveDataText.setText(it)
    })

    // Save the values
    button.setOnClickListener {
        viewModel.liveDataText.value = liveDataText.text.toString()
    }
}

How does it work ?

This is heavily related to our work on SweetPreferences. I basically use Kotlin delegated properties to wrap the calls into a SavedStateHandle, which is the main class offered by the ViewModel Savedstate library.

Take a look at the only file to see the whole code. Then install the library from Github and enjoy your peace of mind when it comes to saving state!

]]>