Comment concevoir un thème personnalisé et léger sous Liferay ?

Cet article survient suite à la réalisation, par notre équipe Plateformes d’intégration et Intelligence Artificielle, d’un site informationnel pour l’un de nos clients, une grande entreprise canadienne de télécommunication et de médias, en utilisant Liferay 7 (la dernière version du portail Liferay).

Alexis, développeur front-end, vous partage son expérience sur ce projet afin de vous expliquer comment il est possible de créer un thème léger et rapide à charger avec Liferay.

En 2015, nous avons découvert le site de Bugaboo, réalisé sous Liferay 6.2. Une chose nous a surpris : l’absence des librairies front-end du thème Liferay.
Depuis l’idée a fait son chemin et un mandat similaire est arrivé à Savoir-faire Linux : Réaliser le site web informationnel d’une grande entreprise canadienne de télécommunications et média, avec Liferay.

Avant toute chose, nous devons nous accorder sur un point, la vitesse de chargement d’un site web est un élément déterminant de la rétention des utilisateurs : après 3 secondes [de chargement], jusqu’à 40% d’entre eux vont quitter le site » (Gomez Inc., 2010, en anglais). Pour plus d’informations, voir Lara Callender Hogan (en anglais).

Liferay n’est pas en soi une solution technique qui peut répondre à tous les besoins. Il est tout à fait possible de faire des sites web avec, mais malheureusement la méthode « recommandée », c’est-à-dire étendre le thème Liferay par défaut, aurait été un échec technique pour le site informationnel de notre client à cause d’un allongement considérable du temps de chargement. C’est alors que, basé sur nos expériences avec nos précédents projets, il a été choisi d’adapter en profondeur le thème Liferay afin de pouvoir répondre aux besoins précis de notre client. Dans ce qui va suivre, nous partageons avec vous notre retour et nos résultats.

Le portail Liferay, un outil puissant qui a besoin d’accompagnement

Liferay est un gestionnaire de contenu (CMS) et un portail écrit en Java fournissant de nombreuses fonctionnalités spécifiques aux développeurs comme une gestion avancée des utilisateurs, des droits d’accès, des documents, etc. Dans notre cas, en tant que société de services, nous avons la possibilité de l’adapter autant que nous le voulons aux besoins de nos clients. Par contre, étant pensé pour être un portail modulaire, il y a quelques défauts liés aux thèmes et aux performances à considérer avant de concevoir un site web avec. En voici la liste :

  • Le thème de base de Liferay charge par défaut toutes les librairies front-end ainsi que l’ensemble des styles que Liferay et ses portlets ont besoin. Cependant, une grande partie du code et des dépendances s’avère inutile pour un visiteur non-connecté (soit la majorité de nos utilisateurs dans le cadre de ce projet). De nombreux problèmes de performance apparaissent donc à cause de cette surcharge inutile de code.
  • Liferay impose la présence de son Design Language, Lexicon (ou plutôt, sa seule implémentation: Clay). Cela provoque également plusieurs problèmes :
    • Manque de correspondance entre le Design Language et les besoins visuels requis pour ce projet, limitant grandement les designs réalisables et ou en compliquant grandement la personnalisation,
    • Conflits au sein des règles CSS, augmentant le temps de développement,
    • Les mises à jour et corrections du thème de base de Liferay peuvent briser les styles développés provoquant alors une augmentation significative du temps de maintenance,
    • L’administration des contenus étant à même les pages, les styles développés peuvent briser le comportement des styles d’administration participant aussi à l’augmentation du temps de développement et de maintenance.

Comment alors réaliser un thème léger sous Liferay ?

La solution pour bénéficier de la puissance de Liferay tout en gardant le contrôle de la vitesse de chargement des pages est de créer un thème léger ne contenant aucune des librairies Liferay front-end comme senna.js, AlloyUi, Lexicon/Clay, etc.

Cependant, dans Liferay 7, ce n’est pas aussi facile que cela n’y paraît, notamment dû à l’impossibilité d’avoir une vue d’administration (indépendante) pour le site. Pour échapper à cela, nos fantastiques développeurs back-end de l’équipe ont développé un module, le « Theme Switcher », qui permet de forcer l’affichage d’un thème en fonction du rôle d’un utilisateur. Dans notre cas, cela nous permet d’afficher un thème classique Liferay uniquement aux administrateurs du site. Voici le lien vers le GitHub du « Theme Switcher » : savoirfairelinux/liferay-theme-switcher

5 étapes pour la mise en place d’un thème léger sous Liferay

