Libérez les dépendances JavaScript du joug des dépendances CSS !

Continuons la présentation des modules d’extension pour Share avec un rapide mot sur les customisations et l’injection de CSS ou JavaScript spécifiques (ce que nous appelons les « dépendances »).
Comme le titre peut le laisser penser, tout ne va pas être merveilleux dans ce petit monde et quelques ajustements vont être nécessaires.

Présentations des customisations

Les customisations permettent d’apporter des ajustements sur les composants d’une page (internationalisations, JavaScript serveur), sur la structure de la page elle-même en permettant des manipulations sur les régions et de compléter les JavaScripts client et les CSS qui doivent être chargés en complément des composants.
Elles sont directement définies dans le fichier de description du module d’extension Share.

Mettre en place une customisation

Je vous invite à consulter cette documentation qui explique en détails les opérations en réaliser.
Pour les non anglophones (ou les paresseux du click) voici en synthèse ce qu’il faut retenir :

  • utiliser SurfBug pour identifier le composant à personnaliser (pour une introduction à SurfBug, référez-vous à mon précédent billet)
  • créer un fichier de description du module
  • ajouter la section customizations > customization
  • référencer la cible à étendre dans l’élément targetPackageRoot

Et voila le minimum !

<extension>
  <modules>
    <module>
      <id>Exemple de personnalisation</id>
      <customizations>
        <customization>
          <targetPackageRoot>org.alfresco.components.documentlibrary</targetPackageRoot>
        </customization>
      </customizations>
    </module>
  </modules>
</extension>

Avec une déclaration aussi minimaliste, nous n’aurons bien entendu pas beaucoup de résultats. Il nous faut spécifier un petit peu ce que nous attendons comme personnalisation du package référencé.
La personnalisation la plus simple consiste en la définition de dépendances.

Les dépendances

Les dépendances permettent de définir une feuille de style et un fichier JavaScript qui seront systématiquement intégrés aux rendus des WebScripts définis dans le package spécifié par targetPackageRoot. C’est une méthode assez simple pour modifier l’interface, mais il faut rester prudent sur son usage car chaque WebScript contenu dans le package sera étendu.
La définition de dépendances est réalisée en insérant une section dependencies dans la déclaration de la customization. La section dependencies est déclarée comme suit :

<dependencies>
  <css>/res/amexio/components/css/exemple-customization-extension.css</css>
  <js>/res/amexio/components/js/exemple-customization-extension.js</js>
</dependencies>

Ce qui donne la définition de module suivante :
<extension>
  <modules>
    <module>
      <id>Exemple de personnalisation</id>
      <customizations>
        <customization>
          <targetPackageRoot>org.alfresco.components.documentlibrary</targetPackageRoot>
          <dependencies>
            <css>/res/amexio/components/css/exemple-customization-extension.css</css>
            <js>/res/amexio/components/js/exemple-customization-extension.js</js>
          </dependencies>
        </customization>
      </customizations>
    </module>
  </modules>
</extension>

Notre targetPackage étant org.alfresco.components.documentlibrary, à chaque fois qu’un composant de ce package sera utilisé sur une page de Share, les fichiers « exemple-customization-extension.css » et « exemple-customization-extension.js » seront inclus dans les entêtes HTML de la page.

Limite des dépendances

Après quelques expérimentations, on se rend compte que la définition d’une extension JavaScript n’est prise en compte qu’à la condition qu’une extension CSS existe également.
La faute à la méthode getExtendingModuleDependencies de la classe BasicExtensibilityModuleHandler qui inclut le traitement des dépendances JavaScript dans la conditionnelle suivante :
if (!allCSSDependencies.isEmpty())

Deuxième constat : même en mode debug, c’est la version minimale (je n’ai pas osé écrire « minifiée ») des scripts qui est utilisée. Ce problème vient – probablement – d’une mauvaise compréhension de la méthode Boolean.getBoolean. Dans la méthode getExtendingModuleDependencies, on peut en effet lire :
ConfigElement clientDebugElement = ((GenericConfigElement)flags).getChild("client-debug");
if (clientDebugElement != null)
{
  isInDebugMode = Boolean.getBoolean(clientDebugElement.getValue());
}

Qui se traduit en :

Récupère la valeur de l'élément "client-debug" dans les fichier de configuration (admettons que client-debug soit positionné à "true")
Si "client-debug" n'est pas nul alors
  isInDebugMode est vrai si l'objet true (la valeur de client-debug) a pour valeur "true"
Fin si

Si nous n’avons pas d’objet nommé « true », ce qui doit être le cas, alors isInDebugMode est faux et la conséquence est que les spécificités du mode debug ne seront pas appliquées.

