Articles de l'utilisateur

Par Kgaut
Adhérent
Kevin Gautreau

Pushover - Envoyer des notifications push depuis Drupal 8

Pour d'autres raisons j'ai utilisé le service de Pushover, qui permet d'envoyer des notifications push vers son téléphone mobile. Dans le principe, vous installer l'application sur votre téléphone / tablette (android ou apple) et via une api vous pourrez envoyer une notifications vers un, ou l'ensemble de vos devices.

J'ai aussi créé un module pour intégrer le service et ainsi pouvoir envoyer une notification push depuis son site Drupal 8. Quelle utilité ? À vous de voir, je l'ai personnellement utilisé pour recevoir une notification push en cas de nouveau commentaire ou bien d'alerte critique sur le site.

Le module est sur Github : https://github.com/kgaut/drupal-pushover, on peut l'installer via composer :

# Ajout du dépot
composer config repositories.drupal-pushover vcs https://github.com/kgaut/drupal-pushover
# Téléchargement du module
composer require drupal/pushover

Une fois installé on le configure via le formulaire dans Configuration / Services Web / Pushover Configuration

pushover-drupal-configuration.jpg

Le code pour déclencher une notification est le suivant :

\Drupal::service('pushover.sender')->sendNotification($title, $message, $url = NULL, $url_title = NULL);

Un exemple d'intégration pour envoyer une notification lors d'un nouveau commentaire :

/**
 * Implements hook_HOOK_insert().
 *
 * Send Pushover notification on new comment.
 *
 * @param \Drupal\comment\Entity\Comment $comment
 */
function kgaut_comment_insert(\Drupal\comment\Entity\Comment $comment) {
  $node = $comment->getCommentedEntity();
  $author = $comment->getAuthorName();
  $title = t('Nouveau commentaire sur Kgaut.NET');
  $description = t('@name à posté un nouveau commentaire sur « @post_title »', [
    '@name' => $author,
    '@post_title' => $node->label(),
  ]);
  $url = $node->url('canonical',['absolute' => TRUE]);
  \Drupal::service('pushover.sender')->sendNotification($title, $description, $url, t('Voir le post'));
}

Et voila le résultat :

pushover-notification-small.png

Bon évidement, on doit aussi pouvoir trouver des usages utiles :)

Ça n'est pas du drupal, mais j'ai aussi ajouté une notification quand quelqu'un se connecte en ssh sur mon serveur, je partagerai peut-être le script en question un de ces 4 !

Par Kgaut
Adhérent
Kevin Gautreau

Drupalcamp Lannion : le programme est annoncé !

La sélection des sujets est terminée, le programme des deux jours de conférence du drupalcamp à Lannion des 27-28 et 29 octobre est maintenant connu !

Au programme : du drupal 8, du cache, du templating, des tests, du composer, de la config, des bonnes pratiques...

Deux salles, deux ambiances : une salle plus dédiées pour les débutants et les sujets plus larges et une salle avec sujets « barbus ».

D'autres salles seront aussi disponible pour des BOF (petites discussions improvisées ou suites informelles de sessions).

Personnellement je donnerai une présentation sur comment gérer son site drupal 8 avec composer et git le samedi matin.

Pour rappel, le 3e jour sera dédié aux ateliers et sprints.

Merci à toutes les personnes qui nous ont proposé des sujets !

Pour rappel, l'accès aux conférence est libre et gratuit, mais l'association propose des « packs weekend » comprenant les repas du midi, le café, le traditionnel sac de goodies et l’accès aux soirées communautaires, proposé à un prix plus que raisonnable (15€ pour les trois jours). Je ne peux que vous encourager à soutenir l'association et prendre votre pack : https://lannion2017.drupalcamp.bzh/reservations

Enfin, toujours pour proposer un tarif aussi accessible, il reste des packs de sponsoring disponible : https://lannion2017.drupalcamp.bzh/sponsors/forfaits

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Accèder à $_GET et $_POST

Récupérer $_GET['test'] :

<span>$</span>test<span> </span><span>=</span><span> \Drupal</span><span>::</span><span>request()->query->get</span><span>(</span><span>'</span>test<span>'</span><span>);</span>

Récupérer $_POST['test'] :

<span>$</span>test<span> </span><span>=</span><span> \Drupal</span><span>::</span><span>request</span><span>()-></span><span>request</span><span>-></span><span>get</span><span>(</span><span>'</span>test<span>'</span><span>);</span>

Récupérer un tableau associatif avec toutes les variables $_GET :

<span>$variables_get = \Drupal::request()->query-></span>all<span>(</span><span>);</span>

Récupérer un tableau associatif avec toutes les variables $_POST :

<span>$variables_post</span><span> </span><span>=</span><span> \Drupal</span><span>::request()->request-></span>all<span>(</span><span>);</span>

 

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Les EntityQuery par l'exemple

Remplacement des Entity Field Query en drupal 7, les Entity Query permettent d'effectuer des requêtes sur nos types d'entités, (custom ou non) selon leurs propriétés ou leurs champs (fields).

Elles sont une bonne solution pour faire des queries même avancées, sans avoir à faire des jointures à en perdre la tête.

Un exemple simple

Récupération et chargement de l'ensemble des utilisateurs activés.

//On commence par donner le type d'entité que l'on souhaite « requêter »
$query = \Drupal::entityQuery('user'); 
//On ajoute une condition, ici « status = 1 » pour ne récupérer que les utilisateurs actifs
$query->condition('status', 1); 
//On lance la requête et récupère les ids des utilisateurs.
$users_ids = $query->execute(); 
//on charge les utilisateurs en question
$users = User::loadMultiple($users_ids);

Que peut-on peut requêter ?

Sous drupal 8, tout est entité (ou presque), on peut ainsi faire des requêtes sur :

  • Les noeuds
  • Les blocs
  • Les utilisateurs
  • Les menus
  • Les éléments de menus
  • Nos types d'entités custom
  • ...

Bref, beaucoup de chose !

La déclaration du type d'entité se fait lors de la génération de la requête :

Pour requêter les utilisateurs
$query = \Drupal::entityQuery('user');

Pour requêter les noeuds
$query = \Drupal::entityQuery('node');

Les conditions

$query->condition(champ_ou_propriété, valeur, opérateur);

Le champ sur lequel on fait une condition peut -être soit une propriété (nid, changed, created, title, name...) ou bien un champ « field » (field_tags, field_image...)

Par défaut l'opérateur est : = (égal)

$query->condition('uid', 1);

mais on peut évidement le spécifier :

$query->condition('uid', $user->id(), '');

On peut utiliser le IN, afin de, par exemple récupérer et charger tous les users en fonction d'un field "interest" qui fait référence à des termes de taxonomies :

$interests = [127, 128, 27];
$query = \Drupal::entityQuery('user');
$query->condition('field_interests', $interests, 'IN');
$profils_similaires = $query->execute();

$users = User::loadMultiple($profils_similaires);

Avec un "LIKE", afin de tester le même département :

$query->condition('field_adresse_zipcode', substr($code_postal, 0, 2).'%', 'LIKE');

Conditions multiples

Aussi on peut ajouter plusieurs conditions, ici je fais un test sur la date de naissance, qu'elle soit bien comprise entre $min_date & $max_date :