Un exemple de thème léger pour Liferay disponible sur notre GitHub : savoirfairelinux/lightweight-liferay-theme. Ce thème utilise une architecture Maven, mais libre à vous de le modifier.

Étape 1 – Nettoyage du thème

Retirez les inclusions du fichier portal_normal.ftl, principalement: <@liferay_util["include"] page=top_head_include />. Mais aussi <@liferay_util["include"] page=body_top_include /> et j’en passe…

Étape 2 – Réparation du thème

Malheureusement la variable top_head_include ne contient pas que les librairies CSS et javascript de Liferay, mais aussi beaucoup d’autres choses. Il est donc nécessaire de ré-inclure les balises du head qui nous intéressent.

Par exemple,

  • Les meta-tags que l’utilisateur peut définir dans chaque pages (keywords, description, etc…):

<@liferay_theme["meta-tags"] />

  • La définition des urls canoniques:
<#if !is_signed_in && layout.isPublicLayout() > 
  <#assign completeURL = portalUtil.getCurrentCompleteURL(request) > 
  <#assign canonicalURL = portalUtil.getCanonicalURL(completeURL, themeDisplay, layout) > 

  <link href="${htmlUtil.escapeAttribute(canonicalURL)}" rel="canonical" /> 

  <#assign availableLocales = languageUtil.getAvailableLocales(themeDisplay.getSiteGroupId()) > 

  <#if availableLocales?size gt 1 > 
    <#list availableLocales as availableLocale> 
      <#if availableLocale == localeUtil.getDefault() > 
        <link href="${htmlUtil.escapeAttribute(canonicalURL) }" hreflang="x-default" rel="alternate" /> 
      </#if> 
      <link href="${htmlUtil.escapeAttribute(portalUtil.getAlternateURL(canonicalURL, themeDisplay, availableLocale, layout)) }" hreflang="${localeUtil.toW3cLanguageId(availableLocale)}" rel="alternate" /> 
  </#list> 
  </#if> 
</#if> 

Pour savoir quoi aller chercher, et comment, basez-vous sur la jsp qui est utilisée pour générer la variable top_head_include : https://github.com/liferay/liferay-portal/blob/7.0.x/portal-web/docroot/html/common/themes/top_head.jsp

Étape 3 – Organisation

Un des problèmes, et vous l’aurez peut-être deviné : les tâches liferay-theme-tasks fonctionneront difficilement.

Cependant, bonne nouvelle : Vous pouvez organiser votre futur thème de la manière dont vous le souhaiter et selon les standards de développement de votre équipe (sass, less, es6, etc.). Dans le cas de notre client, nous avons utilisé et agrémenté le gulpfile habituel pour nos projets web. Le tout chapeauté par Maven et des modules front-end.

Étape 4 – Automatisation du déploiement du thème léger

Cette partie a été la plus longue, je vous avoue, mais elle vous est maintenant disponible clé en main, dans la tâche gulp ‘deployLocal:syncTheme’ du thème d’exemple !

Il s’agit de la mise en place d’un déploiement « live » des fichiers du thème.

D’un point de vue global, le but est de ne pas avoir à compiler ni à attendre le déploiement du thème pour chaque modification de CSS ou de javascript (fonctionnalité normalement présente dans les liferay-theme-tasks). Dans le contexte OSGI de Liferay 7, il n’est pas facile d’accéder directement aux fichiers du thème (CSS, javascript, templates, etc.).

L’idée est alors de modifier la manière dont le thème est déployé dans OSGI, en le faisant pointer non pas vers le fichier .war du thème compilé (en mode webbundle, celui par défaut), mais plutôt vers le dossier extrait de ce .war (en mode webbundledir).

Étape 5 – Profitez

Vous avez maintenant un thème Liferay, qui contient uniquement ce que vous désirez pour votre projet, et qui est déployable comme n’importe quel autre thème Liferay.

Tests et résultats

Dans le but d’évaluer les avantages en terme de performances de la mise en place d’un thème léger, vous pouvez lancer un test, sur une même page avec le même contenu, avec 3 cas possibles de choix de thème. De notre côté, nous avons lancé ces tests sur un serveur Liferay configuré pour la production avec 8 CPU, 28Go de RAM et un disque SSD. Ci-dessous les 3 cas pour lesquels nous avons lancé le test.

Les 3 cas possibles de choix de thème

  1. Utilisation d’un thème vanille de Liferay, un thème classique
  2. Utilisation du thème léger que nous avons développé pour le site informationnel de notre client
  3. Utilisation du thème classique avec l’ajout des styles CSS et javascript du thème que nous avons développé pour notre client

