diff --git a/config/alfresco/rendition-services-context.xml b/config/alfresco/rendition-services-context.xml index 8620dee4f5..ce6cb81787 100644 --- a/config/alfresco/rendition-services-context.xml +++ b/config/alfresco/rendition-services-context.xml @@ -131,6 +131,12 @@ + + + + diff --git a/source/java/org/alfresco/repo/rendition/StandardRenditionLocationResolverImpl.java b/source/java/org/alfresco/repo/rendition/StandardRenditionLocationResolverImpl.java index 9774f22fe1..36b7932c3e 100644 --- a/source/java/org/alfresco/repo/rendition/StandardRenditionLocationResolverImpl.java +++ b/source/java/org/alfresco/repo/rendition/StandardRenditionLocationResolverImpl.java @@ -79,7 +79,7 @@ public class StandardRenditionLocationResolverImpl implements RenditionLocationR */ public RenditionLocation getRenditionLocation(NodeRef sourceNode, RenditionDefinition definition, NodeRef tempRenditionLocation) { - // If a destination NodeRef is specified then don't botther to find the location as one has already been specified. + // If a destination NodeRef is specified then don't bother to find the location as one has already been specified. NodeRef destination = AbstractRenderingEngine.getCheckedParam(RenditionService.PARAM_DESTINATION_NODE, NodeRef.class, definition); if(destination!=null) { diff --git a/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java b/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java index 05e0dd814b..89f84db4a9 100644 --- a/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java +++ b/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java @@ -1054,7 +1054,7 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase * Given a rendition definition, a source node and a temporary rendition node, this method uses a * {@link RenditionLocationResolver} to calculate the {@link RenditionLocation} of the rendition. */ - private RenditionLocation resolveRenditionLocation(NodeRef sourceNode, RenditionDefinition definition, + protected RenditionLocation resolveRenditionLocation(NodeRef sourceNode, RenditionDefinition definition, NodeRef tempRendition) { return renditionLocationResolver.getRenditionLocation(sourceNode, definition, tempRendition); diff --git a/source/java/org/alfresco/repo/rendition/executer/HTMLRenderingEngine.java b/source/java/org/alfresco/repo/rendition/executer/HTMLRenderingEngine.java new file mode 100644 index 0000000000..dfb7243187 --- /dev/null +++ b/source/java/org/alfresco/repo/rendition/executer/HTMLRenderingEngine.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.rendition.executer; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.repo.rendition.RenditionLocation; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.rendition.RenditionServiceException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This class provides a way to turn documents supported by the + * {@link ContentService} standard transformers into basic, clean + * HTML. + *

+ * The HTML that is produced probably isn't going to be suitable + * for direct web publishing, as it's likely going to be too + * basic. Instead, it should be simple and clean HTML, suitable + * for being the basis of some web-friendly HTML once edited + * / further transformed. + * + * @author Nick Burch + * @since 3.4 + */ +public class HTMLRenderingEngine extends AbstractRenderingEngine +{ + private static Log logger = LogFactory.getLog(HTMLRenderingEngine.class); + + /* + * Action constants + */ + public static final String NAME = "htmlRenderingEngine"; + + protected static final QName PRIMARY_IMAGE = QName.createQName( + "http://www.alfresco.org/model/website/1.0", "primaryImage"); + protected static final QName SECONDARY_IMAGE = QName.createQName( + "http://www.alfresco.org/model/website/1.0", "secondaryImage"); + + private DictionaryService dictionaryService; + + public void setDictionaryService(DictionaryService dictionaryService) { + this.dictionaryService = dictionaryService; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.rendition.executer.AbstractRenderingEngine#render(org.alfresco.repo.rendition.executer.AbstractRenderingEngine.RenderingContext) + */ + @Override + protected void render(RenderingContext context) + { + ContentReader contentReader = context.makeContentReader(); + String sourceMimeType = contentReader.getMimetype(); + String targetMimeType = "text/html"; + + // Check that Tika supports it + // TODO + + // Make the HTML Version + ContentWriter contentWriter = context.makeContentWriter(); + // TODO + contentWriter.putContent( + "\n" + + "

Test!

" + ); + + // Extract out any images + // TODO + boolean hasImages = true; // TODO + if(hasImages) + { + Map properties = new HashMap(); + NodeRef imgFolder = null; + + // Extract into it + boolean donePrimary = false; + for(String fakeContent : new String[] {"Test1","Test2"}) + { + if(imgFolder == null) + imgFolder = createImagesDirectory(context); + + // Create the node + properties.clear(); + properties.put(ContentModel.PROP_NAME, fakeContent); + NodeRef img = nodeService.createNode( + imgFolder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(fakeContent), + ContentModel.TYPE_CONTENT, + properties + ).getChildRef(); + + // If we can, associate it with the rendered HTML, so + // that they're properly linked + QName assocType = SECONDARY_IMAGE; + if(!donePrimary) + { + assocType = PRIMARY_IMAGE; + donePrimary = true; + } + if(dictionaryService.getAssociation(assocType) != null) + { + nodeService.createAssociation( + context.getDestinationNode(), img, assocType + ); + } + + // Put the image into the node + ContentWriter writer = contentService.getWriter( + img, ContentModel.PROP_CONTENT, true + ); + writer.putContent(fakeContent); + } + } + } + + /** + * Creates a directory to store the images in. + * The directory will be a sibling of the rendered + * HTML, and named similar to it. + */ + private NodeRef createImagesDirectory(RenderingContext context) + { + // It should be a sibling of the HTML in it's eventual location + // (not it's current temporary one!) + RenditionLocation location = resolveRenditionLocation( + context.getSourceNode(), context.getDefinition(), context.getDestinationNode() + ); + NodeRef parent = location.getParentRef(); + + // Figure out what to call it, based on the HTML node + String folderName = nodeService.getProperty( + context.getSourceNode(), + ContentModel.PROP_NAME + ).toString(); + if(folderName.lastIndexOf('.') > -1) + { + folderName = folderName.substring(0, folderName.lastIndexOf('.')); + } + folderName = folderName + "_files"; + + // Create the directory + Map properties = new HashMap(); + properties.put(ContentModel.PROP_NAME, folderName); + NodeRef imgFolder = nodeService.createNode( + parent, + ContentModel.ASSOC_CONTAINS, + QName.createQName(folderName), + ContentModel.TYPE_FOLDER, + properties + ).getChildRef(); + + return imgFolder; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/rendition/executer/HTMLRenderingEngineTest.java b/source/java/org/alfresco/repo/rendition/executer/HTMLRenderingEngineTest.java new file mode 100644 index 0000000000..8b844aafc7 --- /dev/null +++ b/source/java/org/alfresco/repo/rendition/executer/HTMLRenderingEngineTest.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.rendition.executer; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.rendition.RenditionDefinition; +import org.alfresco.service.cmr.rendition.RenditionService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.BaseAlfrescoSpringTest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Unit tests for the HTML Rendering Engine + * + * @author Nick Burch + */ +public class HTMLRenderingEngineTest extends BaseAlfrescoSpringTest +{ + private final static Log log = LogFactory.getLog(HTMLRenderingEngineTest.class); + private NodeRef companyHome; + private RenditionService renditionService; + private Repository repositoryHelper; + + private NodeRef sourceDoc; + private NodeRef targetFolder; + private String targetFolderPath; + + /* + * (non-Javadoc) + * + * @see org.alfresco.util.BaseAlfrescoSpringTest#onSetUpInTransaction() + */ + @SuppressWarnings("deprecation") + @Override + protected void onSetUpInTransaction() throws Exception + { + super.onSetUpInTransaction(); + this.nodeService = (NodeService) this.applicationContext.getBean("NodeService"); + this.contentService = (ContentService) this.applicationContext.getBean("ContentService"); + this.renditionService = (RenditionService) this.applicationContext.getBean("RenditionService"); + this.repositoryHelper = (Repository) this.applicationContext.getBean("repositoryHelper"); + this.companyHome = repositoryHelper.getCompanyHome(); + + createTargetFolder(); + } + + @Override + protected void onTearDownInTransaction() throws Exception { + super.onTearDownInTransaction(); + + tidyUpSourceDoc(); + } + + private void createTargetFolder() + { + // Set the current security context as admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + Map properties = new HashMap(); + properties.put(ContentModel.PROP_NAME, "TestFolder"); + targetFolder = nodeService.createNode( + companyHome, ContentModel.ASSOC_CONTAINS, + QName.createQName("TestFolder"), + ContentModel.TYPE_FOLDER, + properties + ).getChildRef(); + + targetFolderPath = "/" + + (String) nodeService.getProperty(companyHome, ContentModel.PROP_NAME) + + "/" + + (String) nodeService.getProperty(targetFolder, ContentModel.PROP_NAME) + ; + } + private void tidyUpSourceDoc() + { + // Set the current security context as admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + // Clean up the source + if(sourceDoc != null) + { + nodeService.deleteNode(sourceDoc); + } + + // Clean up the target folder + nodeService.deleteNode(targetFolder); + targetFolder = null; + + // All done + sourceDoc = null; + createTargetFolder(); + } + + private NodeRef createForDoc(String docname) + { + // Create the node + Map properties = new HashMap(); + properties.put(ContentModel.PROP_NAME, docname); + + NodeRef node = nodeService.createNode( + companyHome, ContentModel.ASSOC_CONTAINS, + QName.createQName(docname), + ContentModel.TYPE_CONTENT, + properties + ).getChildRef(); + + // Put the sample doc into it + ContentWriter writer = contentService.getWriter( + node, ContentModel.PROP_CONTENT, true + ); + writer.putContent("TESTING"); + + // All done + return node; + } + + public void testBasics() throws Exception + { + RenditionDefinition def = renditionService.createRenditionDefinition( + QName.createQName("Test"), HTMLRenderingEngine.NAME); + def.setParameterValue( + RenditionService.PARAM_DESTINATION_PATH_TEMPLATE, + targetFolderPath + "/${name}.html" + ); + + sourceDoc = createForDoc("quick.doc"); + + ChildAssociationRef rendition = renditionService.render(sourceDoc, def); + assertNotNull(rendition); + + // Check it was created + NodeRef htmlNode = rendition.getChildRef(); + assertEquals(true, nodeService.exists(htmlNode)); + + // Check it got the right name + assertEquals( + "quick.html", + nodeService.getProperty(htmlNode, ContentModel.PROP_NAME) + ); + + // Check it got the right contents + ContentReader reader = contentService.getReader( + htmlNode, ContentModel.PROP_CONTENT + ); + String html = reader.getContentString(); + assertEquals("