Editeur de script (outil)


Les fonctionnalités décrites dans cette page ne sont disponible qu'à partir d'Ametys v4.2 

 

L'éditeur de scripts permet d'écrire et d'exécuter des scripts depuis l'outil back-office.
Cet outil dispose d'une grille permettant d'afficher les contenus retournés par le script, en plus de l'affichage texte des messages et résultats. Il dispose d'une fonction permettant de mettre en forme les contenus retournés, et de spécifier les colonnes à afficher dans la grille.
Par ailleurs, il peut prendre en compte les contenus actuellement sélectionnés (depuis un outil de recherche par exemple) au moment de l'exécution du script, par le biais d'une variable "selection" mise à disposition.

  1. Scripts
    1. Présentation de l'outil
    2. Langage des scripts
  2. Variables et fonctions Ametys
    1. Variables
    2. Fonctions
  3. Exemples de scripts
    1.  Afficher les programmes ayant la même composante que le programme sélectionné
    2. Générer un rapport sur les programmes et éléments pédagogiques
  4. Fichiers de scripts
    1. Tutorials
    2. Variables
    3. Functions

 

Scripts

Présentation de l'outil

Pour pouvoir accéder à l'outil, il faut posséder le droit "Outil Script" de la catégorie "Autres outils". L'outil est alors disponible dans l'onglet Accueil.

Cet outil est découpé en 3 zones :

- En haut, la partie script, où le script pourra être écrit à l'intérieur d'une fonction "main". En dessous de la zone de script, il y a un bouton "Exécuter" permettant d'exécuter le script.

- En bas à gauche, prenant toute la largeur par défaut, se trouve une grille vide permettant d'afficher les contenus retournés par l'exécution du script.

- En bas à droite, réduit par défaut, se trouve la "Console", c'est à dire le résultat au format texte de l'exécution du script.

Après exécution d'un script avec succès, la partie script est rétracté pour afficher la grille et la console avec les résultats du script.

Langage des scripts

L'éditeur de scripts utilise le moteur de JavaScript de Java 8, appelé "Nashorn". La syntaxe attendue est donc la syntaxe de Nashorn. Ce moteur étant exécuté en Java, il permet en plus du JavaScript d'accéder et manipuler les méthodes et class de Java. Par exemple:

var ArrayList = Java.type('java.util.ArrayList');           
var list = new ArrayList();           

Une autre fonctionnalité couramment utilisée est la méthode "print()" permettant d'afficher du texte dans la sortie console.

print("Sélection actuelle : " + selection)           

Référez-vous à la syntaxe Nashorn pour pouvoir écrire vos propres scripts.

 

Variables et fonctions Ametys

Ametys met à disposition un certain nombre de variables et fonctions pour pouvoir utiliser les fonctionnalités d'Ametys au sein des scripts.

Pour les versions Ametys 4.3 et suivantes
Pour ouvrir l'outil d'aide des scripts, cliquez sur le bouton aide, placé à côté du bouton d'exécution des scripts :

L'aide est classée en 3 catégories : variables, fonctions et tutoriels.
Un filtre est disponible en haut pour chercher rapidement l'un ou l'autre par son nom.
Les exemples disponibles peuvent être sélectionnés à la souris puis copiés dans le presse-papier via le raccourci CTRL+C.

Pour les versions Ametys 4.2 et précédentes
Pour voir à tout moment la liste des variables et fonctions disponibles dans l'outil, il suffit de passer la souris sur le "?" en haut à droite de l'outil scripts :

Pour aller plus loin
Il est possible d'ajouter ses propres fonctions dans le fichier "WEB-INF/param/scripts.xml" (voir plus bas). Il est également possible, au sein d'un plugin, d'utiliser le point d'extension "org.ametys.plugins.core.ui.script.ScriptBindingExtensionPoint" pour apporter d'autres variables et fonctions.

 

Les fonctions et variables disponibles dépendent du contexte courant.
Par exemple, la fonction contents et la variable selection ne sont disponibles que dans le CMS, vous n'aurez pas accès à cette fonction dans l'outil Script de l'interface d'administration.

Variables

- "sourceResolver" est un raccourci pour le composant "org.apache.excalibur.source.SourceResolver", permettant de résoudre des URI d'Ametys. Par exemple :