Pour réaliser les mesures, nous avons utilisé une instance locale de speedtest.io (6.0.3) avec Chrome 62. Pour chacun des cas, 10 appels ont été réalisés. Ci-dessous un résumé des résultats :

Résultat du cas 1 : Le thème classique de Liferay

  • 52 requests
  • 1064.77 kb
  • DOMContentLoaded : 1.24s (±31.95ms)
  • Chargement : 2.06s (±52.89ms)
  • speedIndex : 1349 (±34.99)

Nous considérons ces résultats comme corrects car ils se trouvent dans la moyenne que l’on peut trouver actuellement sur le web. Néanmoins, il y a tout de même un grand nombre de requêtes, dépassant le seuil des 40, habituellement recommandé.

Résultat du cas 2 : Le thème léger

  • 37 requests
  • 800.38 kb
  • DOMContentLoaded : 588ms (±42.96ms)
  • Chargement : 1.17s (±55.17ms)
  • speedIndex : 863 (±39.57)

Moins de requêtes, une page plus légère, ce thème personnalisé est plus performant que le thème classique de Liferay !

Résultat du cas 3 : Le thème classique avec les CSS et javascript du thème léger de notre client

  • 56 requests
  • 1214.59 kb,
  • DOMContentLoaded: 2.22s (±457.34ms),
  • Load: 2.97s (±458.11ms)
  • speedIndex: 2475 (±456.71)

Ce résultat est très important, car il s’agit d’un cas qui pourrait être réel. Un thème Liferay, avec un visuel complètement personnalisé mais plus que deux fois plus lent à charger…

Conclusion

Dans l’ensemble, cette expérience fut instructive et nous a fourni, ainsi qu’à notre client, des résultats à la hauteur de nos espérances.

Néanmoins, la mise en place d’un thème léger ne peut pas s’appliquer pour n’importe quel type de site web et devenir une solution unique pour vos projets web. De plus, avec cette méthode, certaines fonctionnalités (front-end) de plusieurs Portlet Liferay se trouvent inaccessibles.

 

Et si votre avenir était au Québec ?

Le 25 et 26 novembre prochain, Savoir-faire Linux sera présente aux Journées Québec à Paris pour dénicher les perles rares et les pousser à rejoindre l’aventure québécoise !

Et si votre avenir était au Québec

Les Journées Québec, des rencontres gagnantes

Organisées à Paris par le gouvernement du Québec et ses partenaires, les Journées Québec permettent chaque année des professionnels de rencontrer et d’échanger avec de nombreux employeurs québécois à la recherche de nouveaux talents. En cette période de plein emploi dans la belle province, plusieurs dizaines d’entreprises québecoises recherchent de nouveaux employé(e)s parmi de nombreux secteurs comme la santé, l’informatique, le jeu vidéo ou l’informatique.

Tentez l’aventure avec Savoir-faire Linux

Aujourd’hui, c’est notre croissance que nous mettons à l’honneur ! De nouveaux talents sont nécessaires pour renforcer nos équipes et répondre aux besoins grandissants de nos clients. Fervent défenseur du logiciel libre et expert des langages et outils open source depuis près de 20 ans, Savoir-faire Linux est aujourd’hui spécialisée en technologies de l’information, objets connectés et ingénierie logicielle au Québec. Forte d’une équipe multidisciplinaire de plus de 120 experts de 24 nationalités différentes dans 5 villes et sur 2 continents, Savoir-faire Linux fournit des services à plus de 500 organisations à travers le monde, dont l’Université Paris-Dauphine, Radio-Canada, General Electric, Alstom, Défense Canada, etc.

Aventurier et passionné des TICs !
Vous cherchez un tremplin pour votre carrière ? Toucher à tout dans votre domaine ? Découvrir de nouveaux horizons ?  ….. Les 3 ? Rejoignez-nous !

Savoir-faire Linux est à la fois un expert en innovation ouverte et un acteur important dans le monde du libre. La compagnie a bâti sa notoriété et son expertise par le biais de nombreuses contributions à des projets libres et éducatifs et par la mise en place d’alliances stratégiques avec de nombreux acteurs de l’open source tels qu’Odoo, Red Hat, Liferay, Microsoft et la Linux Foundation.

L’entreprise place l’expertise de ses équipes au cœur même de son offre de services et encourage une culture de la collaboration et du partage de connaissances qui permet à ses employés d’apprendre les uns des autres et de rayonner dans leurs communautés… Le tout dans un environnement de travail stimulant offrant de nombreux avantages.

L’entreprise donne un large éventail de possibilités d’emploi à différents niveaux de l’organisation, à Montréal et à Québec. Voici un échantillon :

