diff --git a/config/alfresco/templates/webscripts/org/alfresco/cmis/atomentry.lib.atom.ftl b/config/alfresco/templates/webscripts/org/alfresco/cmis/atomentry.lib.atom.ftl index 8c8f7293a8..6b18d1c1d5 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/cmis/atomentry.lib.atom.ftl +++ b/config/alfresco/templates/webscripts/org/alfresco/cmis/atomentry.lib.atom.ftl @@ -10,6 +10,19 @@ [/#macro] +[#macro objectCMISProps object propfilter] + + [#assign typedef = cmistype(object)] + + [#-- TODO: Spec issue: BaseType not a property --] + [@filter propfilter "BaseType"][@propvalue "BaseType" typedef.typeId.baseTypeId.id "STRING"/][/@filter] + + [#list typedef.propertyDefinitions?values as propdef] + [@filter propfilter propdef.propertyId.name][@prop propdef.propertyId.name object propdef.dataType/][/@filter] + [/#list] + +[/#macro] + [#-- --] [#-- ATOM Entry for Document --] @@ -30,7 +43,7 @@ ${node.name} ${xmldate(node.properties.modified)} -[@documentCMISProps node propfilter/] +[@objectCMISProps node propfilter/] [#if includeallowableactions][@allowableactions node/][/#if] @@ -41,7 +54,7 @@ [#macro documentCMISLinks node] - + [@linkstream node "stream"/] @@ -49,18 +62,6 @@ [/#macro] -[#macro documentCMISProps node propfilter] - - [#-- TODO: Spec issue: BaseType not a property --] - [@filter propfilter "BaseType"][@propvalue "BaseType" "document" "STRING"/][/@filter] - - [#assign typedef = cmistype(node)] - [#list typedef.propertyDefinitions?values as propdef] - [@filter propfilter propdef.propertyId.name][@prop propdef.propertyId.name node propdef.dataType/][/@filter] - [/#list] - -[/#macro] - [#-- --] [#-- ATOM Entry for Version --] @@ -79,7 +80,7 @@ ${node.name} ${xmldate(node.properties.modified)} -[@documentCMISProps node propfilter/] +[@objectCMISProps node propfilter/] ${xmldate(node.properties.modified)} @@ -107,7 +108,7 @@ ${node.name} ${xmldate(node.properties.modified)} -[@documentCMISProps node propfilter/] +[@objectCMISProps node propfilter/] [#if includeallowableactions][@allowableactions node/][/#if] @@ -136,7 +137,7 @@ ${xmldate(node.properties.modified)} [#-- recurse for depth greater than 1 --] -[@folderCMISProps node propfilter/] +[@objectCMISProps node propfilter/] [#if includeallowableactions][@allowableactions node/][/#if] [#if depth < maxdepth || depth == -1] @@ -157,7 +158,7 @@ [#macro folderCMISLinks node] - + [#if cmisproperty(node, "ParentId")?is_string] [/#if] @@ -167,16 +168,38 @@ [/#macro] -[#macro folderCMISProps node propfilter] - - [#-- TODO: Spec issue: BaseType not a property --] - [@filter propfilter "BaseType"][@propvalue "BaseType" "folder" "STRING"/][/@filter] - [#assign typedef = cmistype(node)] - [#list typedef.propertyDefinitions?values as propdef] - [@filter propfilter propdef.propertyId.name][@prop propdef.propertyId.name node propdef.dataType/][/@filter] - [/#list] - +[#-- --] +[#-- ATOM Entry for Relationship --] +[#-- --] + +[#macro assoc assoc propfilter="*" includeallowableactions=false ns=""] +[@entry ns] +${xmldate(date)} [#-- TODO: [@namedvalue "CreatedBy" assoc "STRING"/] --] +[@namedvalue "ObjectId" assoc "ID"/] [#-- TODO: spec id, how to map? --] +[@namedvalue "ObjectId" assoc "ID"/] [#-- TODO: id compliant --] + + +[@assocCMISLinks assoc=assoc/] +${xmldate(date)} [#-- TODO: [@namedvalue "CreationDate" assoc "DATETIME"/] --] +[@namedvalue "ObjectId" assoc "ID"/] [#-- TODO: spec id, how to map? --] +[@namedvalue "ObjectId" assoc "ID"/] [#-- TODO: spec id, how to map? --] +${xmldate(date)} [#-- TODO: [@namedvalue "LastModificationDate" assoc "DATETIME"/] --] + +[@objectCMISProps assoc propfilter/] +[#-- TODO: [#if includeallowableactions][@allowableactions node/][/#if] --] + + +${xmldate(date)} [#-- TODO: [@namedvalue "LastModificationDate" assoc "DATETIME"/] --] +[/@entry] +[/#macro] + +[#macro assocCMISLinks assoc] +[#-- TODO: --] + + + + [/#macro] @@ -239,8 +262,8 @@ [#if filter == "*" || filter?index_of(value) != -1 || filter?matches(value,'i')][#nested][/#if] [/#macro] -[#macro prop name node type] -[#assign value=cmisproperty(node, name)/] +[#macro prop name object type] +[#assign value=cmisproperty(object, name)/] [#if value?is_string || value?is_number || value?is_boolean || value?is_date || value?is_enumerable] [@propvalue name value type/] [#elseif value.class.canonicalName?ends_with("NULL")] @@ -250,20 +273,20 @@ [#macro propvalue name value type] [#if type == "STRING"] -[@values value;v][@stringvalue v/][/@values] +[@values value;v][@stringvalue v/][/@values] [#elseif type == "INTEGER"] -[@values value;v][@integervalue v/][/@values] +[@values value;v][@integervalue v/][/@values] [#elseif type == "DECIMAL"] -[@values value;v][@decimalvalue v/][/@values] +[@values value;v][@decimalvalue v/][/@values] [#elseif type == "BOOLEAN"] -[@values value;v][@booleanvalue v/][/@values] +[@values value;v][@booleanvalue v/][/@values] [#elseif type == "DATETIME"] -[@values value;v][@datetimevalue v/][/@values] +[@values value;v][@datetimevalue v/][/@values] [#elseif type == "URI"] [#-- TODO: check validity of abs url prefix --] -[@values value;v][@urivalue absurl(url.serviceContext) + v/][/@values] +[@values value;v][@urivalue absurl(url.serviceContext) + v/][/@values] [#elseif type == "ID"] -[@values value;v][@idvalue v/][/@values] +[@values value;v][@idvalue v/][/@values] [#-- TODO: remaining property types --] [/#if] [/#macro] @@ -292,15 +315,40 @@ [#-- CMIS Values --] [#-- --] +[#macro namedvalue name object type] +[#assign value=cmisproperty(object, name)/] +[#if value?is_string || value?is_number || value?is_boolean || value?is_date || value?is_enumerable][@typedvalue value type/][#elseif value.class.canonicalName?ends_with("NULL")][/#if] +[/#macro] + +[#macro typedvalue value type] +[#if type == "STRING"] +[@values value;v][@stringvalue v/][/@values] +[#elseif type == "INTEGER"] +[@values value;v][@integervalue v/][/@values] +[#elseif type == "DECIMAL"] +[@values value;v][@decimalvalue v/][/@values] +[#elseif type == "BOOLEAN"] +[@values value;v][@booleanvalue v/][/@values] +[#elseif type == "DATETIME"] +[@values value;v][@datetimevalue v/][/@values] +[#elseif type == "URI"] +[#-- TODO: check validity of abs url prefix --] +[@values value;v][@urivalue absurl(url.serviceContext) + v/][/@values] +[#elseif type == "ID"] +[@values value;v][@idvalue v/][/@values] +[#-- TODO: remaining property types --] +[/#if] +[/#macro] + [#macro values vals][#if vals?is_enumerable][#list vals as val][#nested val][/#list][#else][#nested vals][/#if][/#macro] -[#macro stringvalue value]${value}[/#macro] -[#macro integervalue value]${value?c}[/#macro] -[#macro decimalvalue value]${value?c}[/#macro] -[#macro booleanvalue value]${value?string}[/#macro] -[#macro datetimevalue value]${xmldate(value)}[/#macro] -[#macro urivalue value]${value}[/#macro] -[#macro idvalue value]${value}[/#macro] +[#macro stringvalue value]${value}[/#macro] +[#macro integervalue value]${value?c}[/#macro] +[#macro decimalvalue value]${value?c}[/#macro] +[#macro booleanvalue value]${value?string}[/#macro] +[#macro datetimevalue value]${xmldate(value)}[/#macro] +[#macro urivalue value]${value}[/#macro] +[#macro idvalue value]${value}[/#macro] [#-- --] @@ -527,37 +575,37 @@ [#if type == "STRING"] [@cmisChoices choice.children type/] -[@stringvalue choice.value/] +[@stringvalue choice.value/] [#elseif type == "INTEGER"] [@cmisChoices choice.children type/] -[@stringvalue choice.value/] +[@stringvalue choice.value/] [#elseif type == "DECIMAL"] [@cmisChoices choice.children type/] -[@stringvalue choice.value/] +[@stringvalue choice.value/] [#elseif type == "BOOLEAN"] [@cmisChoices choice.children type/] -[@stringvalue choice.value/] +[@stringvalue choice.value/] [#elseif type == "DATETIME"] [@cmisChoices choice.children type/] -[@stringvalue choice.value/] +[@stringvalue choice.value/] [#elseif type == "URI"] [@cmisChoices choice.children type/] -[@stringvalue choice.value/] +[@stringvalue choice.value/] [#elseif type == "ID"] [@cmisChoices choice.children type/] -[@stringvalue choice.value/] +[@stringvalue choice.value/] [#-- TODO: remaining property types --] [/#if] @@ -581,4 +629,13 @@ [#macro contenturi node]${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/content[#if node.properties.name?? && node.properties.name?last_index_of(".") != -1]${encodeuri(node.properties.name?substring(node.properties.name?last_index_of(".")))}[/#if][/#macro] [#-- Helper to render Alfresco service document uri --] -[#macro serviceuri]${absurl(url.serviceContext)}/api/repository[/#macro] \ No newline at end of file +[#macro serviceuri]${absurl(url.serviceContext)}/api/repository[/#macro] + +[#-- Helper to render Node Ref --] +[#macro noderef node]${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}[/#macro] + +[#-- Helper to render Alfresco Node uri --] +[#macro nodeuri node]/api/node/[@noderef node/][/#macro] + +[#-- Helper to render Alfresco Assoc uri --] +[#macro assocuri assoc]/api/rel/[@noderef assoc.source/]/type/${cmistype(assoc).typeId.id!"undefined"}/target/[@noderef assoc.target/][/#macro] diff --git a/config/alfresco/templates/webscripts/org/alfresco/cmis/atomentry.lib.js b/config/alfresco/templates/webscripts/org/alfresco/cmis/atomentry.lib.js index 2eb560afc5..0a843fb38a 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/cmis/atomentry.lib.js +++ b/config/alfresco/templates/webscripts/org/alfresco/cmis/atomentry.lib.js @@ -1,4 +1,3 @@ - // // Create Alfresco Node from Atom Entry // @@ -199,6 +198,57 @@ function updateNode(node, entry, exclude, validator) } +// +// Create Alfresco Association from Atom Entry +// +// @param source source node +// @param entry atom entry +// @return created association (or null, in case of error) +// +function createAssociation(source, entry) +{ + var object = entry.getExtension(atom.names.cmis_object); + var typeId = (object !== null) ? object.objectTypeId.nativeValue : null; + + // locate relationship type definition + // TODO: check this against spec - default to Relationship, if not specified + var type = cmis.queryType(typeId === null ? "relationship" : typeId); + if (type === null) + { + status.setCode(400, "CMIS object type " + typeId + " not understood"); + return null; + } + if (type.typeId.baseTypeId.id != "relationship") + { + status.setCode(400, "CMIS object type " + typeId + " is not a relationship type"); + return null; + } + if (!type.creatable) + { + status.setCode(400, "Relationship type " + typeId + " is not creatable"); + return null; + } + + // locate target + var targetId = (object !== null) ? object.targetId.nativeValue : null; + if (targetId === null) + { + status.setCode(400, "Target Id has not been specified"); + return null; + } + var target = search.findNode(targetId); + if (target === null) + { + status.setCode(400, "Target Id " + targetId + " does not refer to known item"); + return null; + } + + // create association + var assoc = source.createAssociation(target, type.typeId.QName.toString()); + return assoc; +} + + // callback for validating property update for patch // return null => update not allowed, abort update // true => update allowed diff --git a/config/alfresco/templates/webscripts/org/alfresco/cmis/constants.lib.js b/config/alfresco/templates/webscripts/org/alfresco/cmis/constants.lib.js new file mode 100644 index 0000000000..8620873b57 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/cmis/constants.lib.js @@ -0,0 +1,6 @@ +// +// Constants +// + +// CMIS Enums +CMISRelationshipDirectionEnum = Packages.org.alfresco.cmis.CMISRelationshipDirectionEnum; diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationship.get.atomentry.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationship.get.atomentry.ftl new file mode 100644 index 0000000000..c63fc7ba2c --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationship.get.atomentry.ftl @@ -0,0 +1,11 @@ +[#ftl] +[#import "/org/alfresco/cmis/ns.lib.atom.ftl" as nsLib/] +[#import "/org/alfresco/cmis/atomentry.lib.atom.ftl" as entryLib/] +[#compress] + + +[#assign namespace][@nsLib.entryNS/][/#assign] + +[@entryLib.assoc assoc=assoc propfilter=filter includeallowableactions=includeAllowableActions ns=namespace/] + +[/#compress] diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationship.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationship.get.desc.xml new file mode 100644 index 0000000000..8ab15c4c9c --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationship.get.desc.xml @@ -0,0 +1,9 @@ + + Retrieve relationship (getProperties) + + + /api/rel/{store_type}/{store_id}/{id}/type/{rel_type}/target/{target_store_type}/{target_store_id}/{target_id} + guest + argument + CMIS + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationship.get.js b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationship.get.js new file mode 100644 index 0000000000..2298d2cd09 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationship.get.js @@ -0,0 +1,28 @@ +script: +{ + // relationship type + var relType = url.templateArgs.rel_type; + model.relTypeDef = cmis.queryType(relType); + if (model.relTypeDef === null) + { + status.setCode(400, "Relationship type " + relType + " unknown"); + break script; + } + if (model.relTypeDef.baseType.typeId != "relationship") + { + status.setCode(400, "Type + " + relType + " is not a relationship type"); + break script; + } + + // source and target + var source = [url.templateArgs.store_type, url.templateArgs.store_id, url.templateArgs.id]; + var target = [url.templateArgs.target_store_type, url.templateArgs.target_store_id, url.templateArgs.target_id]; + + // locate association + model.assoc = cmis.findRelationship(model.relTypeDef, source, target); + if (model.assoc === null) + { + status.setCode(404, "Assoc " + source.join("/") + "/" + relType + "/" + target.join("/") + " not found"); + break script; + } +} diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.get.atomfeed.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.get.atomfeed.ftl new file mode 100644 index 0000000000..45002aee62 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.get.atomfeed.ftl @@ -0,0 +1,24 @@ +[#ftl] +[#import "/org/alfresco/cmis/ns.lib.atom.ftl" as nsLib/] +[#import "/org/alfresco/cmis/atomfeed.lib.atom.ftl" as feedLib/] +[#import "/org/alfresco/cmis/atomentry.lib.atom.ftl" as entryLib/] +[#import "/org/alfresco/paging.lib.atom.ftl" as pagingLib/] +[#compress] + + + + +[@feedLib.node node "relationships"] + [@pagingLib.links cursor/] +[/@feedLib.node] + +[#list results as assoc] + [@entryLib.assoc assoc=assoc propfilter=filter includeallowableactions=includeAllowableActions/] +[/#list] + +[@feedLib.hasMore cursor/] +[@pagingLib.opensearch cursor/] + + + +[/#compress] \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.get.desc.xml new file mode 100644 index 0000000000..0c52275ea0 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.get.desc.xml @@ -0,0 +1,10 @@ + + Retrieve list of relationships (getRelationships) + + + /api/node/{store_type}/{store_id}/{id}/rels?filter={filter?}&relationshipType={relationshipType?}&includeSubRelationshipTypes={includeSubRelationshipTypes?}&direction={direction?}&skipCount={skipCount?}&maxItems={maxItems?}&includeAllowableActions={includeAllowableActions?} + /api/path/{store_type}/{store_id}/{id}/rels?filter={filter?}&relationshipType={relationshipType?}&includeSubRelationshipTypes={includeSubRelationshipTypes?}&direction={direction?}&skipCount={skipCount?}&maxItems={maxItems?}&includeAllowableActions={includeAllowableActions?} + guest + argument + CMIS + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.get.js b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.get.js new file mode 100644 index 0000000000..be27d6b317 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.get.js @@ -0,0 +1,59 @@ + + +script: +{ + // locate node + var pathSegments = url.match.split("/"); + var reference = [ url.templateArgs.store_type, url.templateArgs.store_id ].concat(url.templateArgs.id.split("/")); + model.node = cmis.findNode(pathSegments[2], reference); + if (model.node === null) + { + status.setCode(404, "Repository " + pathSegments[2] + " " + reference.join("/") + " not found"); + break script; + } + + // property filter + model.filter = args[cmis.ARG_FILTER]; + if (model.filter === null) + { + model.filter = "*"; + } + + // relationship type + var relType = args[cmis.ARG_RELATIONSHIP_TYPE]; + if (relType != null) + { + model.relTypeDef = cmis.queryType(relType); + if (model.relTypeDef === null) + { + status.setCode(400, "Relationship type " + relType + " unknown"); + break script; + } + if (model.relTypeDef.baseType.typeId != "relationship") + { + status.setCode(400, "Type + " + relType + " is not a relationship type"); + break script; + } + } + + // include sub relationship types + model.includeSubRelationshipTypes = args[cmis.ARG_INCLUDE_SUB_RELATIONSHIP_TYPES] == "true" ? true : false; + + // direction + var direction = args[cmis.ARG_DIRECTION]; + if (direction !== null && !CMISRelationshipDirectionEnum.FACTORY.validLabel(direction)) + { + status.setCode(400, "Direction " + direction + " unknown"); + break script; + } + model.direction = CMISRelationshipDirectionEnum.FACTORY.toEnum(direction); + + // include allowable actions + model.includeAllowableActions = args[cmis.ARG_INCLUDE_ALLOWABLE_ACTIONS] == "true" ? true : false; + + // retrieve relationships + var page = paging.createPageOrWindow(args); + var paged = cmis.queryRelationships(model.node, model.relDefType, model.includeSubRelationshipTypes, model.direction, page); + model.results = paged.results; + model.cursor = paged.cursor; +} diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.post.atom.js b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.post.atom.js new file mode 100644 index 0000000000..b2bea9fab1 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.post.atom.js @@ -0,0 +1,40 @@ + + +script: +{ + // ensure atom entry is posted + if (entry === null) + { + status.code = 400; + status.message = "Expected atom entry"; + status.redirect = true; + break script; + } + + // locate source node + var pathSegments = url.match.split("/"); + var reference = [ url.templateArgs.store_type, url.templateArgs.store_id ].concat(url.templateArgs.id.split("/")); + model.source = cmis.findNode(pathSegments[2], reference); + if (model.source === null) + { + status.code = 404; + status.message = "Repository " + pathSegments[2] + " " + reference.join("/") + " not found"; + status.redirect = true; + break script; + } + + // create + var assoc = createAssociation(model.source, entry); + if (assoc == null) + { + break script; + } + + // success + model.assoc = assoc; + // TODO: set Content-Location + status.code = 201; + // TODO: complete url mapping + status.location = url.server + url.serviceContext + "/api/rel/" + model.source.nodeRef.storeRef.protocol + "/" + model.source.nodeRef.storeRef.identifier + "/" + model.source.nodeRef.id; + status.redirect = true; +} diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.post.atomentry.201.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.post.atomentry.201.ftl new file mode 100644 index 0000000000..c63fc7ba2c --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.post.atomentry.201.ftl @@ -0,0 +1,11 @@ +[#ftl] +[#import "/org/alfresco/cmis/ns.lib.atom.ftl" as nsLib/] +[#import "/org/alfresco/cmis/atomentry.lib.atom.ftl" as entryLib/] +[#compress] + + +[#assign namespace][@nsLib.entryNS/][/#assign] + +[@entryLib.assoc assoc=assoc propfilter=filter includeallowableactions=includeAllowableActions ns=namespace/] + +[/#compress] diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.post.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.post.desc.xml new file mode 100644 index 0000000000..a1181ff6a7 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationships.post.desc.xml @@ -0,0 +1,10 @@ + + Create relationship (createRelationship) + + + /api/node/{store_type}/{store_id}/{id}/rels + /api/path/{store_type}/{store_id}/{id}/rels + guest + argument + CMIS + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/types.get.js b/config/alfresco/templates/webscripts/org/alfresco/repository/store/types.get.js index ce543be686..d8af28258a 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/types.get.js +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/types.get.js @@ -17,13 +17,13 @@ script: { // query a specific type and its descendants var typedef = cmis.queryType(typeId); - if (typedef === null) - { - status.code = 404; - status.message = "Type " + typeId + " not found"; - status.redirect = true; - break script; - } + if (typedef === null) + { + status.code = 404; + status.message = "Type " + typeId + " not found"; + status.redirect = true; + break script; + } var paged = cmis.queryTypeHierarchy(typedef, true, page); model.results = paged.results; model.cursor = paged.cursor; diff --git a/source/java/org/alfresco/repo/cmis/rest/CMISPropertyValueMethod.java b/source/java/org/alfresco/repo/cmis/rest/CMISPropertyValueMethod.java index 9d92a775a5..7bb8933372 100644 --- a/source/java/org/alfresco/repo/cmis/rest/CMISPropertyValueMethod.java +++ b/source/java/org/alfresco/repo/cmis/rest/CMISPropertyValueMethod.java @@ -27,6 +27,7 @@ package org.alfresco.repo.cmis.rest; import java.util.List; import org.alfresco.cmis.CMISServices; +import org.alfresco.repo.template.TemplateAssociation; import org.alfresco.repo.template.TemplateNode; import freemarker.ext.beans.BeanModel; @@ -76,14 +77,6 @@ public final class CMISPropertyValueMethod implements TemplateMethodModelEx Object arg0 = args.get(0); if (arg0 instanceof BeanModel) { - // extract node - TemplateNode node = null; - Object wrapped = ((BeanModel)arg0).getWrappedObject(); - if (wrapped != null && wrapped instanceof TemplateNode) - { - node = (TemplateNode)wrapped; - } - // extract property name String propertyName = null; Object arg1 = args.get(1); @@ -92,8 +85,18 @@ public final class CMISPropertyValueMethod implements TemplateMethodModelEx propertyName = ((TemplateScalarModel)arg1).getAsString(); } - // retrieve property value - result = cmisService.getProperty(node.getNodeRef(), propertyName); + // extract node + Object wrapped = ((BeanModel)arg0).getWrappedObject(); + if (wrapped != null && wrapped instanceof TemplateNode) + { + // retrieve property value from node + result = cmisService.getProperty(((TemplateNode)wrapped).getNodeRef(), propertyName); + } + else if (wrapped != null && wrapped instanceof TemplateAssociation) + { + // retrieve property value from node + result = cmisService.getProperty(((TemplateAssociation)wrapped).getAssociationRef(), propertyName); + } } } diff --git a/source/java/org/alfresco/repo/cmis/rest/CMISScript.java b/source/java/org/alfresco/repo/cmis/rest/CMISScript.java index e600ad880c..1f5959c160 100644 --- a/source/java/org/alfresco/repo/cmis/rest/CMISScript.java +++ b/source/java/org/alfresco/repo/cmis/rest/CMISScript.java @@ -33,11 +33,13 @@ import org.alfresco.cmis.CMISPropertyDefinition; import org.alfresco.cmis.CMISQueryEnum; import org.alfresco.cmis.CMISQueryOptions; import org.alfresco.cmis.CMISQueryService; +import org.alfresco.cmis.CMISRelationshipDirectionEnum; import org.alfresco.cmis.CMISResultSet; import org.alfresco.cmis.CMISServices; import org.alfresco.cmis.CMISTypeDefinition; import org.alfresco.cmis.CMISTypesFilterEnum; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.jscript.Association; import org.alfresco.repo.jscript.BaseScopableProcessorExtension; import org.alfresco.repo.jscript.ScriptNode; import org.alfresco.repo.model.Repository; @@ -46,6 +48,7 @@ import org.alfresco.repo.web.util.paging.Page; import org.alfresco.repo.web.util.paging.PagedResults; import org.alfresco.repo.web.util.paging.Paging; import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -73,7 +76,7 @@ public class CMISScript extends BaseScopableProcessorExtension public static final String ARG_INCLUDE_ALLOWABLE_ACTIONS = "includeAllowableActions"; public static final String ARG_INCLUDE_PROPERTY_DEFINITIONS = "includePropertyDefinitions"; public static final String ARG_INCLUDE_RELATIONSHIPS = "includeRelationships"; - public static final String ARG_INCLUDE_SUB_RELATIONSHIP_TYPES = "includeSubrelationshipTypes"; + public static final String ARG_INCLUDE_SUB_RELATIONSHIP_TYPES = "includeSubRelationshipTypes"; public static final String ARG_LENGTH = "length"; public static final String ARG_MAJOR = "major"; public static final String ARG_MAJOR_VERSION = "majorVersion"; @@ -197,7 +200,7 @@ public class CMISScript extends BaseScopableProcessorExtension */ public String getDefaultTypesFilter() { - return CMISTypesFilterEnum.FACTORY.defaultLabel(); + return CMISTypesFilterEnum.FACTORY.getDefaultLabel(); } /** @@ -242,6 +245,25 @@ public class CMISScript extends BaseScopableProcessorExtension } return node; } + + /** + * Finds an Association + * + * @param sourceType + * @param source + * @param relDef + * @param targetType + * @param target + * + * @return + */ + public Association findRelationship(CMISTypeDefinition relDef, String[] sourceRef, String[] targetRef) + { + NodeRef source = new NodeRef(sourceRef[0], sourceRef[1], sourceRef[2]); + NodeRef target = new NodeRef(targetRef[0], targetRef[1], targetRef[2]); + AssociationRef assocRef = cmisService.getRelationship(relDef, source, target); + return (assocRef == null) ? null : new Association(services, assocRef); + } /** * Query for node children @@ -267,6 +289,31 @@ public class CMISScript extends BaseScopableProcessorExtension return results; } + /** + * Query for node relationships + * + * @param node + * @param relDef + * @param includeSubTypes + * @param direction + * @param page + * @return + */ + public PagedResults queryRelationships(ScriptNode node, CMISTypeDefinition relDef, boolean includeSubTypes, CMISRelationshipDirectionEnum direction, Page page) + { + AssociationRef[] relationships = cmisService.getRelationships(node.getNodeRef(), relDef, includeSubTypes, direction); + + Cursor cursor = paging.createCursor(relationships.length, page); + Association[] assocs = new Association[cursor.getRowCount()]; + for (int i = cursor.getStartRow(); i <= cursor.getEndRow(); i++) + { + assocs[i - cursor.getStartRow()] = new Association(services, relationships[i], getScope()); + } + + PagedResults results = paging.createPagedResults(assocs, cursor); + return results; + } + /** * Query for items checked-out to user * diff --git a/source/java/org/alfresco/repo/cmis/rest/CMISTypeDefinitionMethod.java b/source/java/org/alfresco/repo/cmis/rest/CMISTypeDefinitionMethod.java index b022d01ffe..1188907da9 100644 --- a/source/java/org/alfresco/repo/cmis/rest/CMISTypeDefinitionMethod.java +++ b/source/java/org/alfresco/repo/cmis/rest/CMISTypeDefinitionMethod.java @@ -27,7 +27,10 @@ package org.alfresco.repo.cmis.rest; import java.util.List; import org.alfresco.cmis.CMISDictionaryService; +import org.alfresco.cmis.CMISScope; import org.alfresco.cmis.CMISTypeDefinition; +import org.alfresco.cmis.mapping.CMISMapping; +import org.alfresco.repo.template.TemplateAssociation; import org.alfresco.repo.template.TemplateNode; import org.alfresco.service.namespace.QName; @@ -47,8 +50,10 @@ import freemarker.template.TemplateModelException; */ public class CMISTypeDefinitionMethod implements TemplateMethodModelEx { + private static CMISScope[] EMPTY_SCOPES = new CMISScope[] {}; private CMISDictionaryService dictionaryService; + /** * Construct */ @@ -71,6 +76,7 @@ public class CMISTypeDefinitionMethod implements TemplateMethodModelEx if (arg0 instanceof BeanModel) { // extract node type qname + CMISScope[] matchingScopes = EMPTY_SCOPES; QName nodeType = null; Object wrapped = ((BeanModel)arg0).getWrappedObject(); if (wrapped != null) @@ -79,6 +85,11 @@ public class CMISTypeDefinitionMethod implements TemplateMethodModelEx { nodeType = ((TemplateNode)wrapped).getType(); } + else if (wrapped instanceof TemplateAssociation) + { + nodeType = ((TemplateAssociation)wrapped).getTypeQName(); + matchingScopes = new CMISScope[] { CMISScope.RELATIONSHIP }; + } else if (wrapped instanceof QName) { nodeType = (QName)wrapped; @@ -88,7 +99,7 @@ public class CMISTypeDefinitionMethod implements TemplateMethodModelEx // convert to CMIS type if (nodeType != null) { - result = dictionaryService.findTypeForClass(nodeType); + result = dictionaryService.findTypeForClass(nodeType, matchingScopes); } } } diff --git a/source/java/org/alfresco/repo/cmis/rest/test/BaseCMISWebScriptTest.java b/source/java/org/alfresco/repo/cmis/rest/test/BaseCMISWebScriptTest.java index 4ff446ec75..d7fbfcf2db 100644 --- a/source/java/org/alfresco/repo/cmis/rest/test/BaseCMISWebScriptTest.java +++ b/source/java/org/alfresco/repo/cmis/rest/test/BaseCMISWebScriptTest.java @@ -501,6 +501,31 @@ public class BaseCMISWebScriptTest extends BaseWebScriptTest return entry; } + protected Entry createRelationship(IRI parent, String type, String targetId) + throws Exception + { + return createRelationship(parent, type, targetId, "/org/alfresco/repo/cmis/rest/test/createrelationship.atomentry.xml"); + } + + protected Entry createRelationship(IRI parent, String type, String targetId, String atomEntryFile) + throws Exception + { + String createFile = loadString(atomEntryFile); + createFile = createFile.replace("${RELTYPE}", type); + createFile = createFile.replace("${TARGETID}", targetId); + Response res = sendRequest(new PostRequest(parent.toString(), createFile, Format.ATOMENTRY.mimetype()), 201, getAtomValidator()); + assertNotNull(res); + String xml = res.getContentAsString(); + Entry entry = abdera.parseEntry(new StringReader(xml), null); + assertNotNull(entry); + CMISObject object = entry.getExtension(CMISConstants.OBJECT); + assertEquals("relationship", object.getBaseType().getStringValue()); + assertEquals(targetId, object.getTargetId().getStringValue()); + String testFileHREF = (String)res.getHeader("Location"); + assertNotNull(testFileHREF); + return entry; + } + // // General Test Helpers // diff --git a/source/java/org/alfresco/repo/cmis/rest/test/CMISCustomTypeTest.java b/source/java/org/alfresco/repo/cmis/rest/test/CMISCustomTypeTest.java index 58c57a6a76..a17a9a7741 100644 --- a/source/java/org/alfresco/repo/cmis/rest/test/CMISCustomTypeTest.java +++ b/source/java/org/alfresco/repo/cmis/rest/test/CMISCustomTypeTest.java @@ -25,12 +25,15 @@ package org.alfresco.repo.cmis.rest.test; import java.io.StringReader; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.alfresco.abdera.ext.cmis.CMISConstants; import org.alfresco.abdera.ext.cmis.CMISObject; import org.alfresco.abdera.ext.cmis.CMISProperties; import org.alfresco.abdera.ext.cmis.CMISProperty; +import org.alfresco.repo.cmis.rest.CMISScript; import org.alfresco.util.GUID; import org.alfresco.web.scripts.Format; import org.alfresco.web.scripts.TestWebScriptServer.DeleteRequest; @@ -105,11 +108,11 @@ public class CMISCustomTypeTest extends BaseCMISWebScriptTest Feed children = getFeed(childrenLink.getHref()); assertNotNull(children); int entriesBefore = children.getEntries().size(); - Entry folder = createDocument(children.getSelfLink().getHref(), "testCreateCustomDocument", "/org/alfresco/repo/cmis/rest/test/createcustomdocument.atomentry.xml"); + Entry document = createDocument(children.getSelfLink().getHref(), "testCreateCustomDocument", "/org/alfresco/repo/cmis/rest/test/createcustomdocument.atomentry.xml"); Feed feedFolderAfter = getFeed(childrenLink.getHref()); int entriesAfter = feedFolderAfter.getEntries().size(); assertEquals(entriesBefore +1, entriesAfter); - Entry entry = feedFolderAfter.getEntry(folder.getId().toString()); + Entry entry = feedFolderAfter.getEntry(document.getId().toString()); CMISObject object = entry.getExtension(CMISConstants.OBJECT); assertEquals("D/cmiscustom_document", object.getObjectTypeId().getStringValue()); CMISProperty customProp = object.getProperties().find("cmiscustom_docprop_string"); @@ -315,5 +318,91 @@ public class CMISCustomTypeTest extends BaseCMISWebScriptTest assertEquals(false, result2multiValues.get(1)); } } + + public void testCreateRelationship() + throws Exception + { + Entry testFolder = createTestFolder("testCreateCustomRelationship"); + Link childrenLink = testFolder.getLink(CMISConstants.REL_CHILDREN); + assertNotNull(childrenLink); + Feed children = getFeed(childrenLink.getHref()); + assertNotNull(children); + Entry source = createDocument(children.getSelfLink().getHref(), "testSource", "/org/alfresco/repo/cmis/rest/test/createcustomdocument.atomentry.xml"); + assertNotNull(source); + Entry target = createDocument(children.getSelfLink().getHref(), "testTarget", "/org/alfresco/repo/cmis/rest/test/createcustomdocument.atomentry.xml"); + assertNotNull(target); + + // retrieve relationships feed on source + Link relsLink = source.getLink(CMISConstants.REL_RELATIONSHIPS); + assertNotNull(relsLink); + Feed relsBefore = getFeed(relsLink.getHref()); + assertNotNull(relsBefore); + assertEquals(0, relsBefore.getEntries().size()); + + // create relationship between source and target documents + CMISObject targetObject = target.getExtension(CMISConstants.OBJECT); + assertNotNull(targetObject); + String targetId = targetObject.getObjectId().getStringValue(); + assertNotNull(targetId); + Entry rel = createRelationship(relsLink.getHref(), "R/cmiscustom_assoc", targetId); + assertNotNull(rel); + + // check created relationship + CMISObject sourceObject = source.getExtension(CMISConstants.OBJECT); + assertNotNull(sourceObject); + String sourceId = sourceObject.getObjectId().getStringValue(); + assertNotNull(sourceId); + CMISObject relObject = rel.getExtension(CMISConstants.OBJECT); + assertNotNull(relObject); + assertEquals("R/cmiscustom_assoc", relObject.getObjectTypeId().getStringValue()); + assertEquals(sourceId, relObject.getSourceId().getStringValue()); + assertEquals(targetId, relObject.getTargetId().getStringValue()); + assertEquals(source.getSelfLink().getHref(), rel.getLink(CMISConstants.REL_SOURCE).getHref()); + assertEquals(target.getSelfLink().getHref(), rel.getLink(CMISConstants.REL_TARGET).getHref()); + + // check relationships for created item + Map args = new HashMap(); + args.put(CMISScript.ARG_INCLUDE_SUB_RELATIONSHIP_TYPES, "true"); + Feed relsAfter = getFeed(relsLink.getHref(), args); + assertNotNull(relsAfter); + assertEquals(1, relsAfter.getEntries().size()); + } + + public void testGetRelationship() + throws Exception + { + Entry testFolder = createTestFolder("testGetCustomRelationship"); + Link childrenLink = testFolder.getLink(CMISConstants.REL_CHILDREN); + assertNotNull(childrenLink); + Feed children = getFeed(childrenLink.getHref()); + assertNotNull(children); + Entry source = createDocument(children.getSelfLink().getHref(), "testSource", "/org/alfresco/repo/cmis/rest/test/createcustomdocument.atomentry.xml"); + assertNotNull(source); + Entry target = createDocument(children.getSelfLink().getHref(), "testTarget", "/org/alfresco/repo/cmis/rest/test/createcustomdocument.atomentry.xml"); + assertNotNull(target); + + // retrieve relationships feed on source + Link relsLink = source.getLink(CMISConstants.REL_RELATIONSHIPS); + assertNotNull(relsLink); + + // create relationship between source and target documents + CMISObject targetObject = target.getExtension(CMISConstants.OBJECT); + assertNotNull(targetObject); + String targetId = targetObject.getObjectId().getStringValue(); + assertNotNull(targetId); + Entry rel = createRelationship(relsLink.getHref(), "R/cmiscustom_assoc", targetId); + assertNotNull(rel); + + // get created relationship + Entry relEntry = getEntry(rel.getSelfLink().getHref()); + CMISObject relEntryObject = rel.getExtension(CMISConstants.OBJECT); + CMISObject relObject = rel.getExtension(CMISConstants.OBJECT); + assertNotNull(relObject); + assertEquals(relObject.getObjectTypeId().getStringValue(), relEntryObject.getObjectTypeId().getStringValue()); + assertEquals(relObject.getSourceId().getStringValue(), relEntryObject.getSourceId().getStringValue()); + assertEquals(relObject.getTargetId().getStringValue(), relEntryObject.getTargetId().getStringValue()); + assertEquals(source.getSelfLink().getHref(), relEntry.getLink(CMISConstants.REL_SOURCE).getHref()); + assertEquals(target.getSelfLink().getHref(), relEntry.getLink(CMISConstants.REL_TARGET).getHref()); + } } diff --git a/source/java/org/alfresco/repo/cmis/rest/test/createdocument.atomentry.xml b/source/java/org/alfresco/repo/cmis/rest/test/createdocument.atomentry.xml index 4d416d12d3..ded8931397 100644 --- a/source/java/org/alfresco/repo/cmis/rest/test/createdocument.atomentry.xml +++ b/source/java/org/alfresco/repo/cmis/rest/test/createdocument.atomentry.xml @@ -4,8 +4,8 @@ ${NAME} (summary) ${CONTENT} - - document - + + document + diff --git a/source/java/org/alfresco/repo/cmis/rest/test/createrelationship.atomentry.xml b/source/java/org/alfresco/repo/cmis/rest/test/createrelationship.atomentry.xml new file mode 100644 index 0000000000..1d474bf87b --- /dev/null +++ b/source/java/org/alfresco/repo/cmis/rest/test/createrelationship.atomentry.xml @@ -0,0 +1,9 @@ + + + + + ${RELTYPE} + ${TARGETID} + + + diff --git a/source/java/org/alfresco/repo/cmis/rest/xsd/CMISSchemaTest.java b/source/java/org/alfresco/repo/cmis/rest/xsd/CMISSchemaTest.java index 4f80a65277..40d2b01c16 100644 --- a/source/java/org/alfresco/repo/cmis/rest/xsd/CMISSchemaTest.java +++ b/source/java/org/alfresco/repo/cmis/rest/xsd/CMISSchemaTest.java @@ -208,6 +208,13 @@ public class CMISSchemaTest extends TestCase assertValidXML(xml, cmisValidator.getCMISAtomValidator()); } + public void testAtomEntry() + throws Exception + { + String xml = getXML("example_atomentry.xml"); + assertValidXML(xml, cmisValidator.getCMISAtomValidator()); + } + // // Missing from v0.61 // diff --git a/source/java/org/alfresco/repo/cmis/rest/xsd/example_atomentry.xml b/source/java/org/alfresco/repo/cmis/rest/xsd/example_atomentry.xml new file mode 100644 index 0000000000..45e06be6ec --- /dev/null +++ b/source/java/org/alfresco/repo/cmis/rest/xsd/example_atomentry.xml @@ -0,0 +1,26 @@ + + + + Al Brown + http://www.ibm.com/ + albertcbrown@us.ibm.com + + + urn:uuid:70cfe57f-5d59-4293-9cbc-842107117d65 + + + + + + + + + + + + 2009-04-17T13:50:58.656-07:00 + HTML summary of Entry 70cfe57f-5d59-4293-9cbc-842107117d65 + CMIS Example Document + 2009-04-17T13:50:58.656-07:00 + + diff --git a/source/test-resources/cmis/cmisCustomModel.xml b/source/test-resources/cmis/cmisCustomModel.xml index 044a975f04..ccfd2e7231 100644 --- a/source/test-resources/cmis/cmisCustomModel.xml +++ b/source/test-resources/cmis/cmisCustomModel.xml @@ -42,6 +42,19 @@ true + + + + false + false + + + cm:content + false + true + + +