var newsletterTemplateSource = sourceResolver.resolveURI("context://skins/" + skinId + "/newsletter/" + id + "/stylesheets/template.xsl")           

- "selection" est la liste des contenus actuellement sélectionnés, ou une liste vide si aucun contenu n'est sélectionné.

for each (var content in selection) {           
 print(content.getId())           
}           

- "session" donner accès en lecture et écriture au repository. Par exemple :

var rootNode = session.getRootNode().getNode("ametys:root");           

- "ConsoleHelper" est un raccourci vers la class "org.ametys.workspaces.repository.ConsoleHelper", un helper qui fournit des méthodes comme "setProperty" ou "convertSingleToMultipleProperty" pour aider lors des opérations sur le repository

- "avalonContext" correspond au contexte avalon de la requête. Il possède des informations comme le site courant.

var site = avalonContext.get(org.apache.cocoon.components.ContextHelper.CONTEXT_REQUEST_OBJECT).getAttribute("siteName");           

- "ametysResolver" permet de résoudre les objets Ametys à partir de leur identifiant ou leur "path"

var creator = ametysResolver.resolveById("courseContent://471545a1-2c75-4f75-b774-dda2bf0fa960").getCreator();         
var site = ametysResolver.resolveByPath("ametys:sites/www");           

- "repository" représente le repository, et permet par exemple de changer le workspace de la session :

var credentials = new javax.jcr.SimpleCredentials('ametys', []);           
session = repository.login(credentials, 'archives');           

- "serviceManager" permet de récupérer les components avalon que possède Ametys : 

var contentTypesHelper = serviceManager.lookup(org.ametys.cms.contenttype.ContentTypesHelper.ROLE)           

- "progressionTracker" (à partir de Ametys 4.8) permet de prévenir de l'avancement du script pour l'afficher dans les journaux (et dans le planificateur de tâches pour les scripts asynchrones) :

progressionTracker.setSteps([
  {
    id: 'init',
    label: "Initialisation",
  },
  {
    id: 'work',
    label: "Travail",
    weight: 100,
    children: [
        {
          id: 'workContent',
          label: "Contenus",
          weight: 20
        },
        {
            id: 'workPages',
            label: "Pages",
            weight: 20,
            children: [
                {
                  id: "workPagesSitemap",
                  label: "Plan du site"
                }
            ]
        },
        {
          id: 'workAlias',
          label: "Alias"
        },
    ]
  },
  {
    id: 'finish',
    label: "Finalisation"
  }
])
// ...
progressionTracker.increment('init'); // Default size is 1, so incrementing here is going to complete the step
// ...
progressionTracker.setSize('workContent', 2);
// ...
progressionTracker.increment('workContent'); // Increment of 1
// ...
progressionTracker.increment('workContent');
// ...
progressionTracker.setSize('workPagesSitemap', 10);
// ...
progressionTracker.increment('workPagesSitemap', 10); // Increment of 10
// ...
progressionTracker.setSize('workAlias', 20);
// ...
progressionTracker.increment('workAlias', 5); // Increment of 5, total progress is 5
// ...
progressionTracker.increment('workAlias', 10); // Increment of 10, total progress is 15
// ...
progressionTracker.increment('workAlias', 2); // Increment of 2, total progress is 17
// ...
progressionTracker.increment('workAlias', 3); // Increment of 3, total progress is 20
// ...
progressionTracker.setSize('finish', 3);
// ...
progressionTracker.increment('finish', 2); // Increment of 2
// ...
progressionTracker.increment('finish'); // Increment of 1

Fonctions

- "jcrXPathQuery" exécute une requête XPath JCR pour récupérer une liste de noeuds

for each (var content in jcrXPathQuery("//element(*, ametys:content)[jcr:like(@ametys-internal:contentType, 'odf-enumeration.%')]")) {          
 print(content.getId())          
}          

- "printSqlQuery" exécute la fonction "sqlQuery" (voir plus bas), et affiche directement le résultat dans la console, mis en forme

printSqlQuery("SELECT * FROM users", "SQL-j2c1c5hc")          

- "migrateContent" est une fonction helper permettant d'exécuter une liste de fonctions de migration, tout en marquant ou non les versions précédentes comme incompatibles, en ajoutant ou non un tag ("live" par exemple) sur les nouvelles version.