Vous pouvez aussi retrouver toutes nos offres sur site carrières à l’adresse suivante : carrieres.savoirfairelinux.com

Comment participer aux Journées Québec ?

Pour participer aux Journées Québec et rencontrer les équipes de Savoir-faire Linux, rien de plus simple, il suffit de vous inscrire sur le site web de l’événement à l’adresse suivante : http://journeesquebec.gouv.qc.ca/candidats.html puis d’y créer votre profil. Par la suite, vous pourrez consulter les offres d’emplois proposées et soumettre votre candidature.

Si vous êtes retenu, une invitation vous sera envoyée et vous permettra d’accéder à l’événement et de passer une entrevue avec Savoir-faire Linux sur place.

Pour plus de détails, rendez-vous sur le site des Journées Québec : http://journeesquebec.gouv.qc.ca/candidats.html#haut

Définir des rôles avec Ansible pour Nexus Repository Manager v3.x


Savez-vous comment automatiser l’installation ainsi que la configuration de Nexus Repository Manager version 3.x avec Ansible ? Pas encore ? On vous donne un coup de pouce ici !

Pour rappel, Ansible est un outil de déploiement qui permet à des playbooks d’automatiser les déploiements d’applications et d’infrastructure. L’avantage clé d’Ansible est sa flexibilité puisqu’il permet de modifier les applications avec souplesse et de les appréhender en tant que service. Toutefois, Ansible possède également quelques faiblesses. Avec une conception volontairement simple, il fonctionne uniquement comme une application de paramètres sans tenir compte de la disponibilité de l’information et des problèmes de sécurité, qui doivent alors être gérés par d’autres systèmes. C’est pourquoi un grand nombre de développeurs préfèrent utiliser Ansible en complémentarité avec un système d’agent actif comme Puppet ou avec un outil de gestion centralisé comme Ansible Tower.

Automatisation avec Ansible

Ansible se concentre sur la configuration au niveau de l’application, les scripts d’approvisionnement pouvant être exécutés sur tout outil de support d’infrastructure (PaaS, conteneurs, bare-metal, Vagrant, etc.). Ansible ne nécessite qu’une connexion SSH et un compte sudo sur le système distant.

Les scripts d’approvisionnement Ansible sont écrits dans un style déclaratif à l’aide de fichiers YAML regroupés en rôles. Les instructions atomiques des rôles sont déclarées à l’aide d’un certain nombre de modules de base fournis par Ansible. Consultez la documentation Ansible pour une introduction en profondeur.

Réapprovisionnement et mise à jour de la configuration

L’un des modèles DevOps pour gérer les mises à jour de configuration consiste à fournir un nouvel environnement à partir de zéro et à éliminer complètement l’ancien (pensez à des images de conteneur). Cela implique une gestion fiable de votre cycle de vie des données. Dans notre cas particulier du Nexus Repository Manager, cela consiste à plusieurs gigaoctets d’artefacts téléchargés / mandatés, de certains journaux d’audit et d’objets OrientDB contenant la configuration. Par conséquent, en fonction des contraintes de l’environnement, il peut être logique de mettre à jour la configuration d’une instance Nexus préalablement approvisionnée. La nature déclarative des instructions de base d’Ansible est répond bien à cette contrainte, mais toute logique personnalisée écrite dans un rôle devrait être idempotente et prendre en compte la logique « créer ou éventuellement mettre à jour ».

Il faut également noter que certaines parties de la configuration Nexus ne peuvent pas être mises à jour. Voici quelques exemples :

  • Les paramètres relatifs aux BlobStores
  • Le mot de passe administrateur si vous le perdez (mise à jour: peut-être par cette voie)

Comment bien intégrer l’API Groovy de Nexus avec Ansible

Les étapes de base de l’installation sont très simples et peuvent toutes être écrites en utilisant des modules simples de base Ansible :

  • Télécharger et décompresser l’archive
  • Créer un utilisateur / groupe système
  • Créer un service systemd

(Ces étapes sont dans les tâches / nexus_install.yml)

Et puis vient la surprise : la configuration Nexus n’est pas disponible dans un fichier texte simple qui peut être édité à l’aide d’instructions simples Ansible. Elle est stockée dans une base de données OrientDB intégrée qui ne doit pas être modifiée directement. La manière documentée de configurer Nexus est soit par son interface utilisateur Web, soit par son API d’intégration.

La façon dont l’API d’intégration fonctionne est la suivante :

  1. Écrire un script Groovy qui gère vos changements de configuration;
  2. Charger le script sur Nexus à l’aide d’un requête HTTP PUT, créant une ressource REST pour ce script;
  3. Appeler le script via sa ressource HTTP GET / POST

