Each document type is represented by a node type in the JCR. Therefore, to create a new document type, you have to add a new node type. There are two ways to do this: through XML configuration files, or through the administration portlet.
To learn more about how to use the administration portlet, refer to the Administration Guide.
Otherwise, you can create a new document type by configuring the file /nodetypes-configuration.xml in your extension.
<nodeType name="exo:newnodetype" isMixin="true" hasOrderableChildNodes="false" primaryItemName=""> <supertypes> <supertype>exo:article</supertype> </supertypes> <propertyDefinitions> <propertyDefinition name="text" requiredType="String" autoCreated="true" mandatory="true" onParentVersion="COPY" protected="false" multiple="false"> <valueConstraints/> </propertyDefinition> <propertyDefinition name="date" requiredType="Date" autoCreated="false" mandatory="true" onParentVersion="COPY" protected="false" multiple="false"> <valueConstraints/> </propertyDefinition> </propertyDefinitions> </nodeType>
By defining a supertype, you can reuse other node types and extend them with more properties (just like inheritance in Object Oriented Programming).
The templates are applied to a node type or a metadata mixin type. There are two types of templates, including:
dialogs: are html forms that enable you to create node instances.
views: are html fragments used to display nodes.
From the ECM admin portlet, Manage Template lists all existing node types that have been associated to Dialog and/or View templates. These templates can be attached to permissions (in the usual membership:group form), so that a specific one is displayed according to user rights (which can be useful in a content validation workflow activity).
In eXo Platform, you can specify whether JavaScript is allowed to run on a field of the content template or not by using the "option" parameter.
To enable JavaScript to work with a content template, do as follows:
1. Go to My Groups --> Content Administration --> Content Presentation --> Manage Templates.
2. Edit your desired template.
3. Select the Dialog tab, and then edit the content of dialog1 in the View & Edit Template form.
4. Add option = noSanitization to the code in the Content field as the example below:
String[] htmlArguments = ["jcrPath=/node/default.html/jcr:content/jcr:data", "options=toolbar:CompleteWCM,height:'410px',noSanitization", htmlContent];
By default, JavaScript is disabled for any fields of some content templates to prevent the XSS attacks.
The checkbox 'Document Type' is to define if the node type should be a Document Type or not. If this checkbox is selected, the Site Explorer considers such nodes as user content and applies the following behavior:
The View template will be used to display the DocumentType nodes.
The document types nodes can be created by the Add Document action. The forms shown in the Add Document action are the corresponding Dialog templates of each document type.
Non-document types are hidden unless the 'Show non document types' option is checked.
Templates are written using Groovy Templates and will require some experiences with JCR API and HTML notions.
Dialogs are Groovy Templates that generate forms by mixing static HTML fragments and Groovy calls to the components responsible for building the UI at runtime. As a result, you will get a simple but powerful syntax.
By placing interceptors in your template, you will be able to execute a Groovy script just before and just after saving the node. Pre-save interceptors are mostly used to validate input values and their overall meaning while the post-save interceptor can be used to do some manipulations or references for the newly created node, such as binding it with a forum discussion or wiki space.
To place interceptors, use the following fragment:
<% uicomponent.addInterceptor("ecm-explorer/interceptor/PreNodeSaveInterceptor.groovy", "prev");%>
Interceptor Groovy scripts are managed in the 'Manage Script' section in the ECM admin portlet. They must implement the CmsScript interface. Pre-save interceptors obtain input values within the context:
public class PreNodeSaveInterceptor implements CmsScript {
public PreNodeSaveInterceptor() {
}
public void execute(Object context) {
Map inputValues = (Map) context;
Set keys = inputValues.keySet();
for(String key : keys) {
JcrInputProperty prop = (JcrInputProperty) inputValues.get(key);
println(" --> "+prop.getJcrPath());
}
}
public void setParams(String[] params) {}
}
Whereas the post-save interceptor are passed the path of the saved node in the context:
<% uicomponent.addInterceptor("ecm-explorer/interceptor/PostNodeSaveInterceptor.groovy", "post");%>
public class PostNodeSaveInterceptor implements CmsScript {
public PostNodeSaveInterceptor() {
}
public void execute(Object context) {
String path = (String) context;
println("Post node save interceptor, created node: "+path);
}
public void setParams(String[] params) {}
In the next code sample, each argument is composed of a set of keys and values. The order of arguments are not important and only the key matters. That example defines a field with the id as "hiddenField2", which will generate a hidden field. The value of this field will be automatically set to UTF-8 and no visible field will be printed on the form.
String[] hiddenField2 = ["jcrPath=/node/jcr:content/jcr:encoding", "visible=false", "UTF-8"];
uicomponent.addHiddenField("hiddenInput2", hiddenField2);
Once the form has been saved, the date value will be saved under the relative JCR path ./exo:image/jcr:lastModified.
You cannot either see the non-value field on the form or input value for them. Its value will be automatically created or defined when you are managing templates.
String[] hiddenField1 = ["jcrPath=/node/jcr:content", "nodetype=nt:resource", "mixintype=dc:elementSet", "visible=false"] ;
uicomponent.addHiddenField("hiddenInput1", hiddenField1) ;
It is possible to create widgets that are non-editable (and then only used to print some information).
String[] fieldCategories = ["jcrPath=/node/exo:category", "multiValues=true", "reference=true", "editable=false"]; uicomponent.addTextField("categories", fieldCategories);
In many cases, when creating an instance where the node is out of form, you must still specify the CMS service about the node structure. In other words, you must define if which node type is child of the newly created node or if the current node has any mixin type attributed.
By defining these arguments, the node and its children are created with the correct node type and mixin type.
See the following example:
String[] hiddenField = ["jcrPath=/node/jcrcontent", "nodetype=nt:resource", "mixintype=exo:rss-enable", "visible=false"] ;
uicomponent.addHiddenField("hiddenInput", hiddenField) ;
In the previous sample, the value was automatically created and set according to the current date. However, it is also possible to set a default value for a field.
String[] hiddenField = ["jcrPath=/node/jcrcontent/jcr:mimeType", "image/jpeg"] ;
uicomponent.addHiddenField("hiddenInput", hiddenField) ;
It is possible to tell that a widget should be visible only if its value is not null or when the form is used to edit the node which has been existing.
String nameArgs[] = ["jcrPath=/node", "mixintype=mix:votable", "visible=if-not-null"];
uicomponent.addMixinField("name", nameArgs )
Widgets are natively part of the Platform product to provide a simple and easy way for users to get information and notification on their application. They complete the portlet application that focuses on more transactional behaviors.
The "What You See Is What You Get" widget is one of the most powerful tools. It renders an advanced javascript text editor with many functionalities, including the ability to dynamically upload images or flash assets into a JCR workspace and then to refer to them from the created HTML text.
String[] fieldSummary = ["jcrPath=/node/exo:summary", "options=basic"] ;
uicomponent.addWYSIWYGField("summary", fieldSummary) ;
String[] fieldContent = ["jcrPath=/node/exo:text", "options=toolbar:CompleteWCM,'height:410px'", ""] ;
uicomponent.addRichtextField("content", fieldContent)
The "options" argument is used to tell the component which toolbar should be used.
By default, there are three options for the toolbar:
basic: a minimal set of tools is shown.
default: a large set of tools is shown, no "options" argument is needed in that case.
CompleteWCM: a full set of tools is shown.
There is also a simple text area widget, which has text-input area only:
String [] descriptionArgs = ["jcrPath=/node/exo:title", "validate=empty"];
uicomponent.addTextAreaField("description", descriptionArgs) ;
The select box widget enables you to render a select box with static values. These values are enumerated in a comma-separated list in the "options" argument. The argument with no key (here "text/html") is selected by default.
String[] mimetype = ["jcrPath=/node/jcrcontent/jcr:mimeType", "text/html", "options=text/html,text/plain"] ;
uicomponent.addSelectBoxField("mimetype", mimetype) ;
As usual, the value will be stored at the relative path defined by the jcrPath directive argument.
For more examples on how to create WCM templates, refer to Reference Guide.
In many cases, the previous solution with static options is not good enough and one would like to have the select box checked dynamically. That is what we provide thanks to the introduction of a Groovy script as shown in the code fragment below.
String[] args = ["jcrPath=/node/exodestWorkspace", "script=ecm-explorer/widget/FillSelectBoxWithWorkspaces:groovy", "scriptParams=production"];
uicomponent.addSelectBoxField("destWorkspace", args) ;
The script itself implements the CMS Script interface and the cast is done to get the select box object as shown in the script code which fills the select box with the existing JCR workspaces.
import java.util.List ;
import java.util.ArrayList ;
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.webui.form.UIFormSelectBox;
import org.exoplatform.webui.core.model.SelectItemOption;
import org.exoplatform.services.cms.scripts.CmsScript;
public class FillSelectBoxWithWorkspaces implements CmsScript {
private RepositoryService repositoryService_;
public FillSelectBoxWithWorkspaces(RepositoryService repositoryService) {
repositoryService_ = repositoryService;
}
public void execute(Object context) {
UIFormSelectBox selectBox = (UIFormSelectBox) context;
ManageableRepository jcrRepository = repositoryService_.getRepository();
List options = new ArrayList();
String[] workspaceNames = jcrRepository.getWorkspaceNames();
for(name in workspaceNames) {
options.add(new SelectItem(name, name));
}
selectBox.setOptions(options);
}
public void setParams(String[] params) {}
It is also possible to provide a parameter to the script by using the argument "scriptParams".
One of the most advanced functionalities of this syntax is the ability to plug your own component that shows an interface, enabling you to choose the value of the field.
In the generated form, you will see an icon which is configurable thanks to the selectorIcon argument. The syntax is a bit more complex but not much.
String[] groupArgs = ["jcrPath=/node/exogroup",
"selectorClass=org:exoplatform:ecm:webui:selector:UIGroupMemberSelector"];
uicomponent.addActionField("group", groupArgs);
You can plug your own component using the selectorClass argument. It must follow the eXo UIComponent mechanism and implements the interface ComponentSelector:
package org.exoplatform.ecm.webui.selector;
import org.exoplatform.webui.core.UIComponent;
public interface ComponentSelector {
public UIComponent getSourceComponent();
public void setSourceComponent(UIComponent uicomponent, String[] initParams) ;
}
A widget can have multiple values if you add the argument "multiValues=true" to the directive.
Manage template service
The Template service enables you to create dialogs and view templates for each node type registered. Each node type may have many dialogs and view templates. The template will be used when creating or viewing nodes.
.../webapps/portal/WEB-INF/conf/ecm/ecm-templates-configuration.xml
<component>
<key>org.exoplatform.services.cms.templates.TemplateService</key>
<type>org.exoplatform.services.cms.templates.impl.TemplateServiceImpl</type>
..............
</component>
As usual, one can register a plugin inside the service. This plugin initializes default dialogs and views template of any node type as nt:file, exo:article, exo:workflowAction, exo:sendMailAction, and more.
<component-plugins>
<component-plugin>
<name>addTemplates</name>
<set-method>addTemplates</set-method>
<type>org.exoplatform.services.cms.templates.impl.TemplatePlugin</type>
.........
</component-plugin>
</component-plugins>
With init-parameters as:
<init-params> <value-param> <name>autoCreateInNewRepository</name> <value>true</value> </value-param> <value-param> <name>storedLocation</name> <value>war:/conf/ecm/artifacts/templates</value> </value-param> <value-param> <name>repository</name> <value>repository</value> </value-param> <object-param> <name>template.configuration</name> <description>configuration for the localtion of templates to inject in jcr</description> <object type="org.exoplatform.services.cms.templates.impl.TemplateConfig"> <field name="nodeTypes"> <collection type="java.util.ArrayList"> <value> <object type="org.exoplatform.services.cms.templates.impl.TemplateConfig$NodeType"> <field name="nodetypeName"><string>exo:article</string></field> <field name="documentTemplate"><boolean>true</boolean></field> <field name="label"><string>Article</string></field> <field name="referencedView"> <collection type="java.util.ArrayList"> <value> <object type="org.exoplatform.services.cms.templates.impl.TemplateConfig$Template"> <field name="templateFile"><string>/article/views/view1.gtmpl</string></field> <field name="roles"><string>*</string></field> </object> </value> </collection> </field> <field name="referencedDialog"> <collection type="java.util.ArrayList"> <value> <object type="org.exoplatform.services.cms.templates.impl.TemplateConfig$Template"> <field name="templateFile"><string>/article/dialogs/dialog1.gtmpl</string></field> <field name="roles"><string>*</string></field> </object> </value> </collection> </field> </object> </value> </collection> </field> </object> </object-param> </init-params>
Taxonomy is a particular classification arranged in a hierarchical structure. Taxonomy trees in eXo Platform will help you organize your content into categories.
When you create a new taxonomy tree, you will add a pre-configured exo:action (exo:scriptAction or exo:businessProcessAction) to the root node of the taxonomy tree. This action is triggered when a new document is added anywhere in the taxonomy tree. The default action moves the document to the physical storage location and replaces the document in the taxonomy tree with a symlink of type exo:taxonomyLink that points to it. The physical storage location is defined by a workspace name, a path and the current date and time.
Like adding document types, taxonomy trees can be managed through the Administration portlet, or by adding XML files.
To configure taxonomy trees by adding configuration files in the /webapp/WEB-INF/conf/acme-portal/wcm/taxonomy/ directory, create a new file called $taxonomyName-taxonomies-configuration.xml. For example, if the name of your taxonomy tree is "acme", the file should be named acme-taxonomies-configuration.xml.
You can view the file here: $PLF-HOME/samples/acme-website/webapp/src/main/webapp/WEB-INF/conf/acme-portal/wcm/taxonomy/acme-taxonomies-configuration.xml.
As you can see, the value-params enable you to define the repository, workspace, name of the tree and its JCR path. You can then configure permissions for each group of users in the portal, and the triggered action when a new document is added to the taxonomy tree. Finally, you can describe the structure and names of the categories inside your taxonomy tree.