$query = \Drupal::entityQuery('user');
$query->condition('field_interests', $interests, 'IN');
$query->condition('field_birthday', $min_date->format('Y-m-d'), 'condition('field_birthday', $max_date->format('Y-m-d'), '>');
$profils_similaires = $query->execute();

$users = User::loadMultiple($profils_similaires);

Avec des conditions Or

On peut utiliser des conditions logiques « OR », ici je veux récuperer les utilisateurs qui ont les mêmes centres d'intérêts (field_interests) OU qui sont manuellement mis en avant (field_pinned)

$query = \Drupal::entityQuery('user');

$condition_or = $query->orConditionGroup();
$condition_or->condition('field_pinned',1);
$condition_or->condition('field_interests', $interests, 'IN');

$query->condition($condition_or);

$profils_similaires = $query->execute();

$users = User::loadMultiple($profils_similaires);

Avec des conditions OR et AND

On peut faire des conditions un peu plus balaises, avec ici donc : soit les utilisateurs mis en avant (field_pinned) OU (qui ont les même centres d'intérêts (field_interest) ET qui sont nés après $max_date ET avant $min_date :

$query = \Drupal::entityQuery('user');

$condition_or = $query->orConditionGroup();
$condition_or->condition('field_pinned',1);

$condition_and = $query->andConditionGroup();

$condition_and->condition('field_interests', $interests, 'IN');
$condition_and->condition('field_birthday', $min_date->format('Y-m-d'), 'condition('field_birthday', $max_date->format('Y-m-d'), '>');

$condition_or->condition($condition_and);

$query->condition($condition_or);

$profils_similaires = $query->execute();

$users = User::loadMultiple($profils_similaires);

Gestion de la pagination et du nombre de résultats

$query->range(0, 20);

Gestion du tri

$query->sort('created');

ou en précisant le sens :

$query->sort('field_birthday', 'ASC');

 

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Définir l'item de menu actif en fonction du type de noeud

Sur ce site, j'ai trois types de contenu :

  • Article
  • Snippet
  • Réalisation

Pour chacun de ces trois types de contenu, j'ai une vue (View) de listing qui correspond (cf le menu ci-dessus).

Par défaut quand on est sur la vue de listing des snippets par exemple, on a bien l'élément de menu "Snippets" qui est marqué comme actif, par contre quand on va sur le détail d'un snippet, on perd cette information. Nous allons voir comment sélectionner le menu item qui doit être actif en fonction du type de contenu sur lequel on se trouve.

1ère étape : surcharger le service MenuActiveTrail

dans mon module « kgaut » je créé le fichier KgautMenuActiveTrail.php dans le dossier src/Menu avec le contenu suivant :

routeMatch->getParameter('node');

    if ($node instanceof NodeInterface) {
      $bundle = $node->bundle();
      switch ($bundle) {
        case 'article':
          //Ici je demande à recherche le menu item qui utilise la route correspondant à mon type de contenu
          $links = $this->menuLinkManager->loadLinksByRoute('view.front_articles.page', [], $menu_name);
          break;

        case 'snippet':
          $links = $this->menuLinkManager->loadLinksByRoute('view.front_snippets.page', [], $menu_name);
          break;

        case 'realisation':
          $links = $this->menuLinkManager->loadLinksByRoute('view.front_realisations.page', [], $menu_name);
          break;
      }

      if (isset($links) && $links) {
        $found = reset($links);
      }
    }
    return $found;
  }
}

 

C'est bien beau, mais maintenant il faut signaler à Drupal que l'on a surchargé un de ses services. Pour cela il faut créer le fichier src/MonModuleServiceProvider.php (attention c'est normé !) Dans mon cas j'ai donc créé le fichier KgautServiceProvider.php dans le dossier src de mon module kgaut (oui je suis très égocentrique).

En voici le contenu :

getDefinition('menu.active_trail')->setClass('Drupal\kgaut\Menu\KgautMenuActiveTrail');
  }

}

Un petit vidage de cache plus tard, et on est bon !

Par Kgaut
Adhérent
Kevin Gautreau

Drupal Camp Lannion 2017 - 27-28-29 Octobre

L'association Drupal France et l'équipe de bénévoles, organise comme chaque année un Drupal Camp, pour 2017, c'est en Bretagne, à Lannion du 27 au 29 octobre !

Drupal Camp, c'est quoi ? C'est l’évènement français incontournable pour tous les utilisateurs du CMS : développeurs, chefs de projets, Webmasters, Référenceurs... 2 Jours de conférences et un jours de sprints.

Les journées de vendredi et samedi sont dédiées aux conférences et discussion. La journée du dimanche est consacrée aux ateliers et sprints (contributions). Un atelier est déjà prévu : un sprint de traduction de drupal en breton. https://lannion2017.drupalcamp.bzh/brezhoneg.

L'accès aux conférence est libre et gratuit, mais l'association propose des « packs weekend » comprenant les repas du midi, le café, le traditionnel sac de goodies et l’accès aux soirées communautaires, proposé à un prix plus que raisonable (15€ pour les trois jours) je ne peux que vous encourager à soutenir l'association et prendre votre pack : https://lannion2017.drupalcamp.bzh/reservations

Afin de proposer un tarif aussi accessible, nous faisons appels à des partenaires pour nous aider financièrement dans l'organisation de cet évènement : https://lannion2017.drupalcamp.bzh/sponsors/forfaits

Enfin, comme chaque année, c'est vous qui faites le contenu des drupal camp ! N'hésitez-pas à proposer un sujet de conférence, même si vous n'êtes pas un expert Drupal, vous aurez toujours quelque chose à apprendre aux participants : https://lannion2017.drupalcamp.bzh/programme/guide-aux-conferenciers.

Espérant vous voir nombreux à Lannion fin octobre, n'hésitez-pas à me faire un petit coucou une fois là bas ! (Malheureusement, Jean-Michel, mon suricate ne devrait pas faire le déplacement...)

Par Kgaut
Adhérent
Kevin Gautreau

Message de service : Migration du site sous Drupal 8

j'ai enfin terminé la migration de ce site vers Drupal 8 !

À l'origine un drupal 6, j'avais migré le site en 2012 vers Drupal 7, il était maintenant temps de tester la Migrate API pour tester la migration de contenu vers un site en drupal 8.

J'ai donc créé un module pour migrer l'ensemble des contenus de l'ancien site (articles, réalisations, snippets, commentaires, flag, redirections, fichiers...) Ayant eu du mal à trouver des ressources à jour et fonctionnant correctement, j'ai mis l'ensemble du module sur github, espérant que cela serve à d'autres : https://github.com/kgaut/kgaut_migrate.

Au niveau du front, comme vous le voyez, je suis resté simple (je suis développeur back après tout) j'ai créé un thème basé sur bartik, que j'ai légèrement personnalisé.

Je suis en train de faire le tour mais tout à l'air de fonctionner correctement. Si jamais vous remarquez quelque chose qui ne correspond pas à ce que vous attendez, n'hésitez-pas à me le signaler.

 

 

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Entité - Champ de base « Référence à un terme de taxonomie »

Pour faire un champ de base référence à un terme de taxonomie, on va utiliser le type "entity_reference", en lui passant évidement le nom du vocabulaire, (ici "option_types") :