function _titleMigration(content) { /* migrate title ... */ }          
 migrateContent(content, [_titleMigration], true /*old versions incompatible*/, null /*no tag*/, false /*not verbose*/);          

- "sqlQuery" exécute une requête sql sur la dataSource passée en 2e paramètre. Le paramètre dataSource peut aussi être l'identifiant d'une dataSource, par simplicité.

var dataSource = serviceManager.lookup("org.ametys.core.datasource.SQLDataSourceManager").getSQLDataSource("SQL-j2c1c5hc");          
var result = sqlQuery("SELECT * FROM users", dataSource)          

- "contents" est une fonction helper utilisée pour mettre en forme des contenus, pour les afficher dans la grille de résultats. Le premier paramètre est un tableau avec les différentes colonnes à afficher. Si la valeur est un tableau vide, tous les champs du contenu seront affichés dans la grille. Le deuxième paramètre est une liste de contenus.

return contents([], selection); // affiche la sélection courante dans la grille          

- "sqlUpdate" permet d'effectuer une requête SQL modifiant des données existantes (mise à jour, suppression, ...)

sqlUpdate("UPDATE users SET FIRSTNAME='Firstname' WHERE FIRSTNAME='admin'", "SQL-j2c1c5hc");          

 

Exemples de scripts

Les exemples donnés ci-après sont des exemples pouvant être exécutés dans une application Ametys ODF.

 Afficher les programmes ayant la même composante que le programme sélectionné

var showContentsIds = [];          
selection.forEach(function (content) {          
  var orgUnits = content.getOrgUnits();         
          
  orgUnits.forEach(function (orgUnit) {          
    // Nashorn supporte la syntaxe "for each(var ... in ...)" ci dessous, mais on peut également utiliser le "list.forEach(callback)" de java, ligne au dessus        
    for each (var contentWithOrgUnit in jcrXPathQuery("//element(*, ametys:programContent)[@ametys:orgUnit = '" + orgUnit + "']")) // jcrXPathQuery permet d’exécuter un XPATH sur le repository        
    {          
      if (showContentsIds.indexOf(contentWithOrgUnit.getId()) == -1)          
      {          
        showContentsIds.push(contentWithOrgUnit.getId());          
      }          
    }          
  });          
});          

var returnContents = [];          
showContentsIds.forEach(function (contentId) {          
  returnContents.push(ametysResolver.resolveById(contentId));  // utilisation du ametysResolver pour transformer les ids en contents        
})          

return contents([], returnContents); // retourner "contents()" permet d'afficher des contenus dans la grille de résultats          

Si le script renvoie un objet avec une propriété "results" qui contient une liste de contenus, ils seront affichés dans la grille. Si le script renvoie un objet avec une propriété "columns", la grille sera configurée pour afficher ces columns. Si columns est un tableau vide, alors les champs du ou des contenu(s) seront utilisés pour définir les colonnes de la grille. La méthode "contents(columns, contents)" permet de renvoyer facilement un objet avec les bonnes propriétés pour remplir la grille.

 

Générer un rapport sur les programmes et éléments pédagogiques

var StringUtils = Java.type("org.apache.commons.lang3.StringUtils");         

function doProgram(program) {         
  var out = program.getTitle();         
  out += ";";         
           
  out += program.getLanguage();         
  out += ";";         

  program.getOrgUnits().forEach(function (orgUnit) {         
    var org = ametysResolver.resolveById(orgUnit);         
    if (org != null) {         
      out += org.getTitle() + ",";         
    }         
  });         
  out += ";";         

  out += "ects (" + (StringUtils.isEmpty(program.getEcts()) ? "EMPTY" : "OK") + ")";         
  out += ";";         

  out += ";"; // parcours - Titre (vide)         

  print(out);         

  traverseProgramPart(program, {program: program});         
}         

function traverseProgramPart(part, data) {         
  traversePattern(part, "getProgramPartChildren", traverseProgramPart, data);         
  traversePattern(part, "getCourses", doCourse, data);         
}         

function doCourse(course, data) {         
  var out = data.program.getTitle();         
  out += ";;;;"; // ;lang;Composante;Champs texte; (vide)         
  out += course.getTitle();         

  print(out);         

  traversePattern(course, "getCourseLists", traverseProgramPart, data);         
}         