Module URI à la rescousse !

Le module uri d’Ansible envoie des requêtes HTTP, fournissant une automatisation de ces étapes.

La première étape consiste à téléverser le script Groovy sur Nexus. Notez que le script peut déjà être présent. Par conséquent, lors des ré-exécutions du playbook, nous essayons de le supprimer avant d’entreprendre d’autres actions, par le biais de tasks/declare_script_each.yml, comme suit :

  ---
  - name: Removing (potential) previously declared Groovy script {{ item }}
    uri:
      url: "http://localhost:8081/service/siesta/rest/v1/script/{{ item }}"
      user: 'admin'
      password: "{{ current_nexus_admin_password }}"
      method: DELETE
      force_basic_auth: yes
      status_code: 204,404

  - name: Declaring Groovy script {{ item }}
    uri:
      url: "http://localhost:8081/service/siesta/rest/v1/script"
      user: 'admin'
      password: "{{ current_nexus_admin_password }}"
      body_format: json
      method: POST
      force_basic_auth: yes
      status_code: 204
      body:
        name: "{{ item }}"
        type: 'groovy'
        content: "{{ lookup('template', 'groovy/' + item + '.groovy') }}"

Les requêtes HTTP sont exécutées à partir de l’hôte cible, c’est pourquoi localhost est utilisé ici. force_basic_auth: yes s’assure que le client HTTP n’attende pas une réponse 401 avant de fournir des informations d’identification. Autrement, Nexus répondra immédiatement avec une réponse 403 lorsque aucune authentification n’est passée. status_code est l’état HTTP attendu de la réponse fournie par Nexus. Étant donné que le script Groovy peut ne pas nécessairement exister à ce moment-là, nous devons également accepter le code d’état 404.

L’étape suivante consiste à appeler le script Groovy créé via l’appel HTTP précédent. La plupart des scripts prendront certains paramètres d’entrée (par exemple, créer un utilisateur <x>), et c’est là que Ansible et Groovy aideront. Tous deux issus de l’âge des principes REST, ils peuvent parler et comprendre le format JSON de manière relativement transparente.

Du côté du script Groovy :

import groovy.json.JsonSlurper
parsed_args = new JsonSlurper().parseText(args)
security.setAnonymousAccess(Boolean.valueOf(parsed_args.anonymous_access))

Et pour appeler ce script à partir des arguments passés via Ansible  :

  - include: call_script.yml
    vars:
      script_name: setup_anonymous_access
      args: # this structure will be parsed by the groovy JsonSlurper above
        anonymous_access: true

Avec call_script.yml :

  ---
  - name: Calling Groovy script {{ script_name }}
    uri:
      url: "http://localhost:8081/service/siesta/rest/v1/script/{{ script_name }}/run"
      user: 'admin'
      password: "{{ current_nexus_admin_password }}"
      headers:
        Content-Type: "text/plain"
      method: POST
      status_code: 200,204
      force_basic_auth: yes
      body: "{{ args | to_json }}"

Cela nous permet de passer de manière transparente les paramètres structurés de Ansible aux scripts Groovy, en gardant la structure, les tableaux et les types de base des objets.

Scripts Groovy pour Nexus : conseils et astuces pour le développement

Voici quelques conseils qui peuvent aider les développeurs travaillant sur les scripts Groovy.

Configurer le Classpath dans votre IDE

Comme décrit dans la documentation Nexus, avoir les scripts Nexus dans le classpath de votre IDE peut vraiment vous aider à travailler. Si vous automatisez la configuration de Nexus autant que possible, vous allez inévitablement tomber sur certaines API internes sans documentation. De plus, certaines parties de l’API n’ont aucune source disponible (par exemple, l’API LDAP). Dans de tels cas, un décompilateur peut être utile.

Puisque notre rôle sur Github utilise Maven avec toutes les dépendances nécessaires, vous pouvez simplement l’ouvrir avec IntelliJ et éditer les scripts situés dans « files/groovy ».

Points d’entrée de l’API de Scripting

Comme documenté, il y a quatre points d’entrée implicites pour accéder aux entrailles de Nexus à partir de votre script:

  • core
  • repository
  • blobStore
  • security

Ceux-ci sont utiles pour des opérations simples, mais pour tout ce qui est plus compliqué, vous devrez résoudre plus en profondeur les services par:

  • Indirection à partir des points d’entrée principaux: blobStore.getBlobStoreManager ()
  • Résolution d’un @Singleton interne du contexte du conteneur : container.lookup (DefaultCapabilityRegistry.class.getName())

