From 5b375cc7a7ca54577c978c3d6339a0a5ddb38f02 Mon Sep 17 00:00:00 2001 From: Kevin Roast Date: Tue, 9 Jan 2007 13:25:48 +0000 Subject: [PATCH] . Version History for a document now available in FreeMarker templating model - new node API method: document.versionHistory - returns a list of objects representing the version history of a document, such as name, created date, properties and content for the version - template example to show the version history for a document git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4762 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../service/cmr/repository/TemplateNode.java | 76 ++++-- .../cmr/repository/VersionHistoryNode.java | 243 ++++++++++++++++++ 2 files changed, 296 insertions(+), 23 deletions(-) create mode 100644 source/java/org/alfresco/service/cmr/repository/VersionHistoryNode.java diff --git a/source/java/org/alfresco/service/cmr/repository/TemplateNode.java b/source/java/org/alfresco/service/cmr/repository/TemplateNode.java index bd3ce6bea0..f20547e74a 100644 --- a/source/java/org/alfresco/service/cmr/repository/TemplateNode.java +++ b/source/java/org/alfresco/service/cmr/repository/TemplateNode.java @@ -22,6 +22,7 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -38,6 +39,8 @@ import org.alfresco.service.cmr.audit.AuditInfo; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.lock.LockStatus; import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QNameMap; import org.alfresco.service.namespace.RegexQNamePattern; @@ -67,7 +70,7 @@ public class TemplateNode implements Serializable private static Log logger = LogFactory.getLog(TemplateNode.class); private final static String NAMESPACE_BEGIN = "" + QName.NAMESPACE_BEGIN; - private final static String CONTENT_DEFAULT_URL = "/download/direct/{0}/{1}/{2}/{3}"; + protected final static String CONTENT_DEFAULT_URL = "/download/direct/{0}/{1}/{2}/{3}"; private final static String CONTENT_PROP_URL = "/download/direct/{0}/{1}/{2}/{3}?property={4}"; private final static String FOLDER_BROWSE_URL = "/navigate/browse/{0}/{1}/{2}"; @@ -84,7 +87,7 @@ public class TemplateNode implements Serializable private String path; private String id; private Set aspects = null; - private QNameMap properties; + private QNameMap properties; private List permissions = null; private boolean propsRetrieved = false; protected ServiceRegistry services = null; @@ -126,7 +129,7 @@ public class TemplateNode implements Serializable this.services = services; this.imageResolver = resolver; - this.properties = new QNameMap(this.services.getNamespaceService()); + this.properties = new QNameMap(this.services.getNamespaceService()); } @@ -239,7 +242,7 @@ public class TemplateNode implements Serializable /** * @return All the properties known about this node. */ - public Map getProperties() + public Map getProperties() { if (this.propsRetrieved == false) { @@ -269,6 +272,34 @@ public class TemplateNode implements Serializable return this.properties; } + /** + * @return a list of objects representing the version history of this node. + * @see VersionHistoryNode + */ + public List getVersionHistory() + { + List records = Collections.emptyList(); + + if (this.getAspects().contains(ContentModel.ASPECT_VERSIONABLE)) + { + VersionHistory history = this.services.getVersionService().getVersionHistory(this.nodeRef); + if (history != null) + { + records = new ArrayList(8); + for (Version version : history.getAllVersions()) + { + // create a wrapper for the version information + VersionHistoryNode record = new VersionHistoryNode(version, this); + + // add the client side version to the list + records.add(record); + } + } + } + + return records; + } + /** * @return true if this Node is a container (i.e. a folder) */ @@ -415,9 +446,8 @@ public class TemplateNode implements Serializable */ public String getContent() { - ContentService contentService = this.services.getContentService(); - ContentReader reader = contentService.getReader(this.nodeRef, ContentModel.PROP_CONTENT); - return (reader != null && reader.exists()) ? reader.getContentString() : ""; + TemplateContentData content = (TemplateContentData)this.getProperties().get(ContentModel.PROP_CONTENT); + return content != null ? content.getContent() : ""; } /** @@ -430,25 +460,25 @@ public class TemplateNode implements Serializable { if (getIsDocument() == true) { - try - { - return MessageFormat.format(CONTENT_DEFAULT_URL, new Object[] { - nodeRef.getStoreRef().getProtocol(), - nodeRef.getStoreRef().getIdentifier(), - nodeRef.getId(), - StringUtils.replace(URLEncoder.encode(getName(), "UTF-8"), "+", "%20") } ); - } - catch (UnsupportedEncodingException err) - { - throw new TemplateException("Failed to encode content URL for node: " + nodeRef, err); - } + try + { + return MessageFormat.format(CONTENT_DEFAULT_URL, new Object[] { + nodeRef.getStoreRef().getProtocol(), + nodeRef.getStoreRef().getIdentifier(), + nodeRef.getId(), + StringUtils.replace(URLEncoder.encode(getName(), "UTF-8"), "+", "%20") } ); + } + catch (UnsupportedEncodingException err) + { + throw new TemplateException("Failed to encode content URL for node: " + nodeRef, err); + } } else { - return MessageFormat.format(FOLDER_BROWSE_URL, new Object[] { - nodeRef.getStoreRef().getProtocol(), - nodeRef.getStoreRef().getIdentifier(), - nodeRef.getId() } ); + return MessageFormat.format(FOLDER_BROWSE_URL, new Object[] { + nodeRef.getStoreRef().getProtocol(), + nodeRef.getStoreRef().getIdentifier(), + nodeRef.getId() } ); } } diff --git a/source/java/org/alfresco/service/cmr/repository/VersionHistoryNode.java b/source/java/org/alfresco/service/cmr/repository/VersionHistoryNode.java new file mode 100644 index 0000000000..ff1dd1f7a8 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/repository/VersionHistoryNode.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.service.cmr.repository; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.MessageFormat; +import java.util.Date; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.TemplateNode.TemplateContentData; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionType; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.QNameMap; +import org.springframework.util.StringUtils; + +/** + * Template Node wrapper representing a record in the version history of a node. + * Provides access to basic properties and version information for the frozen state record. + * + * @author Kevin Roast + */ +public class VersionHistoryNode implements Serializable +{ + private QNameMap properties; + private boolean propsRetrieved = false; + private Version version; + private TemplateNode parent; + + /** + * Constructor + * + * @param version Descriptor of the node version information + */ + public VersionHistoryNode(Version version, TemplateNode parent) + { + if (version == null) + { + throw new IllegalArgumentException("Version history descriptor is mandatory."); + } + if (parent == null) + { + throw new IllegalArgumentException("Parent TemplateNode is mandatory."); + } + this.version = version; + this.parent = parent; + this.properties = new QNameMap(parent.services.getNamespaceService()); + } + + /** + * @return The GUID for the frozen state NodeRef + */ + public String getId() + { + return this.version.getFrozenStateNodeRef().getId(); + } + + /** + * @return Returns the frozen state NodeRef this record represents + */ + public NodeRef getNodeRef() + { + return this.version.getFrozenStateNodeRef(); + } + + /** + * @return Returns the type. + */ + public QName getType() + { + return parent.services.getNodeService().getType(this.version.getFrozenStateNodeRef()); + } + + /** + * Helper method to get the item name. + * + * @return the item name + */ + public String getName() + { + return (String)this.getProperties().get(ContentModel.PROP_NAME); + } + + /** + * Helper method to get the created date from the version property data. + * + * @return the date the version was created + */ + public Date getCreatedDate() + { + return this.version.getCreatedDate(); + } + + /** + * Helper method to get the creator of the version. + * + * @return the creator of the version + */ + public String getCreator() + { + return this.version.getCreator(); + } + + /** + * Helper method to get the version label from the version property data. + * + * @return the version label + */ + public String getVersionLabel() + { + return this.version.getVersionLabel(); + } + + /** + * Helper method to get the version type. + * + * @return true if this is a major version, false otherwise. + */ + public boolean getIsMajorVersion() + { + return (this.version.getVersionType() == VersionType.MAJOR); + } + + /** + * Helper method to get the version description. + * + * @return the version description + */ + public String getDescription() + { + return this.version.getDescription(); + } + + /** + * Get the map containing the version property values. + * + * @return the map containing the version properties + */ + public Map getProperties() + { + if (propsRetrieved == false) + { + Map props = parent.services.getNodeService().getProperties( + this.version.getFrozenStateNodeRef()); + + for (QName qname : props.keySet()) + { + Serializable propValue = props.get(qname); + if (propValue instanceof NodeRef) + { + // NodeRef object properties are converted to new TemplateNode objects + // so they can be used as objects within a template + propValue = new TemplateNode(((NodeRef)propValue), parent.services, parent.imageResolver); + } + else if (propValue instanceof ContentData) + { + // ContentData object properties are converted to TemplateContentData objects + // so the content and other properties of those objects can be accessed + propValue = parent.new TemplateContentData((ContentData)propValue, qname); + } + this.properties.put(qname.toString(), propValue); + } + + propsRetrieved = true; + } + + return this.properties; + } + + + // ------------------------------------------------------------------------------ + // Content API + + /** + * @return the content String for this node from the default content property + * (@see ContentModel.PROP_CONTENT) + */ + public String getContent() + { + TemplateContentData content = (TemplateContentData)this.getProperties().get(ContentModel.PROP_CONTENT); + return content != null ? content.getContent() : ""; + } + + /** + * @return For a content document, this method returns the URL to the content stream for + * the default content property (@see ContentModel.PROP_CONTENT) + *

+ * For a container node, this method return the URL to browse to the folder in the web-client + */ + public String getUrl() + { + NodeRef nodeRef = this.version.getFrozenStateNodeRef(); + try + { + return MessageFormat.format(parent.CONTENT_DEFAULT_URL, new Object[] { + nodeRef.getStoreRef().getProtocol(), + nodeRef.getStoreRef().getIdentifier(), + nodeRef.getId(), + StringUtils.replace(URLEncoder.encode(getName(), "UTF-8"), "+", "%20") } ); + } + catch (UnsupportedEncodingException err) + { + throw new TemplateException("Failed to encode content URL for node: " + nodeRef, err); + } + } + + /** + * @return The mimetype encoding for content attached to the node from the default content property + * (@see ContentModel.PROP_CONTENT) + */ + public String getMimetype() + { + TemplateContentData content = (TemplateContentData)this.getProperties().get(ContentModel.PROP_CONTENT); + return (content != null ? content.getMimetype() : null); + } + + /** + * @return The size in bytes of the content attached to the node from the default content property + * (@see ContentModel.PROP_CONTENT) + */ + public long getSize() + { + TemplateContentData content = (TemplateContentData)this.getProperties().get(ContentModel.PROP_CONTENT); + return (content != null ? content.getSize() : 0L); + } +}