From 499367c4e9471ccae7808f30c5ab3b16a60c0ce9 Mon Sep 17 00:00:00 2001 From: Kevin Roast Date: Tue, 8 Aug 2006 12:05:41 +0000 Subject: [PATCH] JavaScript API enhancements - Document and Image transformation services git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3465 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/content-services-context.xml | 3 + .../mimetype/openoffice-document-formats.xml | 4 +- .../public-services-security-context.xml | 1 + .../executer/TransformActionExecuter.java | 10 +- .../repo/content/RoutingContentService.java | 15 ++ .../java/org/alfresco/repo/jscript/Node.java | 212 ++++++++++++++++++ .../cmr/repository/ContentService.java | 8 + 7 files changed, 246 insertions(+), 7 deletions(-) diff --git a/config/alfresco/content-services-context.xml b/config/alfresco/content-services-context.xml index b32565a02c..8ebab92bc2 100644 --- a/config/alfresco/content-services-context.xml +++ b/config/alfresco/content-services-context.xml @@ -66,6 +66,9 @@ + + + diff --git a/config/alfresco/mimetype/openoffice-document-formats.xml b/config/alfresco/mimetype/openoffice-document-formats.xml index 62a0040d75..1c18b17845 100644 --- a/config/alfresco/mimetype/openoffice-document-formats.xml +++ b/config/alfresco/mimetype/openoffice-document-formats.xml @@ -7,7 +7,7 @@ application/pdf pdf - Presentationimpress_pdf_Export + Presentationimpress_pdf_Export Spreadsheetcalc_pdf_Export Textwriter_pdf_Export @@ -30,7 +30,7 @@ text/html html - Presentationimpress_html_Export + Presentationimpress_html_Export SpreadsheetHTML (StarCalc) TextHTML (StarWriter) diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index f3980481d6..19037c2f82 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -405,6 +405,7 @@ org.alfresco.service.cmr.repository.ContentService.getWriter=ACL_NODE.0.sys:base.WriteContent org.alfresco.service.cmr.repository.ContentService.isTransformable=ACL_ALLOW org.alfresco.service.cmr.repository.ContentService.getTransformer=ACL_ALLOW + org.alfresco.service.cmr.repository.ContentService.getImageTransformer=ACL_ALLOW org.alfresco.service.cmr.repository.ContentService.transform=ACL_ALLOW org.alfresco.service.cmr.repository.ContentService.getTempWriter=ACL_ALLOW diff --git a/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java b/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java index 4ddf81e03d..89c040ca15 100644 --- a/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java @@ -169,7 +169,7 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase // Calculate the destination name String originalName = (String)nodeService.getProperty(actionedUponNodeRef, ContentModel.PROP_NAME); - String newName = transformName(originalName, mimeType); + String newName = transformName(this.mimetypeService, originalName, mimeType); // Since we are overwriting we need to figure out whether the destination node exists NodeRef copyNodeRef = null; @@ -225,7 +225,7 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase String originalTitle = (String)nodeService.getProperty(actionedUponNodeRef, ContentModel.PROP_TITLE); if (originalTitle != null && originalTitle.length() > 0) { - String newTitle = transformName(originalTitle, mimeType); + String newTitle = transformName(this.mimetypeService, originalTitle, mimeType); nodeService.setProperty(copyNodeRef, ContentModel.PROP_TITLE, newTitle); } } @@ -267,11 +267,11 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase * Transform name from original extension to new extension * * @param original - * @param originalMimetype * @param newMimetype - * @return + * + * @return name with new extension as appropriate for the mimetype */ - private String transformName(String original, String newMimetype) + public static String transformName(MimetypeService mimetypeService, String original, String newMimetype) { // get the current extension int dotIndex = original.lastIndexOf('.'); diff --git a/source/java/org/alfresco/repo/content/RoutingContentService.java b/source/java/org/alfresco/repo/content/RoutingContentService.java index f97376ea5d..7c9fb8261b 100644 --- a/source/java/org/alfresco/repo/content/RoutingContentService.java +++ b/source/java/org/alfresco/repo/content/RoutingContentService.java @@ -28,6 +28,7 @@ import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy; import org.alfresco.repo.content.filestore.FileContentStore; import org.alfresco.repo.content.transform.ContentTransformer; import org.alfresco.repo.content.transform.ContentTransformerRegistry; +import org.alfresco.repo.content.transform.magick.ImageMagickContentTransformer; import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; @@ -72,6 +73,7 @@ public class RoutingContentService implements ContentService private ContentStore store; /** the store for all temporarily created content */ private ContentStore tempStore; + private ImageMagickContentTransformer imageMagickContentTransformer; /** * The policy component @@ -122,6 +124,11 @@ public class RoutingContentService implements ContentService this.policyComponent = policyComponent; } + public void setImageMagickContentTransformer(ImageMagickContentTransformer imageMagickContentTransformer) + { + this.imageMagickContentTransformer = imageMagickContentTransformer; + } + /** * Service initialise */ @@ -385,6 +392,14 @@ public class RoutingContentService implements ContentService return transformer; } + /** + * @see org.alfresco.service.cmr.repository.ContentService#getImageTransformer() + */ + public ContentTransformer getImageTransformer() + { + return imageMagickContentTransformer; + } + /** * @see org.alfresco.repo.content.transform.ContentTransformerRegistry * @see org.alfresco.repo.content.transform.ContentTransformer diff --git a/source/java/org/alfresco/repo/jscript/Node.java b/source/java/org/alfresco/repo/jscript/Node.java index edc57e7750..5644a16d51 100644 --- a/source/java/org/alfresco/repo/jscript/Node.java +++ b/source/java/org/alfresco/repo/jscript/Node.java @@ -30,6 +30,8 @@ import java.util.StringTokenizer; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.repo.action.executer.TransformActionExecuter; +import org.alfresco.repo.content.transform.magick.ImageMagickContentTransformer; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.version.VersionModel; import org.alfresco.service.ServiceRegistry; @@ -45,6 +47,7 @@ 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.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NoTransformerException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.TemplateImageResolver; @@ -1420,6 +1423,196 @@ public final class Node implements Serializable, Scopeable } + // ------------------------------------------------------------------------------ + // Transformation and Rendering API + + /** + * Transform a document to a new document mimetype format. A copy of the document is made and + * the extension changed to match the new mimetype, then the transformation is applied. + * + * @param mimetype Mimetype destination for the transformation + * + * @return Node representing the newly transformed document. + */ + public Node transformDocument(String mimetype) + { + return transformDocument(mimetype, getPrimaryParentAssoc().getParentRef()); + } + + /** + * Transform a document to a new document mimetype format. A copy of the document is made in the + * specified destination folder and the extension changed to match the new mimetype, then then + * transformation is applied. + * + * @param mimetype Mimetype destination for the transformation + * @param destination Destination folder location + * + * @return Node representing the newly transformed document. + */ + public Node transformDocument(String mimetype, Node destination) + { + return transformDocument(mimetype, destination.getNodeRef()); + } + + private Node transformDocument(String mimetype, NodeRef destination) + { + // the delegate definition for transforming a document + Transformer transformer = new Transformer() + { + public Node transform(ContentService contentService, NodeRef nodeRef, ContentReader reader, ContentWriter writer) + { + Node transformedNode = null; + if (contentService.isTransformable(reader, writer)) + { + try + { + contentService.transform(reader, writer); + transformedNode = new Node(nodeRef, services, imageResolver, scope); + } + catch (NoTransformerException err) + { + // failed to find a useful transformer - do not return a node instance + } + } + return transformedNode; + } + }; + + return transformNode(transformer, mimetype, destination); + } + + /** + * Generic method to transform Node content from one mimetype to another. + * + * @param transformer The Transformer delegate supplying the transformation logic + * @param mimetype Mimetype of the destination content + * @param destination Destination folder location for the resulting document + * + * @return Node representing the transformed content - or null if the transform failed + */ + private Node transformNode(Transformer transformer, String mimetype, NodeRef destination) + { + Node transformedNode = null; + + // get the content reader + ContentService contentService = this.services.getContentService(); + ContentReader reader = contentService.getReader(this.nodeRef, ContentModel.PROP_CONTENT); + + // only perform the transformation if some content is available + if (reader != null) + { + // Copy the content node to a new node + NodeRef copyNodeRef = this.services.getCopyService().copy( + this.nodeRef, + destination, + ContentModel.ASSOC_CONTAINS, + getPrimaryParentAssoc().getQName(), + false); + + // modify the name of the copy to reflect the new mimetype + this.nodeService.setProperty( + copyNodeRef, + ContentModel.PROP_NAME, + TransformActionExecuter.transformName( + this.services.getMimetypeService(), getName(), mimetype)); + + // get the writer and set it up + ContentWriter writer = contentService.getWriter(copyNodeRef, ContentModel.PROP_CONTENT, true); + writer.setMimetype(mimetype); // new mimetype + writer.setEncoding(reader.getEncoding()); // original encoding + + // Try and transform the content using the supplied delegate + transformedNode = transformer.transform(contentService, copyNodeRef, reader, writer); + } + + return transformedNode; + } + + /** + * Transform an image to a new image format. A copy of the image document is made and + * the extension changed to match the new mimetype, then the transformation is applied. + * + * @param mimetype Mimetype destination for the transformation + * + * @return Node representing the newly transformed image. + */ + public Node transformImage(String mimetype) + { + return transformImage(mimetype, null, getPrimaryParentAssoc().getParentRef()); + } + + /** + * Transform an image to a new image format. A copy of the image document is made and + * the extension changed to match the new mimetype, then the transformation is applied. + * + * @param mimetype Mimetype destination for the transformation + * @param options Image convert command options + * + * @return Node representing the newly transformed image. + */ + public Node transformImage(String mimetype, String options) + { + return transformImage(mimetype, options, getPrimaryParentAssoc().getParentRef()); + } + + /** + * Transform an image to a new image mimetype format. A copy of the image document is made in the + * specified destination folder and the extension changed to match the new mimetype, then then + * transformation is applied. + * + * @param mimetype Mimetype destination for the transformation + * @param destination Destination folder location + * + * @return Node representing the newly transformed image. + */ + public Node transformImage(String mimetype, Node destination) + { + return transformImage(mimetype, null, destination.getNodeRef()); + } + + /** + * Transform an image to a new image mimetype format. A copy of the image document is made in the + * specified destination folder and the extension changed to match the new mimetype, then then + * transformation is applied. + * + * @param mimetype Mimetype destination for the transformation + * @param options Image convert command options + * @param destination Destination folder location + * + * @return Node representing the newly transformed image. + */ + public Node transformImage(String mimetype, String options, Node destination) + { + return transformImage(mimetype, options, destination.getNodeRef()); + } + + private Node transformImage(String mimetype, final String options, NodeRef destination) + { + // the delegate definition for transforming an image + Transformer transformer = new Transformer() + { + public Node transform(ContentService contentService, NodeRef nodeRef, ContentReader reader, ContentWriter writer) + { + Node transformedNode = null; + try + { + Map opts = new HashMap(1); + opts.put(ImageMagickContentTransformer.KEY_OPTIONS, options != null ? options : ""); + contentService.getImageTransformer().transform(reader, writer, opts); + transformedNode = new Node(nodeRef, services, imageResolver, scope); + } + catch (NoTransformerException err) + { + // failed to find a useful transformer - do not return a node instance + } + return transformedNode; + } + }; + + return transformNode(transformer, mimetype, destination); + } + + // ------------------------------------------------------------------------------ // Helper methods @@ -1647,4 +1840,23 @@ public final class Node implements Serializable, Scopeable private ContentData contentData; private QName property; } + + + /** + * Interface contract for simple anonymous classes that implement document transformations + */ + private interface Transformer + { + /** + * Transform the reader to the specified writer + * + * @param contentService ContentService + * @param noderef NodeRef of the destination for the transform + * @param reader Source reader + * @param writer Destination writer + * + * @return Node representing the transformed entity + */ + Node transform(ContentService contentService, NodeRef noderef, ContentReader reader, ContentWriter writer); + } } \ No newline at end of file diff --git a/source/java/org/alfresco/service/cmr/repository/ContentService.java b/source/java/org/alfresco/service/cmr/repository/ContentService.java index cbca110d5e..c1c032a6ab 100644 --- a/source/java/org/alfresco/service/cmr/repository/ContentService.java +++ b/source/java/org/alfresco/service/cmr/repository/ContentService.java @@ -126,6 +126,14 @@ public interface ContentService @Auditable(parameters = {"sourceMimetype", "targetMimetype"}) public ContentTransformer getTransformer(String sourceMimetype, String targetMimetype); + /** + * Fetch the transformer that is capable of transforming image content. + * + * @return Returns a transformer that can be used, or null if one was not available + */ + @Auditable + public ContentTransformer getImageTransformer(); + /** * Returns whether a transformer exists that can read the content from * the reader and write the content back out to the writer.