Prenez des exemples du code source de Nexus

Certaines parties de Nexus (7,4%, selon Github) sont également écrites à l’aide de Groovy, contenant de nombreux bons exemples de code: CoreApiImpl.groovy.

Créer des requêtes HTTP à partir de l’interface Web de configuration (requêtes AJAX) fournit également des indications sur les structures de données attendues, les paramètres ou les valeurs de certains paramètres.

Enfin, mettre en place un débogueur distant depuis votre IDE vers une instance Nexus peut aider, car il existe de nombreux endroits où une structure de données très générique est utilisée (comme Map <String, Object>) et seule l’inspection au moment de l’exécution peut rapidement indiquer les types réels nécessaires.

Exemples détaillés

Voici quelques exemples commentés de scripts Groovy tirés du rôle Ansible

Configuration d’une Capability

Les Capabilities de Nexus  peuvent être configurées à l’aide d’une interface utilisateur unifiée. Dans notre cas, cela couvre:

  1. L’accès anonyme
  2. L’URL publique de base
  3. La marque (en-tête et pied de page HTML personnalisés).

Instructions :


    import groovy.json.JsonSlurper
    import org.sonatype.nexus.capability.CapabilityReference
    import org.sonatype.nexus.capability.CapabilityType
    import org.sonatype.nexus.internal.capability.DefaultCapabilityReference
    import org.sonatype.nexus.internal.capability.DefaultCapabilityRegistry

    // unmarshall the parameters as JSON
    parsed_args = new JsonSlurper().parseText(args)

    // Type casts, JSON serialization insists on keeping those as 'boolean'
    parsed_args.capability_properties['headerEnabled'] = parsed_args.capability_properties['headerEnabled'].toString()
    parsed_args.capability_properties['footerEnabled'] = parsed_args.capability_properties['footerEnabled'].toString()

    // Resolve a @Singleton from the container context
    def capabilityRegistry = container.lookup(DefaultCapabilityRegistry.class.getName())
    def capabilityType = CapabilityType.capabilityType(parsed_args.capability_typeId)

    // Try to find an existing capability to update it
    DefaultCapabilityReference existing = capabilityRegistry.all.find {
        CapabilityReference capabilityReference -&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
            capabilityReference.context().descriptor().type() == capabilityType
    }

    // update
    if (existing) {
        log.info(parsed_args.typeId + ' capability updated to: {}',
                capabilityRegistry.update(existing.id(), existing.active, existing.notes(), parsed_args.capability_properties).toString()
        )
    } else { // or create
        log.info(parsed_args.typeId + ' capability created as: {}', capabilityRegistry.
                add(capabilityType, true, 'configured through api', parsed_args.capability_properties).toString()
        )
    }

Configuration d’un mandataire de dépôt Maven

    import groovy.json.JsonSlurper
    import org.sonatype.nexus.repository.config.Configuration

    // unmarshall the parameters as JSON
    parsed_args = new JsonSlurper().parseText(args)

    // The two following data structures are good examples of things to look for via runtime inspection
    // either in client Ajax calls or breakpoints in a live server

    authentication = parsed_args.remote_username == null ? null : [
            type: 'username',
            username: parsed_args.remote_username,
            password: parsed_args.remote_password
    ]

    configuration = new Configuration(
            repositoryName: parsed_args.name,
            recipeName: 'maven2-proxy',
            online: true,
            attributes: [
                    maven  : [
                            versionPolicy: parsed_args.version_policy.toUpperCase(),
                            layoutPolicy : parsed_args.layout_policy.toUpperCase()
                    ],
                    proxy  : [
                            remoteUrl: parsed_args.remote_url,
                            contentMaxAge: 1440.0,
                            metadataMaxAge: 1440.0
                    ],
                    httpclient: [
                            blocked: false,
                            autoBlock: true,
                            authentication: authentication,
                            connection: [
                                    useTrustStore: false
                            ]
                    ],
                    storage: [
                            blobStoreName: parsed_args.blob_store,
                            strictContentTypeValidation: Boolean.valueOf(parsed_args.strict_content_validation)
                    ],
                    negativeCache: [
                            enabled: true,
                            timeToLive: 1440.0
                    ]
            ]
    )

    // try to find an existing repository to update
    def existingRepository = repository.getRepositoryManager().get(parsed_args.name)

    if (existingRepository != null) {
        // repositories need to be stopped before any configuration change
        existingRepository.stop()

        // the blobStore part cannot be updated, so we keep the existing value
        configuration.attributes['storage']['blobStoreName'] = existingRepository.configuration.attributes['storage']['blobStoreName']
        existingRepository.update(configuration)

        // re-enable the repo
        existingRepository.start()
    } else {
        repository.getRepositoryManager().create(configuration)
    }

