Dernière publication:24/02/2020à 13:58MagaliFranchet
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.
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:
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
var ArrayList = Java.type('java.util.ArrayList');
var list = new ArrayList();
var ArrayList = Java.type('java.util.ArrayList');
var list = new ArrayList();
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.
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
print("Sélection actuelle : " + selection)
print("Sélection actuelle : " + selection)
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 :
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
var newsletterTemplateSource = sourceResolver.resolveURI("context://skins/" + skinId + "/newsletter/" + id + "/stylesheets/template.xsl")
var newsletterTemplateSource = sourceResolver.resolveURI("context://skins/" + skinId + "/newsletter/" + id + "/stylesheets/template.xsl")
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é.
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
for each (var content in selection) {
print(content.getId())
}
for each (var content in selection) {
print(content.getId())
}
for each (var content in selection) {
print(content.getId())
}
- "session" donner accès en lecture et écriture au repository. Par exemple :
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
var rootNode = session.getRootNode().getNode("ametys:root");
var rootNode = session.getRootNode().getNode("ametys:root");
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.
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
var site = avalonContext.get(org.apache.cocoon.components.ContextHelper.CONTEXT_REQUEST_OBJECT).getAttribute("siteName");
var site = avalonContext.get(org.apache.cocoon.components.ContextHelper.CONTEXT_REQUEST_OBJECT).getAttribute("siteName");
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"
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
var creator = ametysResolver.resolveById("courseContent://471545a1-2c75-4f75-b774-dda2bf0fa960").getCreator();
var site = ametysResolver.resolveByPath("ametys:sites/www");
var creator = ametysResolver.resolveById("courseContent://471545a1-2c75-4f75-b774-dda2bf0fa960").getCreator();
var site = ametysResolver.resolveByPath("ametys:sites/www");
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 :
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
var credentials = new javax.jcr.SimpleCredentials('ametys', []);
session = repository.login(credentials, 'archives');
var credentials = new javax.jcr.SimpleCredentials('ametys', []);
session = repository.login(credentials, 'archives');
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 :
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
var contentTypesHelper = serviceManager.lookup(org.ametys.cms.contenttype.ContentTypesHelper.ROLE)
var contentTypesHelper = serviceManager.lookup(org.ametys.cms.contenttype.ContentTypesHelper.ROLE)
var contentTypesHelper = serviceManager.lookup(org.ametys.cms.contenttype.ContentTypesHelper.ROLE)
Fonctions
- "jcrXPathQuery" exécute une requête XPath JCR pour récupérer une liste de noeuds
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
for each (var content in jcrXPathQuery("//element(*, ametys:content)[jcr:like(@ametys-internal:contentType, 'odf-enumeration.%')]")) {
print(content.getId())
}
for each (var content in jcrXPathQuery("//element(*, ametys:content)[jcr:like(@ametys-internal:contentType, 'odf-enumeration.%')]")) {
print(content.getId())
}
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
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
printSqlQuery("SELECT * FROM users", "SQL-j2c1c5hc")
printSqlQuery("SELECT * FROM users", "SQL-j2c1c5hc")
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.
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
- "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é.
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
var dataSource = serviceManager.lookup("org.ametys.core.datasource.SQLDataSourceManager").getSQLDataSource("SQL-j2c1c5hc");
var result = sqlQuery("SELECT * FROM users", dataSource)
var dataSource = serviceManager.lookup("org.ametys.core.datasource.SQLDataSourceManager").getSQLDataSource("SQL-j2c1c5hc");
var result = sqlQuery("SELECT * FROM users", dataSource)
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.
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
return contents([], selection); // affiche la sélection courante dans la grille
return contents([], selection); // affiche la sélection courante dans la grille
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, ...)
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
sqlUpdate("UPDATE users SET FIRSTNAME='Firstname' WHERE FIRSTNAME='admin'", "SQL-j2c1c5hc");
sqlUpdate("UPDATE users SET FIRSTNAME='Firstname' WHERE FIRSTNAME='admin'", "SQL-j2c1c5hc");
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é
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
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
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
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
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
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);
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);
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
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
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.
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.
<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>
<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>
<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.
Oups !
La copie dans le presse papier a échouée. Ouvrez le code et copier-le manuellement.