If you publish your training offer from an entry device ODF-Web portal to ODF, a bug caused the links to the internal pages of the entry device not to work on the portal: https: //issues.ametys.org/browse/ODF -1297
To correct this, you need to modify the WEB-INF/param/workflow-program.xmlyour applications ODF-Web(caution: ODF applications alone must not be modified).
In these files, find and replace org.ametys.odf.cdmfr.SendCDMFRFunction by org.ametys.plugins.odfweb.workflow.SendCDMFRFunction
Duplicating a catalog
To correct the bug https://issues.ametys.org/browse/ODF-1314you need to modify the workflow file WEB-INF/param/workflow-program.xml by adding the following action to the <initial-actions>
Oops!
Copy to clipboard failed. Open the code and copy it manually.
Unpublish a training course from a non-web entry device ODF
This section concerns only non-Web ODF input devices, i.e. those that do not use plugin ODF -Web
A workflow action to unpublish a training course has been added in order to be able to unpublish a training course published on a ODF portal. This new action only makes sense if the entry device publishes its training courses on a ODF portal. However, the following steps must be followed in all cases:
1) Add the right to unpublish
In the WEB-INF/param/rights.xmladd the right Workflow_Rights_Unpublish
Oops!
Copy to clipboard failed. Open the code and copy it manually.
Add translations of the right wording and description to the WEB-INF/i18n/application.xml and WEB-INF/i18n/application_en.xml
application.xml
Oops!
Copy to clipboard failed. Open the code and copy it manually.
<message key="APPLICATION_RIGHTS_UNPUBLISH_LABEL">Dépublication</message>
<message key="APPLICATION_RIGHTS_UNPUBLISH_DESCRIPTION">Autorise à dépublier un contenu</message>
<message key="APPLICATION_RIGHTS_UNPUBLISH_LABEL">Dépublication</message>
<message key="APPLICATION_RIGHTS_UNPUBLISH_DESCRIPTION">Autorise à dépublier un contenu</message>
<message key="APPLICATION_RIGHTS_UNPUBLISH_LABEL">Dépublication</message>
<message key="APPLICATION_RIGHTS_UNPUBLISH_DESCRIPTION">Autorise à dépublier un contenu</message>
application_en.xml
Oops!
Copy to clipboard failed. Open the code and copy it manually.
<message key="APPLICATION_RIGHTS_UNPUBLISH_LABEL">Unpublish</message>
<message key="APPLICATION_RIGHTS_UNPUBLISH_DESCRIPTION">Allow unpublish action on contents</message>
<message key="APPLICATION_RIGHTS_UNPUBLISH_LABEL">Unpublish</message>
<message key="APPLICATION_RIGHTS_UNPUBLISH_DESCRIPTION">Allow unpublish action on contents</message>
<message key="APPLICATION_RIGHTS_UNPUBLISH_LABEL">Unpublish</message>
<message key="APPLICATION_RIGHTS_UNPUBLISH_DESCRIPTION">Allow unpublish action on contents</message>
2) Add workflow action
Then add workflow action no. 10 for unpublishing a course in the file WEB-INF/param/workflow-program.xml The action is available in draft, proposed or validated states.
Oops!
Copy to clipboard failed. Open the code and copy it manually.
The button will be available if the contributor has the right to unpublish and the course has been validated at least once.
Don't forget to add the right to unpublish to your contributors!
Unpublish a training course from an entry device ODF-web
This section only applies to ODF Web entry systems, which publish their training offer on a portal. If you have a ODF non-web entry system, you must follow the instructions in the previous chapter.
The depublishing workflow action already exists on ODF-Web applications.
If courses are published on a portal, you simply need to add an instruction in the workflow file to remove a course from the portal, following its unpublication from the entry device.
To do this, in the workflow file WEB-INF/param/workflow-program.xml of the device, add the post-function org.ametys.odf.cdmfr.DeleteRemoteProgramFunction to depublication action n°10
Oops!
Copy to clipboard failed. Open the code and copy it manually.
From version 2.6.0 onwards, pedagogical elements must be part of a catalog, just like training courses.
In this way, catalog duplication also duplicates teaching elements (teaching elements are no longer shared between catalogs).
Add "catalog" metadata to ELPs
The ELPs (or UE) model must contain a "catalog" metadata. If you have overloaded the ELPs model (content type org.ametys.plugins.odf.Content.course), you must add this metadata.
If you're using the standard content type for ELPs, skip to the next section.
Oops!
Copy to clipboard failed. Open the code and copy it manually.
Please do not add the reference to this new metadata in the editing view. The catalog must not be modified from the editing form, as it requires verification.
Add a reference to this metadata in the "details", "index" and "main" views in "view" mode.
Oops!
Copy to clipboard failed. Open the code and copy it manually.
To be able to copy ELPs when duplicating a training catalog, modify the workflow file WEB-INF/param/workflow-course.xml to add the create-by-copying action in the <initial-actions>
Oops!
Copy to clipboard failed. Open the code and copy it manually.
Run the following script to automatically position the name of the belonging catalog on all ELPs. TheELP catalog is retrieved from the parent courses.
If a ELP is attached to several formations from different catalogs, an error message will be displayed. Migration must then be carried out manually. It will be necessary to delete theELP from one or more courses, so that theELP belongs only to courses in a single catalog. You can then run script again.
Oops!
Copy to clipboard failed. Open the code and copy it manually.
var qm = session.getWorkspace().getQueryManager();
var getCatalogs = function (clContainer)
{
var parentNode = clContainer;
var catalog = null;
while (parentNode != null && catalog == null)
{
if (parentNode.getPrimaryNodeType().getName() == 'ametys:courseContent')
{
var query = qm.createQuery("//element(*, ametys:courseList)[@ametys:coursesReferences = 'courseContent://" + parentNode.getUUID() + "']", javax.jcr.query.Query.XPATH);
var clNodes = query.execute().getNodes();
var catalogs = [];
while (clNodes.hasNext())
{
var clCatalogs = getCatalogs(clNodes.next().getParent());
for (var i=0; i<clCatalogs.length; i++)
{
if (catalogs.indexOf(clCatalogs[i]) == -1)
{
catalogs.push(clCatalogs[i]);
}
}
}
return catalogs;
}
else if (parentNode.getPrimaryNodeType().getName() == 'ametys:programContent')
{
catalog = parentNode.getProperty("ametys:catalog").getString() + '';
}
else
{
parentNode = parentNode.getParent();
}
}
return catalog != null ? [catalog] : [];
}
var processCourse = function (course)
{
if (!course.hasProperty("ametys:catalog"))
{
var query = qm.createQuery("//element(*, ametys:courseList)[@ametys:coursesReferences = 'courseContent://" + course.getUUID() + "']", javax.jcr.query.Query.XPATH);
var clNodes = query.execute().getNodes();
var catalog = null;
while (clNodes.hasNext())
{
var clCatalogs = getCatalogs(clNodes.next().getParent());
if (catalog == null && clCatalogs.length == 1)
{
catalog = clCatalogs[0];
}
else if (clCatalogs.length > 1 || clCatalogs[0] != catalog)
{
var title = course.getProperty("ametys:title").getString();
var code = course.getProperty("ametys:elpCode").getString();
print("ERROR >> L'ELP " + title + " (" + code + ") est référencée par plusieurs listes appartenant à des catalogues différents => migration impossible");
catalog = null;
break;
}
}
if (catalog != null)
{
course.setProperty("ametys:catalog", catalog);
course.save();
return true;
}
}
return false;
}
// Default workspace
var query = qm.createQuery("//element(*, ametys:courseContent)[not(@ametys:catalog)]", javax.jcr.query.Query.XPATH);
var nodes = query.execute().getNodes();
var count = 0;
while (nodes.hasNext())
{
if (processCourse(nodes.next()))
{
count++;
}
}
print(count + " ELPs ont été mises à jour dans le workspace 'default'");
var qm = session.getWorkspace().getQueryManager();
var getCatalogs = function (clContainer)
{
var parentNode = clContainer;
var catalog = null;
while (parentNode != null && catalog == null)
{
if (parentNode.getPrimaryNodeType().getName() == 'ametys:courseContent')
{
var query = qm.createQuery("//element(*, ametys:courseList)[@ametys:coursesReferences = 'courseContent://" + parentNode.getUUID() + "']", javax.jcr.query.Query.XPATH);
var clNodes = query.execute().getNodes();
var catalogs = [];
while (clNodes.hasNext())
{
var clCatalogs = getCatalogs(clNodes.next().getParent());
for (var i=0; i<clCatalogs.length; i++)
{
if (catalogs.indexOf(clCatalogs[i]) == -1)
{
catalogs.push(clCatalogs[i]);
}
}
}
return catalogs;
}
else if (parentNode.getPrimaryNodeType().getName() == 'ametys:programContent')
{
catalog = parentNode.getProperty("ametys:catalog").getString() + '';
}
else
{
parentNode = parentNode.getParent();
}
}
return catalog != null ? [catalog] : [];
}
var processCourse = function (course)
{
if (!course.hasProperty("ametys:catalog"))
{
var query = qm.createQuery("//element(*, ametys:courseList)[@ametys:coursesReferences = 'courseContent://" + course.getUUID() + "']", javax.jcr.query.Query.XPATH);
var clNodes = query.execute().getNodes();
var catalog = null;
while (clNodes.hasNext())
{
var clCatalogs = getCatalogs(clNodes.next().getParent());
if (catalog == null && clCatalogs.length == 1)
{
catalog = clCatalogs[0];
}
else if (clCatalogs.length > 1 || clCatalogs[0] != catalog)
{
var title = course.getProperty("ametys:title").getString();
var code = course.getProperty("ametys:elpCode").getString();
print("ERROR >> L'ELP " + title + " (" + code + ") est référencée par plusieurs listes appartenant à des catalogues différents => migration impossible");
catalog = null;
break;
}
}
if (catalog != null)
{
course.setProperty("ametys:catalog", catalog);
course.save();
return true;
}
}
return false;
}
// Default workspace
var query = qm.createQuery("//element(*, ametys:courseContent)[not(@ametys:catalog)]", javax.jcr.query.Query.XPATH);
var nodes = query.execute().getNodes();
var count = 0;
while (nodes.hasNext())
{
if (processCourse(nodes.next()))
{
count++;
}
}
print(count + " ELPs ont été mises à jour dans le workspace 'default'");
var qm = session.getWorkspace().getQueryManager();
var getCatalogs = function (clContainer)
{
var parentNode = clContainer;
var catalog = null;
while (parentNode != null && catalog == null)
{
if (parentNode.getPrimaryNodeType().getName() == 'ametys:courseContent')
{
var query = qm.createQuery("//element(*, ametys:courseList)[@ametys:coursesReferences = 'courseContent://" + parentNode.getUUID() + "']", javax.jcr.query.Query.XPATH);
var clNodes = query.execute().getNodes();
var catalogs = [];
while (clNodes.hasNext())
{
var clCatalogs = getCatalogs(clNodes.next().getParent());
for (var i=0; i<clCatalogs.length; i++)
{
if (catalogs.indexOf(clCatalogs[i]) == -1)
{
catalogs.push(clCatalogs[i]);
}
}
}
return catalogs;
}
else if (parentNode.getPrimaryNodeType().getName() == 'ametys:programContent')
{
catalog = parentNode.getProperty("ametys:catalog").getString() + '';
}
else
{
parentNode = parentNode.getParent();
}
}
return catalog != null ? [catalog] : [];
}
var processCourse = function (course)
{
if (!course.hasProperty("ametys:catalog"))
{
var query = qm.createQuery("//element(*, ametys:courseList)[@ametys:coursesReferences = 'courseContent://" + course.getUUID() + "']", javax.jcr.query.Query.XPATH);
var clNodes = query.execute().getNodes();
var catalog = null;
while (clNodes.hasNext())
{
var clCatalogs = getCatalogs(clNodes.next().getParent());
if (catalog == null && clCatalogs.length == 1)
{
catalog = clCatalogs[0];
}
else if (clCatalogs.length > 1 || clCatalogs[0] != catalog)
{
var title = course.getProperty("ametys:title").getString();
var code = course.getProperty("ametys:elpCode").getString();
print("ERROR >> L'ELP " + title + " (" + code + ") est référencée par plusieurs listes appartenant à des catalogues différents => migration impossible");
catalog = null;
break;
}
}
if (catalog != null)
{
course.setProperty("ametys:catalog", catalog);
course.save();
return true;
}
}
return false;
}
// Default workspace
var query = qm.createQuery("//element(*, ametys:courseContent)[not(@ametys:catalog)]", javax.jcr.query.Query.XPATH);
var nodes = query.execute().getNodes();
var count = 0;
while (nodes.hasNext())
{
if (processCourse(nodes.next()))
{
count++;
}
}
print(count + " ELPs ont été mises à jour dans le workspace 'default'");
After running script, go to the back office and check that all ELPs are in the "Validated" state. Validate all ELPs that are not in the validated state!
Then run the following script to correctly reflect the changes for the "Live" label.
Oops!
Copy to clipboard failed. Open the code and copy it manually.
var query = session.getWorkspace().getQueryManager().createQuery("//element(*, ametys:courseContent)", javax.jcr.query.Query.XPATH);
var nodes = query.execute().getNodes();
var count = 0;
while (nodes.hasNext())
{
var node = nodes.next();
node.checkin();
node.checkout();
var versionName = node.getBaseVersion().getName();
node.getVersionHistory().addVersionLabel(versionName, "Live", true);
count++;
}
print(count + " courses have been checkpoint");
var query = session.getWorkspace().getQueryManager().createQuery("//element(*, ametys:courseContent)", javax.jcr.query.Query.XPATH);
var nodes = query.execute().getNodes();
var count = 0;
while (nodes.hasNext())
{
var node = nodes.next();
node.checkin();
node.checkout();
var versionName = node.getBaseVersion().getName();
node.getVersionHistory().addVersionLabel(versionName, "Live", true);
count++;
}
print(count + " courses have been checkpoint");
var query = session.getWorkspace().getQueryManager().createQuery("//element(*, ametys:courseContent)", javax.jcr.query.Query.XPATH);
var nodes = query.execute().getNodes();
var count = 0;
while (nodes.hasNext())
{
var node = nodes.next();
node.checkin();
node.checkout();
var versionName = node.getBaseVersion().getName();
node.getVersionHistory().addVersionLabel(versionName, "Live", true);
count++;
}
print(count + " courses have been checkpoint");
Addition of the "Catalog" criterion and result column to the ELPs search tool
If you have overloaded the BO search model with educational elements, you need to add the "Catalog" search criterion and the result column.
If you're using the standard search engine, skip to the next section.
Criteria definition and search column
Oops!
Copy to clipboard failed. Open the code and copy it manually.
When importing a course from a XML CDM-fr feed, the following rules apply:
if the catalog is not present or empty in CDM-fr, theELP is directly assigned to the catalog of the imported or synchronized course.
if the catalog is non-empty,
and it is identical to that of the imported course, theELP is imported or synchronized.
and it is different from that of the imported course, an error is raised and theELP is neither imported nor synchronized.
Please note that if you are in "remote" (Portal) synchronization mode, and you have forced the import catalog in the configuration parameters, the value contained in CDM-fr will be ignored. Training courses and ELPs will automatically be assigned to the catalog defined in the configuration parameter.