Créer un rôle

    import groovy.json.JsonSlurper
    import org.sonatype.nexus.security.user.UserManager
    import org.sonatype.nexus.security.role.NoSuchRoleException

    // unmarshall the parameters as JSON
    parsed_args = new JsonSlurper().parseText(args)

    // some indirect way to retrieve the service we need
    authManager = security.getSecuritySystem().getAuthorizationManager(UserManager.DEFAULT_SOURCE)

    // Try to locate an existing role to update
    def existingRole = null

    try {
        existingRole = authManager.getRole(parsed_args.id)
    } catch (NoSuchRoleException ignored) {
        // could not find role
    }

    // Collection-type cast in groovy, here from String[] to Set&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
    privileges = (parsed_args.privileges == null ? new HashSet() : parsed_args.privileges.toSet())
    roles = (parsed_args.roles == null ? new HashSet() : parsed_args.roles.toSet())

    if (existingRole != null) {
        existingRole.setName(parsed_args.name)
        existingRole.setDescription(parsed_args.description)
        existingRole.setPrivileges(privileges)
        existingRole.setRoles(roles)
        authManager.updateRole(existingRole)
    } else {
        // Another collection-type cast, from Set&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; to List&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;String&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
        security.addRole(parsed_args.id, parsed_args.name, parsed_args.description, privileges.toList(), roles.toList())
    }

Le rôle est disponible sur Ansible Galaxy et Github. Il comporte:

  • Le téléchargement et l’extraction de Nexus
  • La création de l’unité de service systemd
  • (Facultatif) La configuration d’un reverse-proxy Apache supportant SSL
  • La configuration du mot de passe administrateur
  • La configuration LDAP
  • La configuration des privilèges et des rôles
  • La création des utilisateurs locaux
  • La configuration des Blobstores
  • La configuration de tous les types de dépôts
  • La spécification de l’URL de base
  • La mise en place de la marque (en-tête HTML et pied de page personnalisés)
  • La mise en place des tâches automatiques

 

Maîtriser la génération de thumbnails sur Liferay 7CE et DXP

par

Maîtriser la génération de thumbnails avec Liferay DXPLe Thumbnail Generator vise à améliorer la génération de thumbnails, proposée par Liferay.

Ce plugin a été créé au cours d’un projet nécessitant la présence d’un très grand nombre de thumbnails de dimensions précises, afin d’optimiser au maximum les temps de chargement des pages Web. En effet, Liferay offre seulement  deux thumbnails lors du chargement d’une image sur la plateforme. Le Thumbnail Generator permet donc d’avoir un contrôle total sur le nombre de thumbnails créés, mais aussi sur la façon dont ils sont créés.

Après avoir brièvement décrit les principaux composants de ce plugin, j’expliquerai comment le configurer pour maîtriser complètement la génération de thumbnails avec Liferay.

I. Description des composants du plugin

Les Listeners

Le Thumbnail Generator utilise deux Model Listeners permettant de repérer les événements de création, de modification et de suppression de documents dans l’application Liferay. Un document peut correspondre à n’importe quel type de fichier (texte, image, vidéo, pdf…), nous verrons plus tard comment configurer le plugin pour qu’il traite uniquement les documents nous intéressant.

Le premier Listener repère la création et la modification d’un document, puis crée ou met à jour les thumbnails du document.
Le deuxième repère la suppression d’un document et supprime dans la foulée les thumbnails qui lui sont associés.

Le Servlet Filter

Le Servlet Filter intercepte toutes les requêtes faisant appel à un document de l’application et effectue une série de vérification avant de renvoyer un thumbnail en réponse.

Il va tout d’abord analyser les paramètres de la requête afin de savoir si un thumbnail est demandé. Le filtre va ensuite vérifier que le thumbnail existe bel et bien, pour finalement le retourner à l’auteur de la requête. Si l’une de ces vérifications échoue, la requête sera ignorée par le filtre et elle suivra son cours normal, retournant le document original demandé.

Le ThumbnailService

Enfin, le ThumbnailService s’occupe de la création/suppression des thumbnails et de leur organisation dans le système de stockage de Liferay, en utilisant les paramètres de la configuration du plugin.

II. Utilisation du plugin

L’utilisation du Thumbnail Generator se résume à la configuration du plugin et à la récupération des thumbnails.

Configuration

