/* * Copyright (C) 2003-2007 eXo Platform SAS. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see<http://www.gnu.org/licenses/>. */ package org.exoplatform.services.cms.templates.impl; import org.exoplatform.container.component.BaseComponentPlugin; import org.exoplatform.container.configuration.ConfigurationManager; import org.exoplatform.container.xml.InitParams; import org.exoplatform.container.xml.ObjectParameter; import org.exoplatform.container.xml.ValueParam; import org.exoplatform.services.cms.BasePath; import org.exoplatform.services.cms.impl.DMSConfiguration; import org.exoplatform.services.cms.impl.DMSRepositoryConfiguration; import org.exoplatform.services.cms.impl.Utils; import org.exoplatform.services.cms.templates.TemplateService; import org.exoplatform.services.jcr.RepositoryService; import org.exoplatform.services.jcr.core.ManageableRepository; import org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.services.wcm.utils.WCMCoreUtils; import javax.jcr.Node; import javax.jcr.PathNotFoundException; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.ValueFormatException; import javax.jcr.nodetype.NodeDefinition; import javax.jcr.nodetype.NodeType; import javax.jcr.nodetype.NodeTypeIterator; import javax.jcr.nodetype.NodeTypeManager; import javax.jcr.nodetype.PropertyDefinition; import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; public class TemplatePlugin extends BaseComponentPlugin { static final public String DIALOGS = "dialogs"; static final public String VIEWS = "views"; static final public String SKINS = "skins"; static final public String DEFAULT_DIALOG = "dialog1"; static final public String DEFAULT_VIEW = "view1"; static final String[] UNDELETABLE_TEMPLATES = { DEFAULT_DIALOG, DEFAULT_VIEW }; static final public String DEFAULT_DIALOGS_PATH = "/" + DIALOGS + "/" + DEFAULT_DIALOG; static final public String DEFAULT_VIEWS_PATH = "/" + VIEWS + "/" + DEFAULT_VIEW; static final public String NT_UNSTRUCTURED = "nt:unstructured"; static final public String DOCUMENT_TEMPLATE_PROP = "isDocumentTemplate"; static final public String TEMPLATE_LABEL = "label"; public static final String[] EXO_ROLES_DEFAULT = new String[] { "*" }; private static final String NAME; private static final String COMMENT_TEMPLATE; private static final String HEADER_VIEW; private static final String JAVA_HEADER_VIEW; private static final String DEF_FIELD_PROPERTY; private static final String FIELD_PROPERTY; private static final String START_DIALOG_FORM; private static final String END_DIALOG_FORM; private static final String TD_LABEL; private static final String TD_COMPONENT; private static final String START_TABLE; private static final String END_TABLE; private static final String START_TR; private static final String END_TR; private static final String CHECK_PROPERTY; private static final String GET_PROPERTY; private static final String START_JAVA; private static final String END_JAVA; private static final String DEFAULT_CSS; private static final String JCR_PRIMARY_TYPE = "jcr:primaryType"; private static final String JCR_MIXIN_TYPES = "jcr:mixinTypes"; private RepositoryService repositoryService_; private ConfigurationManager configManager_; private NodeHierarchyCreator nodeHierarchyCreator_; private String cmsTemplatesBasePath_ ; private InitParams params_ ; private String storedLocation_ ; private boolean autoCreateInNewRepository_=false; private static final Log LOG = ExoLogger.getLogger(TemplatePlugin.class.getName()); private Set<String> configuredNodeTypes; private TemplateService templateService; static { COMMENT_TEMPLATE = "<%\n// Generate template for nodetype automatically\n%>\n"; HEADER_VIEW = new StringBuilder("<style>\n") .append("\t<% _ctx.include(uicomponent.getTemplateSkin(\"${NodeType}\", \"Stylesheet\")); %>\n") .append("</style>\n").toString(); JAVA_HEADER_VIEW = new StringBuilder("<%\n\tdef node = uicomponent.getNode();\n") .append("\tdef name = node.getName();\n") .append("\tdef values;\n") .append("\tdef valueDisplay;\n%>").toString(); DEF_FIELD_PROPERTY = "\t\t\t\t\t<%\n\t\t\t\t\t\tString[] fieldProperty; \n\t\t\t\t\t%>\n"; FIELD_PROPERTY = "fieldProperty"; START_DIALOG_FORM = new StringBuilder("<div class=\"UIForm FormLayout FormScrollLayout\">\n") .append("\t<% uiform.begin();\n") .append("\t /* start render action*/\n") .append("\t if (uiform.isShowActionsOnTop()) uiform.processRenderAction();\n") .append("\t /* end render action*/\n") .append("\t%> \n") .append("\t\t<div class=\"HorizontalLayout\">\n").toString(); NAME = new StringBuilder("\n\t\t\t\t\t<%\n") .append("\t\t\t\t\t\tString[] fieldName = [\"jcrPath=/node\", \"editable=if-null\", \"validate=empty,name\"];\n") .append("\t\t\t\t\t\tuicomponent.addTextField(\"name\", fieldName);\n") .append("\t\t\t\t\t%>\n").toString(); START_TABLE = "\n\t\t\t<table class=\"UIFormGrid\">\n"; START_TR = "\n\t\t\t\t<tr>\n"; END_TR = "\t\t\t\t</tr>"; START_JAVA = "\n\t\t\t\t<%\n"; END_JAVA = "\n\t\t\t\t%>"; CHECK_PROPERTY = "\t\t\t\t if (node.hasProperty(\"${propertyname}\")) {"; GET_PROPERTY = "node.getProperty(\"${propertyname}\")"; TD_LABEL = new StringBuilder("").append("\t\t\t\t\t<td class=\"FieldLabel\">") .append("<%=_ctx.appRes(\"${nodetypename}.dialog.label.${propertyname}\")%>") .append("</td>").toString(); TD_COMPONENT = new StringBuilder("\n").append("\t\t\t\t\t<td class=\"FieldComponent\">") .append("${contentcomponent}").append("\t\t\t\t\t</td>\n").toString(); END_TABLE = "\n\t\t\t</table>\n"; END_DIALOG_FORM = new StringBuilder("").append("\t\t</div>\n") .append("\t<% /* start render action*/\n") .append("\t if (!uiform.isShowActionsOnTop()) uiform.processRenderAction();\n") .append("\t /* end render action*/\n") .append("\t uiform.end();\n\t%>\n").append("</div>").toString(); DEFAULT_CSS = new StringBuilder(".UIFormGrid {") .append("\n\tborder:1px solid #B7B7B7;") .append("\n\tborder-collapse:collapse;") .append("\n\tmargin:auto;") .append("\n\tpadding-left:1px;") .append("\n\ttable-layout:fixed;") .append("\n}") .append("\n\n.UIFormGrid .FieldLabel {") .append("\n\tfont-weight:bold;") .append("\n\twidth:auto;") .append("\n}") .append("\n\n.UIFormGrid td {") .append("\n\tborder-left:1px solid #CCCCCC;") .append("\n\tborder-right:1px solid #CCCCCC;") .append("\n\tborder-top:1px solid #FFFFFF;") .append("\n\tbackground:#F3F3F3 none repeat scroll 0 0;") .append("\n\theight:20px;") .append("\n\tline-height:20px;") .append("\n\tpadding:4px;") .append("\n}").toString(); } /** * DMS configuration which used to store informations */ private DMSConfiguration dmsConfiguration_; public TemplatePlugin(InitParams params, RepositoryService jcrService, ConfigurationManager configManager, NodeHierarchyCreator nodeHierarchyCreator, DMSConfiguration dmsConfiguration) throws Exception { nodeHierarchyCreator_ = nodeHierarchyCreator; repositoryService_ = jcrService; configManager_ = configManager; cmsTemplatesBasePath_ = nodeHierarchyCreator_.getJcrPath(BasePath.CMS_TEMPLATES_PATH); params_ = params; ValueParam locationParam = params_.getValueParam("storedLocation") ; storedLocation_ = locationParam.getValue(); ValueParam param = params_.getValueParam("autoCreateInNewRepository"); if(param!=null) { autoCreateInNewRepository_ = Boolean.parseBoolean(param.getValue()); } dmsConfiguration_ = dmsConfiguration; templateService = WCMCoreUtils.getService(TemplateService.class); } public void init() throws Exception { configuredNodeTypes = new HashSet<String>(); importPredefineTemplates() ; } @SuppressWarnings("rawtypes") private void addTemplate(TemplateConfig templateConfig, Node templatesHome, String storedLocation) throws Exception { Set<String> editedPredefinedNodeTypes = templateService.getAllEditedConfiguredNodeTypes(); NodeTypeManager ntManager = templatesHome.getSession().getWorkspace().getNodeTypeManager() ; NodeTypeIterator nodetypeIter = ntManager.getAllNodeTypes(); List<String> listNodeTypeName = new ArrayList<String>(); while (nodetypeIter.hasNext()) { NodeType n1 = nodetypeIter.nextNodeType(); listNodeTypeName.add(n1.getName()); } List nodetypes = templateConfig.getNodeTypes(); TemplateConfig.NodeType nodeType = null ; Iterator iter = nodetypes.iterator() ; while(iter.hasNext()) { nodeType = (TemplateConfig.NodeType) iter.next(); if(Utils.getAllEditedConfiguredData( "ContentTemplateList", "EditedConfiguredTemplateList", true).contains(nodeType.getNodetypeName()) || !listNodeTypeName.contains(nodeType.getNodetypeName())) { if (LOG.isErrorEnabled()) { LOG.error("The nodetype: " + nodeType.getNodetypeName() + " or its templates doesn't exist!"); } continue; } configuredNodeTypes.add(nodeType.getNodetypeName()); // Do not update predefined templates which was edited by user if (editedPredefinedNodeTypes.contains(nodeType.getNodetypeName())) continue; Node nodeTypeHome = null; nodeTypeHome = Utils.makePath(templatesHome, nodeType.getNodetypeName(), NT_UNSTRUCTURED); if(nodeType.getDocumentTemplate()) nodeTypeHome.setProperty(DOCUMENT_TEMPLATE_PROP, true) ; else nodeTypeHome.setProperty(DOCUMENT_TEMPLATE_PROP, false) ; nodeTypeHome.setProperty(TEMPLATE_LABEL, nodeType.getLabel()) ; List dialogs = nodeType.getReferencedDialog(); addNode(storedLocation, nodeType, dialogs, DIALOGS, templatesHome); List views = nodeType.getReferencedView(); addNode(storedLocation, nodeType, views, VIEWS, templatesHome); List skins = nodeType.getReferencedSkin(); if(skins != null) { addNode(storedLocation, nodeType, skins, SKINS, templatesHome); } } } public void setBasePath(String basePath) { cmsTemplatesBasePath_ = basePath ; } @SuppressWarnings("unchecked") private void importPredefineTemplates() throws Exception { ManageableRepository repository = repositoryService_.getCurrentRepository(); DMSRepositoryConfiguration dmsRepoConfig = dmsConfiguration_.getConfig(); String workspace = dmsRepoConfig.getSystemWorkspace(); Session session = repository.getSystemSession(workspace) ; Node templatesHome = Utils.makePath(session.getRootNode(), cmsTemplatesBasePath_, NT_UNSTRUCTURED); TemplateConfig templateConfig = null ; Iterator<ObjectParameter> iter = params_.getObjectParamIterator() ; while(iter.hasNext()) { Object object = iter.next().getObject() ; if(!(object instanceof TemplateConfig)) { break ; } templateConfig = (TemplateConfig)object ; addTemplate(templateConfig,templatesHome,storedLocation_) ; } session.logout(); } @SuppressWarnings("rawtypes") private void addNode(String basePath, TemplateConfig.NodeType nodeType, List templates, String templateType, Node templatesHome) throws Exception { for (Iterator iterator = templates.iterator(); iterator.hasNext();) { TemplateConfig.Template template = (TemplateConfig.Template) iterator.next(); String templateFileName = template.getTemplateFile(); String path = basePath + templateFileName; InputStream in = configManager_.getInputStream(path); String nodeName = templateFileName.substring(templateFileName.lastIndexOf("/") + 1, templateFileName.indexOf(".")); Node nodeTypeHome = null; if (!templatesHome.hasNode(nodeType.getNodetypeName())) { nodeTypeHome = Utils.makePath(templatesHome, nodeType.getNodetypeName(), NT_UNSTRUCTURED); } else { nodeTypeHome = templatesHome.getNode(nodeType.getNodetypeName()); } Node specifiedTemplatesHome = null; try { specifiedTemplatesHome = nodeTypeHome.getNode(templateType); } catch(PathNotFoundException e) { specifiedTemplatesHome = Utils.makePath(nodeTypeHome, templateType, NT_UNSTRUCTURED); } if(!specifiedTemplatesHome.hasNode(nodeName)) { templateService.addTemplate(templateType, nodeType.getNodetypeName(), nodeType.getLabel(), nodeType.getDocumentTemplate(), nodeName, template.getParsedRoles(), in, templatesHome); } } } /** * Build default style sheet * @param nodeType * @return */ public String buildStyleSheet(NodeType nodeType) { return COMMENT_TEMPLATE.concat(DEFAULT_CSS); } /** * Build string of dialog form template base on properties of nodetype * @param nodeType * @return */ public String buildDialogForm(NodeType nodeType) throws ValueFormatException, RepositoryException { StringBuilder buildDialogForm = new StringBuilder(COMMENT_TEMPLATE); buildDialogForm.append(START_DIALOG_FORM).append(START_TABLE); buildDialogForm.append(START_TR); buildDialogForm.append(DEF_FIELD_PROPERTY); buildDialogForm.append(TD_LABEL.replace("${nodetypename}", nodeType.getName()) .replace(":", "_") .replace("${propertyname}", "name")); buildDialogForm.append(TD_COMPONENT.replace("${contentcomponent}", NAME)); buildDialogForm.append(END_TR); buildDialogForm.append(buildDialogNodeType(nodeType)); buildDialogForm.append(END_TABLE); buildDialogForm.append(END_DIALOG_FORM); return buildDialogForm.toString(); } /** * Build string of dialog template base on properties of nodetype * @param nodeType * @return */ private String buildDialogNodeType(NodeType nodeType) throws ValueFormatException, RepositoryException { return buildDialogNodeType(nodeType, "/node/"); } /** * Build string of dialog template base on properties of nodetype * @param nodeType * @return */ private String buildDialogNodeType(NodeType nodeType, String jcrPath) throws ValueFormatException, RepositoryException { StringBuilder buildDialogNodeType = new StringBuilder(); StringBuilder componentField; String propertyNameFormat; String propertyPath; String propertyId; StringBuilder params; StringBuilder validate; StringBuilder defaultValues; Value[] defaultValuesArr; // Render all property defined in NodeType PropertyDefinition[] prodefs = nodeType.getPropertyDefinitions(); for (PropertyDefinition prodef : prodefs) { // Dismiss all auto created property String propertyName = prodef.getName(); if (prodef.isAutoCreated() || "*".equals(propertyName) || JCR_PRIMARY_TYPE.equals(propertyName) || JCR_MIXIN_TYPES.equals(propertyName)) continue; propertyNameFormat = propertyName.replace(":", "_"); propertyPath = jcrPath.concat(propertyName); propertyId = propertyPath.replace(":", "_"); componentField = new StringBuilder("\n\t\t\t\t\t\tuicomponent.addTextField(\"").append(propertyId) .append("\", "); validate = new StringBuilder("validate="); buildDialogNodeType.append(START_TR); buildDialogNodeType.append(TD_LABEL.replace("${nodetypename}", nodeType.getName()) .replace(":", "_") .replace("${propertyname}", propertyNameFormat)); params = new StringBuilder("\t\t\t\t\t\t").append(FIELD_PROPERTY) .append(" = [\"jcrPath=") .append(propertyPath) .append("\""); if (prodef.isMultiple()) { params.append(", \"multiValues=true\""); } if (prodef.isMandatory()) { validate.append("empty,"); } // Select component field base on required type switch (prodef.getRequiredType()) { case PropertyType.BOOLEAN : params.append(", \"options=true,false\""); componentField = new StringBuilder("\n\t\t\t\t\t\tuicomponent.addSelectBoxField(\"").append(propertyId) .append("\", "); break; case PropertyType.STRING : break; case PropertyType.DATE : validate.append("datetime,"); params.append(", \"options=displaytime\", \"visible=true\""); componentField = new StringBuilder("\n\t\t\t\t\t\tuicomponent.addCalendarField(\"").append(propertyId) .append("\", "); break; case PropertyType.LONG : validate.append("number,"); break; case PropertyType.DOUBLE : validate.append("number,"); break; case PropertyType.REFERENCE : params.append(", \"reference=true\", \"editable=false\""); break; case PropertyType.BINARY : componentField = new StringBuilder("\n\t\t\t\t\t\tuicomponent.addUploadField(\"").append(propertyId) .append("\", "); break; default: break; } defaultValuesArr = prodef.getDefaultValues(); if (defaultValuesArr != null) { defaultValues = new StringBuilder("defaultValues="); for(Value value : defaultValuesArr) { defaultValues.append(value.getString()).append(","); } if (defaultValues.indexOf(",") > -1) { params.append(", \"").append(defaultValues.deleteCharAt(defaultValues.length() - 1)).append("\""); } } if (validate.indexOf(",") > -1) { params.append(", \"").append(validate.deleteCharAt(validate.length() - 1)).append("\""); } params.append("];"); componentField.append(FIELD_PROPERTY).append(");"); buildDialogNodeType.append(TD_COMPONENT.replace("${contentcomponent}", START_JAVA.concat(params.append(componentField) .append(END_JAVA) .append("\n") .toString()))); buildDialogNodeType.append(END_TR); } // Render all property for all Child Node if have by call recursive NodeDefinition[] childdefs = nodeType.getChildNodeDefinitions(); for (NodeDefinition childdef : childdefs) { if (childdef != null) { for (NodeType requiredNodeType : childdef.getRequiredPrimaryTypes()) { if (childdef.getName().equals("*")) { jcrPath = jcrPath.concat(childdef.getRequiredPrimaryTypes()[0].getName()).concat("/"); } else { jcrPath = jcrPath.concat(childdef.getName()).concat("/"); } buildDialogNodeType.append(buildDialogNodeType(requiredNodeType, jcrPath)); } } } return buildDialogNodeType.toString(); } /** * Build string of view template form base on properties of nodetype * @param nodeType * @return */ public String buildViewForm(NodeType nodeType) { StringBuilder buildViewForm = new StringBuilder(COMMENT_TEMPLATE); buildViewForm.append(HEADER_VIEW.replace("${NodeType}", nodeType.getName())); buildViewForm.append(JAVA_HEADER_VIEW); buildViewForm.append("\n\t\t<div id=\"$uicomponent.id\">"); buildViewForm.append(START_TABLE); buildViewForm.append(START_TR); buildViewForm.append(TD_LABEL.replace("${nodetypename}", nodeType.getName()) .replace(":", "_") .replace("${propertyname}", "name")); buildViewForm.append(TD_COMPONENT.replace("${contentcomponent}\t\t\t\t\t", "${name}")); buildViewForm.append(END_TR); buildViewForm.append(buildViewNodeType(nodeType)); buildViewForm.append(END_TABLE); buildViewForm.append("\t\t</div>"); return buildViewForm.toString(); } /** * Build string of view template base on properties of nodetype * @param nodeType * @return */ private String buildViewNodeType(NodeType nodeType) { StringBuilder buildViewNodeType = new StringBuilder(); String label = TD_LABEL.replace("${nodetypename}", nodeType.getName().replace(":", "_")); PropertyDefinition[] prodefs = nodeType.getPropertyDefinitions(); for (PropertyDefinition prodef : prodefs) { buildViewNodeType.append(START_JAVA).append(CHECK_PROPERTY.replace("${propertyname}", prodef.getName())).append(END_JAVA); buildViewNodeType.append(START_TR); buildViewNodeType.append(label.replace("${propertyname}", prodef.getName().replace(":", "_"))); buildViewNodeType.append(START_JAVA); if (prodef.getRequiredType() == PropertyType.BINARY) { if (prodef.isMultiple()) { buildViewNodeType.append("\t\t\t\t\t// Render for multi value;\n"); buildViewNodeType.append("\t\t\t\t\tvalues = ") .append(GET_PROPERTY.replace("${propertyname}", prodef.getName())) .append(".getValues()") .append(";\n"); buildViewNodeType.append("\t\t\t\t\tvalueDisplay = \"\";\n"); buildViewNodeType.append("\t\t\t\t\tfor(value in values) {\n" ); buildViewNodeType.append("\t\t\t\t\t\tvalueDisplay += \"BINARY DATA\" + \",\";\n" ); buildViewNodeType.append("\t\t\t\t\t}\n" ); buildViewNodeType.append("\t\t\t\t\tif (valueDisplay.length() > 0 && valueDisplay.indexOf(\",\") > -1) " + "valueDisplay = valueDisplay.substring(0, valueDisplay.length() - 1);"); } else { buildViewNodeType.append("\t\t\t\t\t// Render for single value;\n"); buildViewNodeType.append("\t\t\t\t\tvalueDisplay = \"BINARY DATA\""); } } else { if (prodef.isMultiple()) { buildViewNodeType.append("\t\t\t\t\t// Render for multi value;\n"); buildViewNodeType.append("\t\t\t\t\tvalues = ") .append(GET_PROPERTY.replace("${propertyname}", prodef.getName())) .append(".getValues()") .append(";\n"); buildViewNodeType.append("\t\t\t\t\tvalueDisplay = \"\";\n"); buildViewNodeType.append("\t\t\t\t\tfor(value in values) {\n" ); buildViewNodeType.append("\t\t\t\t\t\tvalueDisplay += value.getString() + \",\";\n" ); buildViewNodeType.append("\t\t\t\t\t}\n" ); buildViewNodeType.append("\t\t\t\t\tif (valueDisplay.length() > 0 && valueDisplay.indexOf(\",\") > -1) " + "valueDisplay = valueDisplay.substring(0, valueDisplay.length() - 1);"); } else { buildViewNodeType.append("\t\t\t\t\t// Render for single value;\n"); buildViewNodeType.append("\t\t\t\t\tvalueDisplay = ") .append(GET_PROPERTY.replace("${propertyname}", prodef.getName())) .append(".getString();"); } } buildViewNodeType.append(END_JAVA); buildViewNodeType.append(TD_COMPONENT.replace("${contentcomponent}\t\t\t\t\t", "${valueDisplay}")); buildViewNodeType.append(END_TR); buildViewNodeType.append(START_JAVA).append("\t\t\t\t\t}").append(END_JAVA); } return buildViewNodeType.toString(); } public Set<String> getAllConfiguredNodeTypes() { return configuredNodeTypes; } }