Je vous invite a aller jeter un œil à cette méthode pour bien vous rendre compte de la chose et de comment elle est formulée.

Lever cette limite

Si vous avez parcourus quelque peu se blog, vous devez savoir que lorsque je tombe sur une limite d’Alfresco, j’ai tendance (serait-ce une addiction ?) à vouloir la lever. En rencontrant ce problème, je me suis donc attelé à sa correction.
Comme d’habitude, la première étape consiste à vérifier que nous disposons d’un point d’extension possible. Ce point d’extension, on le trouve dans le fichier spring-surf-application-context.xml, en provenance de spring-surf-1.0.0.jar (remarque en passant : ne pas se fier au numéro de version de SpringSurf, Alfresco embarque une version légèrement ultérieure à la 1.0.0) :
<bean id="webscripts.extensibility.handler" class="org.springframework.extensions.surf.extensibility.impl.BasicExtensibilityModuleHandler">
  <property name="moduleDeploymentService" ref="module.deployment.service"/>
  <property name="defaultModuleEvaluator" ref="default.extensibility.evaluator"/>
  <property name="scriptConfigModel" ref="script.config.model.instance"/>
</bean>

La classe BasicExtensibilityModuleHandler est directement définie dans un bean Spring, il va donc nous être possible de :

  1. Définir notre propre classe héritant de BasicExtensibilityModuleHandler et corrigeant la méthode getExtendingModuleDependencies
  2. Redéfinir le bean Spring webscripts.extensibility.handler pour faire usage de notre propre classe

Facile, non ?
Pour le premier point, je propose l’ajustement suivant (j’ai enlevé les commentaires d’origine pour plus de lisibilité) :
public String getExtendingModuleDependencies(ExtensionModule module, String path, String appContext)
{
  StringBuilder dependencies = new StringBuilder();
  LinkedHashSet allCSSDependencies = new LinkedHashSet();
  LinkedHashSet allJSDependencies = new LinkedHashSet();
  
  for (Customization customization: module.getCustomizations())
  {
    if (customization.getTargetPackageName() != null)
    {
      String targetPackage = customization.getTargetPackageName().replace(".", "/");
      if (path.startsWith(targetPackage))
      {
        allCSSDependencies.addAll(customization.getCssDependencies());
        allJSDependencies.addAll(customization.getJsDependencies());
      }
    }
  }
  
  if (allCSSDependencies.isEmpty())
  {
    // No CSS dependencies to create imports for...
  }
  else
  {
    dependencies.append("

\n");
  }
  
  // DO NOT FORGET TO PROCESS JS DEPENDENCIES !!!
  if (allJSDependencies.isEmpty())
  {
    // No JS dependencies to create imports for...
  }
  else
  {
    boolean isInDebugMode = true;
    Object flags = scriptConfigModel.getGlobal().get("flags");
    if (flags instanceof GenericConfigElement)
    {
      ConfigElement clientDebugElement = ((GenericConfigElement) flags).getChild("client-debug");
      if (clientDebugElement != null)
      {
        // Do not use Boolean.getBoolean(clientDebugElement.getValue()); as in Spring Surf BasicExtensibilityModuleHandler class
        isInDebugMode = Boolean.valueOf(clientDebugElement.getValue()).booleanValue();
      }
    }
  
    for (String jsLib: allJSDependencies)
    {
      if (!isInDebugMode)
      {
        // If we're NOT in debug mode then convert the requested JavaScript
        jsLib = jsLib.replace(".js", "-min.js");
      }
      dependencies.append("");
    }
  }
  return dependencies.toString();
}

Pour le second, un simple fichier « -context.xml » placé dans le dossier alfresco/web-extension de Share fera l’affaire. Ce fichier contiendra la définition suivante :
<bean id="webscripts.extensibility.handler" class="fr.amexio.springframework.extensions.surf.extensibility.impl.CorrectBasicExtensibilityModuleHandler">
  <property name="moduleDeploymentService" ref="module.deployment.service"/>
  <property name="defaultModuleEvaluator" ref="default.extensibility.evaluator"/>
  <property name="scriptConfigModel" ref="script.config.model.instance"/>
</bean>

Et voila !
On peut maintenant utiliser des dépendances JavaScript indépendamment des dépendances CSS, ainsi que bénéficier du mode « debug ».
La modification que nous avons réalisée ci-dessus est valable pour Alfresco 4.0a, 4.0b, 4.0c, 4.0d et probablement pour la 4.0 Entreprise. La récente évolution de SpringSurf (cf. trunk SVN) devrait corriger ces petits problèmes.

Cette entrée a été publiée dans Alfresco, avec comme mot(s)-clef(s) , , , . Vous pouvez la mettre en favoris avec ce permalien.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Protected by WP Anti Spam