La page de configuration du Thumbnail Generator (Menu => Control Panel => Configuration => System Settings => Thumbnail Configuration) permet de définir deux options :

  • Le format des fichiers qui seront traités par le plugin : par exemple, pour restreindre la création de thumbnails pour les fichiers de type JPG et PNG, il suffit d’ajouter ces formats dans la configuration et tous les autres fichiers ne seront pas pris en compte par le plugin.
  • Les lignes de commande permettant de créer les thumbnails : pour définir un thumbnail à générer, il faut ajouter une ligne dans la configuration avec la syntaxe suivante : ‘nom:commande. Le nom permettra par la suite d’accéder à ce thumbnail, la commande correspond à la ligne de commande qui générera le thumbnail (voir la documentation ImageMagick pour explorer toutes les options possibles).
    Par exemple : ‘img_480:convert ${inputFile} -resize 480×270 ${outputFile}‘ permet de générer un thumbnail de dimension 480×270 et qui sera récupérable grâce à son nom « img_480 ».

 page de configuration du Thumbnail Generator

Dans la capture d’écran ci-dessus, trois thumbnails différents seront créés pour chaque fichiers .jpg et .png chargés dans l’application Liferay.

La configuration du plugin permet de contrôler le nombre de thumbnail à générer, mais aussi la façon dont ils sont créés. La commande convert provient de la très puissante librairie de retouche d’image ImageMagick.
À la place de cette commande, on aurait pu utiliser toute autre commande exécutable sur la machine hebergeant l’application.

Récupération des thumbnails

Une fois  que le module est déployé et configuré, il est prêt à l’utilisation. Des thumbnails seront générés automatiquement lors de l’upload de documents dans l’application.
Pour récupérer un thumbnail d’un document, il suffit d’ajouter le paramètre « thumb={nomDuThumbnail} » dans l’url faisant appel à ce document.

Exemple
L’URL  d’un document (test.jpg) sur une instance Liferay en local ressemble à ceci : http://localhost:8080/documents/20147/0/test.jpg/0d72d709-3e48-24b3-3fe6-e39a3c528725?version=1.0&t=1494431839298&imagePreview=1
L’URL d’un thumbnail associé à ce document, dont le nom est img_480, pourra être appelé de la façon suivante : http://localhost:8080/documents/20147/0/test.jpg/0d72d709-3e48-24b3-3fe6-e39a3c528725?version=1.0&t=1494431839298&imagePreview=1&thumb=img_480

III. Administration

Afin de donner plus de contrôle à l’utilisateur dans la gestion de ce module, une page d’administration (votre site > Configuration >  Thumbnails administration) a été créée permettant d’effectuer quelques actions sur les thumbnails :

  • Re-générer tous les thumbnails.
  • Effacer tous les thumbnails.
  • Effacer les thumbnails orphelins (qui ne sont plus liés à aucun documents mais qui sont toujours présents à cause d’un changement de la configuration).

Administration du Thumbnail Generator

En conclusion, ce bref tutoriel vous a présenté le plugin Liferay appelé Thumbnail Generator et décrit comment utiliser, configurer, récupérer les vignettes et administrer le plugin. Téléchargez, essayez et faites nous part de vos commentaires !

Notre expertise et la puissance des technologies open source dans Azure

Logos Microsoft + Savoir-faire Linux  

 

 

 

Nous sommes heureux de vous annoncer la sortie d’une vidéo promotionnelle produite par Microsoft eux-même ! Fruit d’une collaboration autour de la plateforme Azure, cette vidéo souligne la pertinence et l’essor des technologies open source dans l’environnement infonuagique Azure de Microsoft ainsi que notre capacité d’innovation en combinant technologies open source et solutions infonuagiques hybrides.

Premier  «partenaire Microsoft Open Source» au Canada

Savoir-faire Linux est, depuis 2015, le premier  «partenaire Microsoft Open Source» au Canada. Ce partenariat rappelle notre force : la conception et l’intégration de technologies libres et open source dans des écosystèmes complexes ou hybrides, comme la plateforme Azure. Nous aidons nos clients grâce à l’innovation des technologies ouvertes et à des alliances stratégiques pour maximiser la puissance des systèmes d’information complexes ou hétérogènes et créer des environnements spécifiques pour nos clients.

Pour Microsoft, l’open source fait désormais partie intégrante de l’approche quotidienne de l’innovation cloud. En tant qu’expert et chef de file de l’industrie du logiciel libre et  des technologies open source au Canada, Savoir-faire Linux se devait d’offrir des moyens d’améliorer l’expérience des utilisateurs et des développeurs en ajoutant à son éventail de solutions, la plateforme Azure, désormais compatible avec de nombreuses technologies open source.