$fields['type'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Catégorie'))
      ->setSetting('target_type', 'taxonomy_term')
      ->setSetting('handler', 'default:taxonomy_term')
      ->setSetting('handler_settings', [
         // ici définir l'id du vocabulaire
          'target_bundles' => [ 'option_types' => 'option_types']
      ])
      //on utilisera l'affichage en mode "radio / checkbox" en fonction de la cardinalité
      ->setDisplayOptions('form', array(
        'type' => 'options_buttons',
        'weight' => 3,
      ))
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

Pour utiliser le formulaire type "tag" :

    $fields['type'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Catégorie'))
      ->setSetting('target_type', 'taxonomy_term')
      ->setSetting('handler', 'default:taxonomy_term')
      ->setSetting('handler_settings', [
          'target_bundles' => [ 'option_types' => 'option_types']
      ])
      ->setDisplayOptions('form', array(
        'type' => 'entity_reference_autocomplete',
        'weight' => 3,
        'settings' => array(
          'match_operator' => 'CONTAINS',
          'size' => '10',
          'autocomplete_type' => 'tags',
          'placeholder' => '',
        ),
      ))
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

Note, pour avoir une cardinalité illimitée (autant de valeurs possibles que l'utilisateur le désir) ajouter l'appel suivant à la déclaration de votre propriétée :

->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)

 

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Views - Ajouter une relation vers une entité de type custom

Contexte :

Un type d'entité custom "Carte" qui utilise come table de stockage "cards". Cette entité possède une propriété "owner" qui permet de la relier à l'utilisateur qui possède la carte. (propriété du type "entity_reference").

Dans une vue listant les cartes, on peut facilement créer une relation vers le "owner" pour accèder à ses informations.

Par contre sur une vue listant les utilisateurs, on ne peut pas avoir la relation inverse, liant les utilisateurs à leur carte. Pour cela il faut utiliser le hook_views_data_alter :

/**
 * Implements hook_views_data_alter().
 */
function MONMODULE_views_data_alter(array &$data) {
  $data['users_field_data']['cards'] = [
    'title' => t("Les cartes de l'utilisateur"),
    'help' => t("Permet de relier l'utilisateur courant à ses cartes"),
    'relationship' => [
      'group' => t('Cartes'),
      'label' => t("Cartes de l'utilisateur"),
      'base' => 'cards', // nom de la table stockant nos entités custom
      'base field' => 'owner', // nom du champ sur lequel faire la jointure
      'relationship field' => 'uid', // nom du champ cible sur lequel faire la jointure (de la table users_field_data)
      'id' => 'standard',
    ],
  ];
}

Merci à flocondetoile sur IRC pour m'avoir mis sur la bonne piste !

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Entité - Champ de base « image »

$field['photo'] = BaseFieldDefinition::create('image')
      ->setLabel(t('Photo'))
      ->setDescription(t("Photo du contenu"))
      ->setSettings([
        'file_directory' => 'dossier/image',
        'alt_field_required' => FALSE,
        'file_extensions' => 'png jpg jpeg',
      ])
      ->setDisplayOptions('view', array(
        'label' => 'hidden',
        'type' => 'image',
        'weight' => 0,
      ))
      ->setDisplayOptions('form', array(
        'label' => 'hidden',
        'type' => 'image_image',
        'weight' => 4,
      ))
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

Merci DuaelFr ;)

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Ajouter des suggestions de template pour les Noeuds

Dans drupal 8, parfois le thème ajoute des suggestions de template qui vont bien, en fonction du type de noeud et du view_mode, mais ça n'est pas toujours le cas, voici comment faire pour ajouter des suggestions de template via le hook HOOK_theme_suggestions_HOOK() :

function MONMODULE_theme_suggestions_node(array $variables) {
  $suggestions = [];
  /** @var \Drupal\node\Entity\Node $node */
  $node = $variables['elements']['#node'];
  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
  $suggestions[] = $variables['theme_hook_original'] . '__' . $sanitized_view_mode;
  $bundle = $node->bundle();
  $sanitized_bundle = strtr($bundle, '.', '_');
  $suggestions[] = $variables['theme_hook_original'] . '__' . $sanitized_bundle;
  $suggestions[] = $variables['theme_hook_original'] . '__' . $sanitized_bundle . '__' . $sanitized_view_mode;
  return $suggestions;
}

Pour un type de contenu article en mode de vue teaser on aura ainsi les possibilités de templates suivantes :

  • node.html.twig
  • node--article.html.twig
  • node--teaser.html.twig
  • node--article--teaser.html.twig
Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Migrate - exemples de migrations "réelles"

Ce site à été construit à l'origine sous Drupal 6, migré sous drupal 7 en 2012, Je suis en train de préparer la migration de ce site vers Drupal 8 via la Migrate API.

Vu que je galère pas mal à trouver des exemples à jours, complets et qui fonctionne, je me suis dis que ce serait intéressant de partager mon expérience, le plus simple étant de partager directement mon module de migration, je l'ai donc mis directement sur github afin que vous puissiez piocher directement dedans pour voir comment je migre l'intégralité du contenu de ce site vers une installation de Drupal 8.

J'ai tenté de faire un readme assez complet, mais les recettes YAML ne sont pas encore super bien documentées, c'est à faire... Pour l'ensemble, c'est du glané à droite à gauche, sur internet mais aussi dans les exemples du module migrate_plus.

En vrac ce que vous pourrez trouver :

  • Explications de base sur la mise en oeuvre de migrate
  • Migration de termes de taxonomie (les vocabulaires étant été recréés préalablement)
  • Migration de noeuds (pareil, les types de contenus et les champs sont recréés manuellement)
  • Migration de « managed files » (fichiers liés à des noeuds (comme l'image d'introduction de ce post)
  • Migration et traitement (process) de zone  « wysiwyg » avec fichiers intégrés : j'en ai profité pour « ranger » l'ensemble des fichiers uploadés dans ckeditor.
  • Migration d'alias d'url (module path)
  • Migration de redirections (module redirect)
  • Migration de flag (les noeuds de ce blog ont des "likes" qui sont des flags.
  • Migration de commentaires
  • Migration d'un champ "images" multiple, avec le plugin iterator

Sera fait dans les prochains jours :

  • Documentation des fichiers YAML, Sources et Process.
  • Migration de paragraphs, si j'ai la foi.

Comme je l'ai dis, je me suis beaucoup inspiré du sous-module migrate_example de migrate_plus, c'est pourquoi il reste encore pas mal de commentaire en anglais faisant référence à des types de contenu « beer », qu'il faut que j'adapte.

Remarque : Je ne suis pas un expert de migrate, l'ensemble est probablement améliorable, mais comme disait un sage : « le mieux est l'ennemi du bien », je cherche avant tout à avoir quelque chose qui fonctionne. Ceci étant dit, n'hésitez-pas à me faire part de vos remarques, commentaires ou questions via les commentaires ou bien les issues sur Github ! Je suis preneur de toute idée d'amélioration.

Le tout est disponible sur Github : https://github.com/kgaut/kgaut_migrate.

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Entité - Champ de base « Date »

    $fields['date_naissance']  = BaseFieldDefinition::create('datetime')
      ->setLabel(t('Date de naissance'))
      ->setRequired(TRUE)
      ->setDefaultValue(NULL)
      ->setSetting('datetime_type', 'date')
      ->setDisplayOptions('form', array(
        'type' => 'date',
      ))
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

 

Pour un champ DateTime : Drupal 8 - Entité - Champ de base datetime

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Ajouter des propriétés à un type d'entité existant

Drupal vient avec des types d'entité prédéfinis (Node, ou User par exemple). À ces type d'entité il est possible d'ajouter des fields, mais il est aussi possible d'ajouter des propriétés.

Voici comment ajouter des propriétés (prénom et nom) aux utilisateurs.

Dans le fichier monmodule.module :

function monmodule_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
  if ($entity_type->id() === 'user') {
    $fields = [];

    $fields['firstname'] = \Drupal\Core\Field\BaseFieldDefinition::create('string')
      ->setLabel(t('Firstname'))
      ->setSettings(array(
        'max_length' => 100,
        'text_processing' => 0,
      ))
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['lastname'] = \Drupal\Core\Field\BaseFieldDefinition::create('string')
      ->setLabel(t('Lastname'))
      ->setSettings(array(
        'max_length' => 100,
        'text_processing' => 0,
      ))
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    return $fields;
  }
}

Dans le fichier monmodule.install :

/**
 * Create user additional properties
 */
function monmodule_update_8001() {
  $entity_manager = \Drupal::entityManager();
  $definition = $entity_manager->getFieldStorageDefinitions('user')['firstname'];
  $entity_manager->onFieldStorageDefinitionCreate($definition);
  $definition = $entity_manager->getFieldStorageDefinitions('user')['lastname'];
  $entity_manager->onFieldStorageDefinitionCreate($definition);
}

function monmodule_uninstall() {
  $entity_manager = \Drupal::entityManager();
  $definition = $entity_manager->getLastInstalledFieldStorageDefinitions('user')['firstname'];
  $entity_manager->onFieldStorageDefinitionDelete($definition);
  $definition = $entity_manager->getLastInstalledFieldStorageDefinitions('user')['lastname'];
  $entity_manager->onFieldStorageDefinitionDelete($definition);
}

 

Par Kgaut
Adhérent
Kevin Gautreau

Docker image pour SolR avec plusieurs cores

Pour un projet en cours sur Drupal 8, j'ai besoin d'un moteur de recherche avec plusieurs indexes. J'utilise évidement le module search_api avec un moteur de recherche SolR, Vu que je suis en mode "développement" je vais utiliser une image docker qui me fournira une installation fonctionnelle de SolR, directement plugable avec Drupal.

Par habitude j'utilise les images de Thiebaud Schmittlin : https://github.com/TehesFR/docker-solr

Une fois docker installé l'image se lance avec la commande suivante :

docker run -p 8080:8983 tehes/docker-solr:4.10

Cela lancera une image SolR 4.10 sur le port 8080, si on souhaite utiliser une autre version du moteur il suffit d'adapter le tag :

docker run -p 8080:8983 tehes/docker-solr:6.6

Et si besoin de plusieurs indexes ?

Par contre dans mon cas, j'avais besoin de plusieurs cores pour mes différents indexes, j'ai donc adapté son image pour en créer une nouvelle : https://github.com/kgaut/docker-solr-multicore

C'est du « quick'n'Dirty » Je me suis basé uniquement sur la version 6.6 de SolR, la seule modification par rapport au DockerFile original est l'ajout d'une variable NB_CORES qui est par défaut à 4. L'image créé donc 4 core permettant d'avoir 4 indexes.

J'ai mis mon image sur Docker hub : https://hub.docker.com/r/kgaut/docker-solr-multicore/

Pour lancer l'image :

docker run -p 8080:8983 kgaut/docker-solr-multicore

4 cores et pourquoi pas 8 ?

Si jamais vous avez besoin de changer le nombre de cores, vous pouvez modifier cette image de la façon suivante :

git clone git@github.com:kgaut/docker-solr-multicore.git my-docker-solr-multicore
cd my-docker-solr-multicore

Modifiez le fichier DockerFile et à  la ligne 11 remplacez "env NB_CONTAINER 4" par "env NB_CONTAINER 8"

Construisez votre image (cette étape peut prendre un peu de temps) :

docker build -t my-docker-solr-multicore .

une fois terminée, lancez l'image :

docker run -p 8080:8983 my-docker-solr-multicore

et bim ! 8 cores :

Améliorations

Je suis loin d'être un expert docker, et si vous connaissez un moyen de passer le nombre de core voulu lors du lancement de l'image, je suis preneur. Même si vu que les cores sont créés au moment du build de l'image, je ne suis pas bien sur que cela soit possible...

Si vous avez d'autres idées d'améliorations, n'hésitez-pas, merci !

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Créer un élément de menu vers une vue

Dans le fichier monmodule.links.menu.yml :

monmodule.menu.cle:
  title: 'Titre de ma mage'
  description: 'Liste des options'
  route_name: view.admin_options.page
  parent: system.admin_content
  weight: 90

Quelques explications :

  • monmodule.menu.cle est la clée du menu item, cette clée doit être unique
  • title : Le label affiché pour le menu
  • description : Sera affichée au rollover
  • route_name : le nom de la route, sous la forme view.NOM_VUE.NOM_AFFICHAGE
  • parent : Le parent (niveau supérieur) de notre élément de menu, ici notre menu item sera présent sous "Contenu" dans le menu d'administration
  • weight : le poids de l'élément de menu, pour gérer l'odre.

Petite astuce pour le nom de la route, rendez-vous sur la page d'édition de votre vue vous retrouverez les élement dans l'url (voici un exemple pour mon cas : http://monsite.dev/admin/structure/views/view/admin_options/edit/page

Si vous souhaitez ajouter le menu item au premier "niveau" d'un menu (donc sans parent), au lieu de "parent", il faut utiliser la clé "menu_name" qui doit référencer le nom machine du menu, voici la structure à adopter :

monmodule.menu.cle:
  title: 'Titre de ma mage'
  description: 'Liste des options'
  route_name: view.admin_options.page
  manu_name: "main"
  weight: 90
Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Rediriger un utilisateur lors de sa connexion

Pour rediriger un utilisateur lorsqu'il se connecte on peut utiliser le hook HOOK_user_login().

Le code qui suit ne redirige que si une redirection n'est pas déjà présente dans l'url :


function MONMODULE_user_login($account) {
  if (!isset($_GET['destination'])) {
    $response = new \Symfony\Component\HttpFoundation\RedirectResponse(\Drupal::url('popote.user.commandes'));
    $response->send();
  }
}

 

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Migrate - exemples réels de migrations

Ce site à été construit à l'origine sous Drupal 6, migré sous drupal 7 en 2012, Je suis en train de préparer la migration de ce site vers Drupal 8 via la Migrate API.

Vu que je galère pas mal à trouver des exemples à jours, complets et qui fonctionne, je me suis dis que ce serait intéressant de partager mon expérience, le plus simple étant de partager directement mon module de migration, je l'ai donc mis directement sur github afin que vous puissiez piocher directement dedans pour voir comment je migre l'intégralité du contenu de ce site vers une installation de Drupal 8.

J'ai tenté de faire un readme assez complet, mais les recettes YAML ne sont pas encore super bien documentées, c'est à faire... Pour l'ensemble, c'est du glané à droite à gauche, sur internet mais aussi dans les exemples du module migrate_plus.

En vrac ce que vous pourrez trouver :

  • Explications de base sur la mise en oeuvre de migrate
  • Migration de termes de taxonomie (les vocabulaires étant été recréés préalablement)
  • Migration de noeuds (pareil, les types de contenus et les champs sont recréés manuellement)
  • Migration de « managed files » (fichiers liés à des noeuds (comme l'image d'introduction de ce post)
  • Migration et traitement (process) de zone  « wysiwyg » avec fichiers intégrés : j'en ai profité pour « ranger » l'ensemble des fichiers uploadés dans ckeditor.
  • Migration d'alias d'url (module path)
  • Migration de redirections (module redirect)

Sera fait dans les prochains jours :

  • Migration de commentaires
  • Migration de flag (les noeuds de ce blog ont des "likes" qui sont des flags.
  • Documentation des fichiers YAML, Sources et Process.

Comme je l'ai dis, je me suis beaucoup inspiré du sous-module migrate_example de migrate_plus, c'est pourquoi il reste encore pas mal de commentaire en anglais faisant référence à des types de contenu « beer », qu'il faut que j'adapte.

Remarque : Je ne suis pas un expert de migrate, l'ensemble est probablement améliorable, mais comme disait un sage : « le mieux est l'ennemi du bien », je cherche avant tout à avoir quelque chose qui fonctionne. Ceci étant dit, n'hésitez-pas à me faire part de vos remarques, commentaires ou questions via les commentaires ou bien les issues sur Github ! Je suis preneur de toute idée d'amélioration.

Le tout est disponible sur Github : https://github.com/kgaut/kgaut_migrate.


 

Par Kgaut
Adhérent
Kevin Gautreau

Docker image pour SolR avec plusieurs cores

Pour un projet en cours sur Drupal 8, j'ai besoin d'un moteur de recherche avec plusieurs indexes. J'utilise évidement le module search_api avec un moteur de recherche SolR, Vu que je suis en mode "développement" je vais utiliser une image docker qui me fournira une installation fonctionnelle de SolR, directement plugable avec Drupal.

Par habitude j'utilise les images de Thiebaud Schmittlin : https://github.com/TehesFR/docker-solr

Une fois docker installé l'image se lance avec la commande suivante :

docker run -p 8080:8983 tehes/docker-solr:4.10

Cela lancera une image SolR 4.10 sur le port 8080, si on souhaite utiliser une autre version du moteur il suffit d'adapter le tag :

docker run -p 8080:8983 tehes/docker-solr:6.6

Et si besoin de plusieurs indexes ?

Par contre dans mon cas, j'avais besoin de plusieurs cores pour mes différents indexes, j'ai donc adapté son image pour en créer une nouvelle : https://github.com/kgaut/docker-solr-multicore

C'est du « quick'n'Dirty » Je me suis basé uniquement sur la version 6.6 de SolR, la seule modification par rapport au DockerFile original est l'ajout d'une variable NB_CORES qui est par défaut à 4. L'image créé donc 4 core permettant d'avoir 4 indexes.

J'ai mis mon image sur Docker hub : https://hub.docker.com/r/kgaut/docker-solr-multicore/

Pour lancer l'image :

docker run -p 8080:8983 kgaut/docker-solr-multicore

4 cores et pourquoi pas 8 ?

Si jamais vous avez besoin de changer le nombre de cores, vous pouvez modifier cette image de la façon suivante :

git clone git@github.com:kgaut/docker-solr-multicore.git my-docker-solr-multicore
cd my-docker-solr-multicore

Modifiez le fichier DockerFile et à  la ligne 11 remplacez "env NB_CONTAINER 4" par "env NB_CONTAINER 8"

Construisez votre image (cette étape peut prendre un peu de temps) :

docker build -t my-docker-solr-multicore .

une fois terminée, lancez l'image :

docker run -p 8080:8983 my-docker-solr-multicore

et bim ! 8 cores :

Améliorations

Je suis loin d'être un expert docker, et si vous connaissez un moyen de passer le nombre de core voulu lors du lancement de l'image, je suis preneur. Même si vu que les cores sont créés au moment du build de l'image, je ne suis pas bien sur que cela soit possible...

Si vous avez d'autres idées d'améliorations, n'hésitez-pas, merci !

Tags: 

 

Par Kgaut
Adhérent
Kevin Gautreau

MesPronos : Une nouvelle saison de pronostics commence

Comme pour la saison dernière, mon site mespronos.net vous proposera de faire vos pronostics sur la saison 2017-2018 de « Ligue 1 Conforama » (oui, c'est comme ça qu'il faut dire maintenant...)

C'est toujours évidement gratuit, toujours la possibilité aussi de créer ses propres groupes pour faire un concours entre amis ou collègues.

Commencé à l'origine pour mettre les mains dans drupal 8 à l'époque des premières version alpha, le module est en version alpha depuis un an maintenant, j'ai bon espoir de sortir une version beta d'ici le mondial 2018. Le code est disponible pour les curieux sur github,  ainsi que sur drupal.org. Alors, oui avant de sortir une version stable, il y a beaucoup de chose à refactoriser afin que le code soit plus propre !

 

 

Tags: 

 

Par Kgaut
Adhérent
Kevin Gautreau

Module Drupal 8 - Flag, un « j'aime » en plus puissant

Flag est un module drupal 7 et 8 permettant de « Marquer » du contenu.

Pour avoir une idée simple de ce que cela veut dire, on peut penser au « j'aime » de facebook. Si un utilisateur clique sur le « J'aime » en dessous d'une photo, alors il la flag.

Par défaut un flag est personnel, donc chacun possède ses propres contenus flagués ou non. Mais un flag peut aussi être global, et dans ce cas, un contenu flagué le sera pour l'ensemble des membres.

Via l'interface d'administration du module, on peut gérer autant de type de flag que l'on veut :

liste des flags

Un flag ne peut s'appliquer qu'à un type d'entité, mais par contre peut s'appliquer à l'ensemble de ses bundles, ou non. Par exemple on peut créer un type de flag pour les nœuds, mais le restreindre à certains types de contenus.

Ensuite le flag peut se comporter comme un champs que l'on choisi d'afficher ou non suivant les view mode, on peut personnaliser pas mal d'option d'affichage :

Options flag

Flag options 2

Flag s'intègre bien avec actions, rules, views...

Les flags sont des types d'entités auxquels on peut ajouter des champs. Si par exemple on l'utiliser pour faire du reporting de contenu non légal, il est utile d'ajouter une zone de texte pour que l'utilisateur explique pourquoi il reporte le contenu.

Pour installer le module deux options, soit via composer avec :

composer require drupal/flag

Soit en le téléchargeant directement sur la page du module sur drupal.org : https://www.drupal.org/project/flag

Par Kgaut
Adhérent
Kevin Gautreau

PHP, Git, MySQL, configuration et environnements

Un point qui revient souvent lors du développement est la gestion des paramètres de configuration (MySQL par exemple) sur les différents environnements (production, préproduction, local…) et entre différents développeurs d'une même équipe.

Si l’on est tout seul à travailler sur un projet avec un seul environnement (la production) alors la question ne se pose pas, on met les paramètres dans le fichier de configuration (wp-config.php pour wordpress, settings.php pour drupal…) et ça fonctionne.

Le problème du versioning de la configuration

Mais si l’on utilise un système de gestion de version (git avec gitlab / github par exemple) alors cela veut dire que nos identifiants de connexion à la base de données de notre site en production sont stockés sur d’autres serveurs que l’unique sur lequel il devrait être...

Github et Gitlab sont des solutions éprouvées, mais on est jamais à l’abri d’une fuite ou d’un piratage, ou tout bêtement de l’oubli de désactiver l’accès au dépôt à un ancien collaborateur avec qui l’histoire s’est mal terminée et qui a une éthique pro douteuse.

Le problème du travail en équipe

Autre potentiel problème, imaginons une équipe de deux personnes (A et B) qui travaillent en local sur un site en développement.

A travaille sous windows avec wamp, par défaut l’identifiant de connexion à la base de données sous wamp est «root», il n’y a pas de mot de passe.

B travaille sous macos avec mamp, par défaut l’identifiant de connexion à la base de données sous mamp est «root», le mot de passe est lui «root».

On voit vite le jeu que cela va être à chaque commit, A et B s'écrasant mutuellement le fichier de configuration de l’autre.

En plus, notre sysadmin étant moins drôle, il refuse de définir le mot de passe de mysql en production sur «root», encore moins marrant, il refuse de ne pas en définir… Donc on se retrouve avec un troisième jeu d'identifiants que l’on devra (re)mettre à chaque mise à jour du site en production…

Note : évidement utiliser "root" comme idenfiant ou mot de passe, ailleurs qu'en local est évidement à éviter impérativement ! Il faut créer un utilisateur qui n'a la main que sur la (ou les) base(s) de données du site. Bonne pratique à utiliser aussi en local, même si c'est moins «dangereux».

Le problème des environnements

En plus des différences d’identifiants de base de données, il existe souvent d’autres divergences entre deux environnements d’un même site.

Généralement notre site ne fonctionne pas exactement pareil en local, sur notre serveur de développement, sur notre préproduction et sur notre serveur de production.

Par exemple, si l'on travail sur un site d'e-commerce, on aura pas forcément la même brique de paiement, ou il faudra lui passer des paramètres différents pour activer le mode test.

Autres exemples : l'affichage des messages d'erreurs, la désactivation du cache en local pour éviter d’avoir à le vider manuellement à chaque modification...

Enfin, On peut (doit ?) aussi désactiver ou intercepter les envois de mail sur les autres serveurs que celui de production.

Les solutions

Avertissement

Les solutions décrites ici sont des solutions que j'ai trouvées, expérimentées, modifiées... Je n'en réclame ni la paternité ni leur supériorité sur une autre solution, d'ailleurs, si vous faites autrement, n'hésitez-pas à en parler.

Se baser sur le HTTP_HOST

Un première possibilité serait, au sein même du fichier de configuration, d’ajouter des conditions en fonction de l’url par exemple.

Ainsi si l’url du site est “monsite.dev” on sait que l’on est en local, si c’est "monsite.com" alors on est en production.

Exemple de code :

//Paramètres par défaut = production
$databases = array (
  'default' => 
  array (
    'default' => 
    array (
      'database' => 'monsite.com',
      'username' => 'monsite',
      'password' => 'loremipsum',
      'host' => 'localhost',
      'port' => '',
      'driver' => 'mysql',
      'prefix' => '',
    ),
  ),
);
$conf['site_env'] = 'PROD';

// Si l'url se termine par .mapreprod.monentreprise.com"
// par exemple monsite.mapreprod.monentreprise.com
// alors on est en préproduction
if(preg_match('`\.mapreprod.monentreprise.com$`',$_SERVER['HTTP_HOST'])) {
  $conf['site_env'] = 'PREPROD';
  $databases['default']['default']['database'] = 'monsite.com';
  $databases['default']['default']['username'] = 'monsite_preprod';
  $databases['default']['default']['password'] = '123456789';
}

// Si l'url se termine par .dev"
// par exemple monsite.dev
// alors on est en local
if(preg_match('`\.dev$`',$_SERVER['HTTP_HOST'])) {
  $conf['site_env'] = 'DEV';
  $conf['theme_debug'] = true;
  $conf['hybridauth_debug'] = 1;
  $databases['default']['default']['database'] = 'monsite.com';
  $databases['default']['default']['username'] = 'root';
  $databases['default']['default']['password'] = 'mysql';
}

Par défaut, je définis les paramètres de production de mon site, ensuite, je me base sur le HTTP_HOST (l'adresse de notre site) pour déterminer l'environnement (ici préproduction ou local).

Et en fonction j'ajuste à la fois ma configuration mysql ainsi que différents paramètres de configuration pour mon site.

À noter, je définis aussi une variable qui contient l'environnement dans lequel je suis (ici : $conf['site_env']) ce qui peut être utile dans notre code pour savoir si l'on doit par exemple envoyer mail, sms, notification...

Quelques inconvénients à cette solution :

  • Les identifiants de notre prod et de nos autres environnement sont versionnés.
  • Si un nouveau développeur arrive sur le projet et qu'il a un fonctionnement différent, alors le fichier de config sera sans cesse écrasé.

La config différentielle dans un fichier non versionné

C'est la solution que j'utilise maintenant, on se base non plus sur l'url mais sur un fichier supplémentaire, qui défini la configuration spécifique et écrase potentiellement la configuration selon l'environnement.

Exemple :

Le fichier de configuration commun et versionné :

// La configuration de la base de donnée n'est pas définie car elle
// spécifique à chaque environnement
 $databases = array();

// On inclue le fichier settings.local.php s'il existe
if (file_exists(__DIR__ . '/settings.local.php')) {
  include __DIR__ . '/settings.local.php';
}

Le fichier de configuration settings.local.php en production (lui non versionné) :

<?php
  $conf['site_env'] = 'PROD';
  $databases['default']['default']['database'] = 'monsite.com';
  $databases['default']['default']['username'] = 'monsite';
  $databases['default']['default']['password'] = 'loremipsum';

Le fichier de configuration settings.local.php en local (lui non versionné non plus) :

<?php
  $conf['site_env'] = 'DEV';
  $conf['theme_debug'] = true;
  $conf['hybridauth_debug'] = 1;
  $databases['default']['default']['database'] = 'monsite.com';
  $databases['default']['default']['username'] = 'root';
  $databases['default']['default']['password'] = 'mysql';

Les fichiers settings.local.php ne sont pas versionnés grâce au fichier .gitignore et cette ligne (ici pour un drupal, mais à adapter en fonction de votre configuration) :

web/sites/*/settings.local.php

Avantages de cette solution :

  • Une fois configuré, le fichier de configuration de base ne bouge pas, il contient la configuration commune du site.
  • Pas de risque de s'écraser mutuellement la configuration entre développeurs.
  • Les fichiers settings.local.php ne sont pas versionné et donc aucun identifiant ne se retrouve sur github ou gitlab.

Je vois un inconvénient principal : quand un nouveau développeur arrive sur le projet, il faut qu'il comprenne cette organisation, c'est pourquoi généralement je crée un fichier settings.local.example.php qui lui est versionné et j'ajoute les lignes suivantes au readme du projet :

Pour ne pas risquer d'altérer la configuration de la base de données en production,
les identifiants de base de données doivent être renseignés dans un fichier `sites/default/settings.local.php`,
vous pouvez copier le fichier `sites/default/settings.local.example.php` en le renommant pour avoir un exemple
de fichier local.

Le fichier settings.php ne doit jamais être modifié directement, sauf cas très particulier.

Conclusion

La solution décrite juste au-dessus me convient bien, et semble bien marcher aussi pour les personnes avec qui je travaille. Néanmoins, ça n'est évidemment pas la solution universelle et vous avez peut-être mieux, si c'est le cas, n'hésitez-pas à venir en discuter dans les commentaires !

Par Kgaut
Adhérent
Kevin Gautreau

Module Drupal 8 - Email Registration pour utiliser l'email comme identifiant

Un besoin classique sur un site avec gestion de membre est d'utiliser l'email comme identifiant. De base drupal utiliser la notion de "pseudo" et demande un email en plus.

Si l'on souhaite n'utiliser que l'email et supprimer complètement la notion de pseudo, on peut utiliser le module "Email Registration".

Ainsi il cache complètement le champs "pseudo" du formulaire de création de compte. Pour des raisons techniques le "username" est quand même généré en fonction de l'adresse email et de l'id de l'utilisateur, mais le module propose un hook spécifique si l'on souhaite mettre en place notre propre règle de génération de pseudo (hook_email_registration_name).

Le pseudo peut quand même être utilisé et défini par l'utilisateur en ajustant les permissions, mais il n'est plus nécessaire pour s'inscrire. De même façon, lors de l'authentification, il est possible d'utiliser à la fois son email ou son pseudo s'il est définis.

Ce module existe en stable pour drupal 7 est est en alpha pour drupal 8 mais fonctionne déjà sans soucis.

Pour installer le module, soit via composer avec :

composer require drupal/email_registration

Soit en le téléchargeant directement sur la page du module sur drupal.org : https://www.drupal.org/project/email_registration

Par Kgaut
Adhérent
Kevin Gautreau

Module Drupal 8 - Admin Toolbar

Une présentation rapide d'un petit module d'administration qui ne révolutionne rien mais qui permet de gagner de précieuses secondes : Admin toolbar.

Il surcharge la barre d'administration et lui ajoute des menus déroulants :

Ce module, couplé à coffee, permet de fluidifier la navigation dans le backoffice drupal, qui peut souvent s’avérer laborieuse avec les arborescences à 57 niveaux...

Et le sous module "admin_toolbar_tools" ajoute des liens vers des fonctions bien pratiques pour l’administrateur d'un site drupal : vidage de cache, updates, cron...

Pour l’installer via composer :

composer require drupal/admin_toolbar

Ou bien en le téléchargeant directement sur la page du module : https://www.drupal.org/project/admin_toolbar

Par Kgaut
Adhérent
Kevin Gautreau

Module Drupal 8 - CKEditor Responsive Plugin

Un peu d'auto-promotion pour commencer cette année 2017, je vais vous parler d'un de mes petits modules : CKEditor Responsive Plugin.

Ce module comme son nom l'indique est un plug-in pour CKEditor, qui permet d'ajouter des zones responsives dans une textarea afin de remplacer les tableaux.

Au lieu d'insérer du markup type table, ce plugin va insérer des divs avec des classes de dimensionnement assez comunes. Par exemple pour deux zones 50% voici ce qui sera inséré :

<div class="ckeditor-col-container clearfix">
  <div class="grid-6 sixcol first-col"><p>lorem ipsum</p></div>
  <div class="grid-6 sixcol last-col"><p>lorem ipsum</p></div>
</div>

À vous ensuite d'ajouter les propriétés et définitions nécessaires dans votre feuille de style front si besoin. Vous pouvez prendre exemple sur la css intégré au module (qui est utilisé dans l'éditeur.

Voci le fonctionnement du module : clic sur le bouton dans l'éditeur :

Sélection du template voulu :

Voici le résultat dans l'éditeur :

Ce module n'a pas du tout vocation à remplacer un module type paragraphs, mais il permet de laisser au client la possibilité de faire des mise en page avancées sans qu'il n'ai besoin de connaître le HTML ou de risquer de casser la mise en page.

Il est disponible pour Drupal 7 et Drupal 8.

Pour l'installation via composer :

composer require drupal/ckeditor_responsive_plugin

Ou bien en le téléchargeant directement sur la page du module : https://www.drupal.org/project/ckeditor_responsive_plugin

Par Kgaut
Adhérent
Kevin Gautreau

Module Drupal 8 - Geolocation Field

Geolocation field est un module Drupal 7 et 8 qui, comme son nom l'indique, permet d'ajouter un champs à ses types de contenus de type "Position GPS".

En backoffice on peut proposer une google map avec champ de recherche et l'administrateur pourra ainsi placer le pointeur précisement :

En front, différentes options d'affichage sont possibles :

Il est aussi possible de ne pas utiliser la google map fournie pour le front mais d'utiliser les données en brut et retourner du json par exemple si on a un ensemble de points à afficher sur une carte.

Pour l'installation, en passant par composer :

composer require drupal/geolocation

Ou bien en le téléchargeant directement sur la page du module : https://www.drupal.org/project/geolocation

Par Kgaut
Adhérent
Kevin Gautreau

Module Drupal 8 - Weight pour trier les contenus

Le module weight permet d'ajouter un attribut de "poids" au contenu qui peut être utilisé dans les listing pour les ordonner comme l'on veut.

La version 8 s'intègre très bien avec views et permet d'avoir un réordonnancent en backoffice sur un listing de contenu. (ce qui pouvait être obtenu sous drupal 7 avec la combinaison weight + draggable views)

Installation du module

"À l'ancienne" en téléchargeant la dernière version sur drupal.org : https://www.drupal.org/project/weight

ou via composer avec composer require drupal/weight.

La version 8.x-3.0 du module est sortie, mais à ce jour elle nécessite 3 patches pour fonctionner correctement :

Si vous utilisez composer pour gérer vos modules, voici la section à ajouter à votre fichier composer.json pour patcher le module automatiquement :

  // Dans la section "extra"      
  "patches": {
            "drupal/weight": {
                "Strict warning: Non-static method ": "https://www.drupal.org/files/issues/weight-non-static-method-2671844-3.patch",
                "Views widget assumes that weight field name is named 'field_weight'": "https://www.drupal.org/files/issues/weight-views-field-name-2687953-0.patch",
                "Weight selector missing": "https://www.drupal.org/files/issues/weight-show-selector-2671840-4.patch"
            }
        }

Plus d'informations sur la gestion des patchs via composer ici.

Configuration du tri

Première chose à faire, ajouter un champ de type "Poids" au type de contenu / entité que l'on souhaite trier :

Vous pourrez ensuite configurer le range qui par défaut est de 20 (donc possibilité de trier de -20 à +20) vous pouvez l'augmenter si vous avez beaucoup de contenu.

Maintenant nous allons utiliser créer une vue d'administration pour pouvoir "drag'n'droper" les contenus pour en changer le poids.

Il faut utiliser le format "Tableau" et bien penser à limiter au type de contenu que l'on souhaite trier :

Ajoutons aux champs, le champs "poids" que nous avons créé, afin d'avoir la petite flèche multidimensionnelle pour réordonner. Attention il faut bien prendre l'élément "Selector" si l'on sélectionne l'autre, alors on aura juste une cellule dans le tableau nous indiquant le poids du contenu, ce n'est pas ce que l'on veut ici :

Deuxième chose à faire, ajouter un critère de tri sur ce champ :

Évidement il faudra trier par poids croissant (un contenu avec un poids de -10 doit être placé avant celui qui a un poids de 10).

Réordonnez les critères de tri pour mettre le tri par poids en premier :

Enregistrez votre vue et rendez-vous sur la page crée, vous pouvez maintenant réordonner vos contenus comme vous le souhaiter, en pensant bien à enregistrer.

Enfin, pensez bien à modifier vos vues front pour prendre en compte le critère de tri maintenant existant (de la même façon que nous l'avons créé dans la vue backoffice.)

Par Kgaut
Adhérent
Kevin Gautreau

Drupal 8 - Définir un fil d'Ariane personnalisé

Drupal 8 améliore grandement la manière de gérer le fil d'Ariane (ou breadcrumbs en Anglais). Dans ce post nous allons voir comment définir un fil d'Ariane pour les contenus d'un type particulier.

Ici nous considérerons que :

  • Le type de contenu s'appellera "Témoignage", son nom machine sera "temoignage"
  • Mon module s'apelle "temoignages_module"
  • J'ai une page de listing définie par un controller dont la route sera temoignages.page_listing.

Ces éléments seront à adapter en fonction de vos besoins.

Définitions du service

S'il n'existe pas, créer le fichier temoignages_module.services.yml et ajouter le contenu suivant :

services:
  temoignages_module.breadcrumb.temoignage:
    class: Drupal\temoignages_module\Breadcrumb\TemoignagesBreadcrumb
    tags:
      - { name: breadcrumb_builder, priority: 100 }

Implémentation du service

Toujours dans le module, créer le fichier "TemoignagesBreadcrumb.php" dans le dossier : src/Breadcrumb du module temoignages_module, et y coller le contenu suivant :

<?php

namespace Drupal\temoignages_module\Breadcrumb;

use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Link;
use Drupal\Core\Breadcrumb\Breadcrumb;

class TemoignagesBreadcrumb implements BreadcrumbBuilderInterface {

  public function applies(RouteMatchInterface $route_match) {
    if ($route_match->getCurrentRouteMatch()->getRouteName() == 'entity.node.canonical') {
      $node = $route_match->getParameter('node');
      if ($node->getType() == 'temoignage') {
        return TRUE;
      }
    }
    return FALSE;
  }

  public function build(RouteMatchInterface $route_match) {
    $breadcrumb = new Breadcrumb();
    $breadcrumb->addCacheContexts(['route']);
    $links = [];
    $links[] = Link::createFromRoute(t('Home'), '<front>');
    $links[] = Link::createFromRoute(t('Tous les témoignages'), 'temoignages_module.page_listing');
    return $breadcrumb->setLinks($links);
  }
}

En détails

La première méthode applies doit retourner TRUE ou FALSE en fonction de si on est dans un cas ou notre breadcrumb personnalisé doit être appliqué.

Je vérifie pour commencer que je suis en train de visualiser une node :

$route_match->getCurrentRouteMatch()->getRouteName() == 'entity.node.canonical'

Si c'est le cas, je récupère cette node et si elle est bien du type "temoignage" je retourne TRUE

      $node = $route_match->getParameter('node');
      if ($node->getType() == 'temoignage') {
        return TRUE;
      }

Sinon je retourne FALSE.

Enfin, la méthode build, comme son nom l'indique construit le fil d'Ariane, qui consiste en fait en un tableau de liens.

Je commence par mettre un tag de cache à mon fil d'Ariane en lui disant qu'il dépend de la route.

$breadcrumb->addCacheContexts(['route']);

Je construis ensuite mon tableau de liens, qui en sera constitué de deux, le premier vers la page d'accueil et le second vers ma page de listing :

    $links = [];
    $links[] = Link::createFromRoute(t('Home'), '<front>');
    $links[] = Link::createFromRoute(t('Tous les témoignages'), 'temoignages_module.page_listing');
    return $breadcrumb->setLinks($links);

Et... c'est tout, plus qu'à reconstruire le cache et vous pourrez vérifier que votre fil d'Ariane fonctionne bien

Vous trouverez plus d'exemple dans les sources de mon module de pronostics : https://github.com/mespronos/mespronos/tree/8.x-1.x/src/Breadcrumb, pour la définitions des services liés, c'est ici : https://github.com/mespronos/mespronos/blob/8.x-1.x/mespronos.services.yml.

Par Kgaut
Adhérent
Kevin Gautreau

Créer un thème personnalisé pour Drupal 8

Quand l'on travaille avec Drupal, on a rapidement besoin d'avoir un thème personnalisé ne serai-ce que pour y stocker ses templates custom et ses feuilles de styles.

Personnellement, j'ai l'habitude de faire un thème "front" et un thème "back" car j'ai souvent besoin de personnaliser le rendu de l'interface d'administration pour qu'elle convienne au mieux à mes clients.

Drupal 8 comme Drupal 7 utilise la notion de thème "parent", c'est à dire que si mon thème custom a comme parent (comme base_theme) le thème bartik, alors il reprendra toutes ses propriétés (templates, css, js...), et l'on pourra surcharger ce qui nous intéresse.

Il existe des thèmes "starter kit", on peut citer Zen (https://www.drupal.org/project/zen) par exemple qui est un des plus connu. Il s'agit en fait d'un thème parent qui propose un thème starterkit tout configuré que l'on peut ensuite personnaliser en fonction de nos besoins.

On peut aussi évidement faire ses propres thèmes custom. À noter que si vous ne prenez pas comme thème parent Bartik ou Zen, il faudra quand même prendre le thème "classy" comme parent.

Comment créer son theme ?

On va partir sur le nom de theme suivant "Mon Site Front" : on va commencer par créer le dossier mon_site_front dans le dossier /themes/custom (on pourrait le mettre directement dans /themes, mais comme pour les modules c'est une bonne pratique de différentier les élément personnalisé (custom) des éléments téléchargés (qui seront eux dans un sous-dossier "contrib").

La définition d'un thème passe, comme pour un module par un fichier mon_site_front.info.yml, en voici son contenu :

name: Mon Site Front
type: theme
description: Mon theme de demonstration
package: MonSite
core: 8.x
base theme: bartik

On doit aussi créer un fichier mon_site_front.theme, vide, qui pourra contenir plus tard nos fonction de preprocess et différents hooks.

J'ai ici pris comme thème de base "Bartik".

Une fois cela fait on passe dans le menu d'administration "Apparence" pour activer notre thème et le définir comme thème par défaut :

Pour faire un thème d'administration custom cette fois on va prendre comme base thème "Seven" :

name: Mon Site Back
type: theme
description: Mon theme de demonstration Backoffice
package: MonSite
core: 8.x
base theme: seven

Cette fois dans le menu Apparence, on devra installer le thème puis le définir comme thème d'administration :

Un thème d’administration que je trouve très sympa et qui me sert de thème parent est Adminimal (https://www.drupal.org/project/adminimal_theme).

Drupal Console permet aussi de générer des thème, via la commande drupal generate:theme, et il demande de quel thème parent notre thème custom doit hériter.

Vous pouvez retrouver les maigres sources de ce post sur github :

Pages