// Méthode helper pour traverser les noeuds de l'arbre des program/subprogram/container/courseList/course        
function traversePattern(traversable, getChildrenFctName, traverseChildFct, data) {         
  if (typeof traversable[getChildrenFctName] == "function")         
  {         
    traversable[getChildrenFctName]().forEach(function (child) {         
      traverseChildFct(child, data);         
    });         
  }         
}         

var contents = [];         
print("Formation;lang;Composante;Champs texte;Parcours - Titre"); // entête du fichier csv         
jcrXPathQuery("//element(*, ametys:programContent)").forEach(doProgram);         

 Ce script affiche dans la console, au format csv, la liste de toutes les formations disponibles, avec le nom de la composante parente, la langue, si le champ texte "ects" est vide ou pas, et le titre des éléments pédagogiques sous la formation.

 

Fichiers de scripts

Que ce soit via le point d'extension ou via le fichier WEB-INF/param/scripts.xml il est possible d'ajouter des éléments à l'outil de scripts.

A partir de Ametys 4.3, le format du fichier change et c'est celui-ci qui est décrit dans cette documentation.

Le format est le suivant

<?xml version="1.0" encoding="UTF-8"?>     
<binding>     
  <tutorials>...</tutorials>     
  <variables>...</variables>     
  <functions>...</functions>     
</binding>     

Tutorials

Permet de décrire les tutoriels.

Un tutoriel est constitué de :

  • un libellé
  • un texte principal
  • d'exemples
    • un texte d'introduction de l'exemple
    • le code de l'exemple (directement ou dans un fichier séparé)
<tutorial>     
  <name type="i18n">MYI18NIZEDNAME</name>     
  <text type="i18n">MYI18NIZEDTEXT</name>     
  <examples>     
    <example>     
      <text type="i18n">MYINTROTOSAMPLE</text>     
      <script file="tuto.js"/>     
    </example>     
    <example>     
      <text type="i18n">MYINTROTOSAMPLE</text>     
      <script>onlinetuto();</script>     
    </example>     
  </examples>     
</tutorial>     

Variables

Les variables sont codées dans le point d'extension en Java (et il n'est donc pas possible d'en ajouter depuis le fichier WEB-INF/param/scripts.xml). Mais elles sont documentées dans le fichier XML.

Elles sont constituées comme les tutoriaux avec en plus :

  • Signature : leur signature
    • Type : leur type (java ou javascript)
    • Subtype : si le type est un objet permet de décrire l'objet avec une signature.
<variable>     
  <name>myVariable</name>     
  <text type="i18n">MYVARIABLETEXT</text>     
  <signature>     
    <type>my.variable.Type</type>     
  </signature>     
  <examples><!-- voir les exemples dans les tutoriels --></examples>     
</variable>     

Functions

Les fonctions constituées comme les tutoriels avec en plus

  • Script : le code de la fonction (directement ou dans un fichier séparé). Il est possible de mettre plusieurs fois cette balise pour charger plusieurs fichiers.
  • Signature: leur signature
    • Type : leur type de retour (java ou javascript)
    • Subtype : si le type est un objet permet de décrire l'objet avec une signature.
    • Texte associé à la valeur de retour
    • Arguments.
      • Nom de l'argument
      • Type de l'argument
      • Subtype : si le type est un objet permet de décrire l'objet avec une signature.
      • Text associé à l'argument
      • Valeur par défaut de l'argument s'il est optionnel. Mettre null pour indiquer que l'élement est optionnel mais sans afficher de valeur par défaut.
<function>     
  <name>myFunction</name>     
  <text type="i18n">MYFUNCTIONTEXT</text>     
  <script file="myfunction.js"/>     
  <signature>     
    <type>MyReturnType</type>     
    <text type="i18n">RETURNTYPETEXT</text>     
    <arguments>     
      <argument>     
        <name>my1stArg</name>     
        <type>MyFirstArgType</type>     
        <subtype>  
            <argument>  
                <name>subtype</name>  
                <type>Boolean</type>  
            </argument>  
        </subtype>  
        <text type="i18n">MYFIRSTARGTEXT</text>     
        <optional>"My default string value"</optional>    
      </argument>     
    </arguments>     
  </signature>     
  <examples><!-- voir les exemples dans les tutoriels --></examples>     
</function>     

Pour une fonction, il faut toujours placer la balise <arguments> même si elle est vide dans le cas où la fonction ne prend pas d'arguments.

Retour en haut