The features described on this page are only available inAmetys v4.2 and higher.
The script editor lets you write and run scripts from the back-office tool. This tool features a grid for displaying the content returned by script, in addition to the text display of messages and results. It features a function for formatting returned content, and for specifying the columns to be displayed in the grid. In addition, it can take into account content currently selected (from a search tool, for example) at the time of execution of script, by means of a "selection" variable.
To access the tool, you must have the "Tool Script" right in the "Other tools" category. The tool is then available in the Home tab.
This tool is divided into 3 zones:
- At the top, the script area, where script can be written inside a "main" function. Below the script area, there's an "Execute" button to run the script function.
- At bottom left, taking up the entire width by default, is an empty gridfor displaying the contents returned by the execution of script.
- In the bottom right-hand corner, reduced by default, you'll find the"Console", i.e. the result of the script execution in text format.
After successful execution of a script , the script part is retracted to display the grid and the console with the results of the script.
Scripting language
The script editor uses Java 8's JavaScript engine, called"Nashorn". The expected syntax is therefore Nashorn syntax. As this engine runs at Java, it allows access to and manipulation of Java methods and classes, in addition to JavaScript . For example, Nashorn
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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();
Another commonly used feature is the"print()" method for displaying text in console output.
Oops!
Copy to clipboard failed. Open the code and copy it manually.
print("Sélection actuelle : " + selection)
print("Sélection actuelle : " + selection)
print("Sélection actuelle : " + selection)
Refer to the Nashorn syntax to write your own scripts.
Variables and functions Ametys
Ametys provides a number of variables and functions to enable you to useAmetys 's features within your scripts.
For versions Ametys 4.3 and higher To open the script help tool, click on the help button next to the script execution button:
Help is divided into 3 categories: variables, functions and tutorials. A filter is available at the top to quickly search for one or the other by name. Available examples can be selected with the mouse and then copied to the clipboard using the shortcut CTRL+C.
For versions Ametys 4.2 and earlier To view the list of variables and functions available in the tool at any time, just move your mouse over the "?" in the top right-hand corner of the scripts tool :
To go further You can add your own functions to the file "WEB-INF/param/scripts.xml" (see below). Within plugin, you can also use the "org.ametys.plugins.core.ui.script.ScriptBindingExtensionPoint" extension point to add other variables and functions.
The functions and variables available depend on the current context. For example, the contents function and the selection variable are only available in CMS, you will not have access to this function in the Script tool of the administration interface.
Variables
-"sourceResolver" is a shortcut for the "org.apache.excalibur.source.SourceResolver" component, used to resolve URI fromAmetys. For example:
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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" is the list of currently selected contents, or an empty list if no contents are selected.
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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" give read and write access to the repository. For example :
Oops!
Copy to clipboard failed. Open the code and copy it manually.
var rootNode = session.getRootNode().getNode("ametys:root");
var rootNode = session.getRootNode().getNode("ametys:root");
var rootNode = session.getRootNode().getNode("ametys:root");
-"ConsoleHelper" is a shortcut to the "org.ametys.workspaces.repository.ConsoleHelper" class, a helper that provides methods such as "setProperty" or "convertSingleToMultipleProperty" to assist with repository operations.
-avalonContext" corresponds to the avalon context of the request. It contains information such as the current site.
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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" resolves Ametys objects using their identifier or "path".
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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" represents the repository, and allows you to change the session workspace, for example:
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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" is used to retrieve the avalon components owned by Ametys :
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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)
-"progressionTracker" (from Ametys 4.8 onwards) allows script progress to be notified and displayed in logs (and in the task scheduler for asynchronous scripts):
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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
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
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
Functions
-jcrXPathQuery" executes an XPath query JCR to retrieve a list of nodes
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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" executes the "sqlQuery" function (see below), and displays the result directly in the console, formatted as follows
Oops!
Copy to clipboard failed. Open the code and copy it manually.
printSqlQuery("SELECT * FROM users", "SQL-j2c1c5hc")
printSqlQuery("SELECT * FROM users", "SQL-j2c1c5hc")
printSqlQuery("SELECT * FROM users", "SQL-j2c1c5hc")
-migrateContent" is a helper function for executing a list of migration functions, while marking previous versions as incompatible or not, and adding a tag ("live" for example) to new versions or not.
Oops!
Copy to clipboard failed. Open the code and copy it manually.
-"sqlQuery" executes a sql query on the dataSource passed as the 2nd parameter. The dataSource parameter can also be the identifier of a dataSource, for simplicity's sake.
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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" is a helper function used to format contents for display in the results grid. The first parameter is an array with the various columns to be displayed. If the value is an empty table, all content fields will be displayed in the grid. The second parameter is a list of contents.
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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" can be used to run a query SQL to modify existing data (update, delete, etc.).
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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");
Sample scripts
The examples given below are examples that can be executed in an application Ametys ODF .
Display programs with the same component as the selected program
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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
If the script returns an object with a "results" property that contains a list of contents, they will be displayed in the grid. If the script returns an object with a "columns" property, the grid will be configured to display these columns. If columns is an empty array, then the contents field(s) will be used to define the grid columns. The "contents(columns, contents)" method makes it easy to return an object with the right properties to fill the grid.
Generate a report on programs and pedagogical elements
Oops!
Copy to clipboard failed. Open the code and copy it manually.
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);
This script displays in the console, in csv format, the list of all available courses, with the name of the parent component, the language, whether the "ects" text field is empty or not, and the title of the teaching elements under the course.
Script files
Either via the extension point or via the WEB-INF/param/scripts file.xml it is possible to add elements to the scripting tool.
From Ametys 4.3 onwards, the file format changes, and it is this format that is described in this documentation.
The format is as follows
Oops!
Copy to clipboard failed. Open the code and copy it manually.
Variables are encoded in the extension point at Java (and therefore cannot be added from WEB-INF/param/scripts.xml). However, they are documented in the XML file.
They are set up in the same way as the tutorials, with the addition of :
Signature: their signature
Type : their type (java or javascript)
Subtype: if the type is an object, describes the object with a signature.
Oops!
Copy to clipboard failed. Open the code and copy it manually.
<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
Functions such as tutorials, plus
Script tag: the function code (directly or in a separate file). This tag can be used several times to load multiple files.
Signature: their signature
Type : their return type (java or javascript)
Subtype: if the type is an object, describes the object with a signature.
Text associated with the return value
Arguments.
Argument name
Argument type
Subtype: if the type is an object, describes the object with a signature.
Text argument
Default value of argument if optional. Set null to indicate that the element is optional, but without displaying a default value.
Oops!
Copy to clipboard failed. Open the code and copy it manually.