Merge from SEAMIST3

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@10731 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
David Caruana
2008-09-04 11:09:45 +00:00
parent 3e303a86d5
commit fb231f88bc
26 changed files with 749 additions and 41 deletions

View File

@@ -28,6 +28,7 @@
<link rel="cmis-parents" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/parents"/> <link rel="cmis-parents" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/parents"/>
<link rel="cmis-allversions" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/versions"/> <link rel="cmis-allversions" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/versions"/>
<link rel="cmis-stream" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/content" type="${node.mimetype}"/> <link rel="cmis-stream" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/content" type="${node.mimetype}"/>
<link rel="cmis-type" href="${absurl(url.serviceContext)}/api/type/${cmistypeid(node)}"/>
[/#macro] [/#macro]
[#macro documentCMISProps node] [#macro documentCMISProps node]
@@ -119,6 +120,7 @@
<link rel="cmis-parent" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/parent"/> <link rel="cmis-parent" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/parent"/>
<link rel="cmis-children" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/children"/> <link rel="cmis-children" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/children"/>
<link rel="cmis-descendants" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/descendants"/> <link rel="cmis-descendants" href="${absurl(url.serviceContext)}/api/node/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/descendants"/>
<link rel="cmis-type" href="${absurl(url.serviceContext)}/api/type/${cmistypeid(node)}"/>
[/#macro] [/#macro]
[#macro folderCMISProps node] [#macro folderCMISProps node]
@@ -136,6 +138,109 @@
[/#macro] [/#macro]
[#-- --]
[#-- ATOM Entry for Type Definition --]
[#-- --]
[#macro typedef typedef includeProperties=true includeInheritedProperties=true]
[#if true] [#-- TODO: spec issue 40 --]
[@typedefCMISProps typedef includeProperties includeInheritedProperties/]
[#else]
<author><name>${person.properties.userName}</name></author>
<content>${typedef.objectTypeId}</content> [#-- TODO --]
<id>urn:uuid:type-${typedef.objectTypeId}</id>
<link rel="self" href="${absurl(url.serviceContext)}/api/type/${typedef.objectTypeId}"/>
[@typedefCMISLinks typedef/]
<summary>${typedef.description!typedef.objectTypeDisplayName}</summary>
<title>${typedef.objectTypeDisplayName}</title>
<updated>${xmldate(date)}</updated> [#-- TODO --]
[@typedefCMISProps typedef includeProperties/]
[/#if]
[/#macro]
[#macro typedefCMISLinks typedef]
<link rel="cmis-type" href="${absurl(url.serviceContext)}/api/type/${typedef.objectTypeId}"/>
<link rel="cmis-parent" href="${absurl(url.serviceContext)}/api/type/${typedef.parentTypeId}"/>
<link rel="cmis-children" href="${absurl(url.serviceContext)}/api/type/${typedef.objectTypeId}/children"/>
<link rel="cmis-descendants" href="${absurl(url.serviceContext)}/api/type/${typedef.objectTypeId}/descendants"/>
[/#macro]
[#macro typedefCMISProps typedef includeProperties=true includeInheritedProperties=true]
<cmis:type>
<cmis:objectId>${typedef.objectTypeId}</cmis:objectId>
<cmis:baseType>[@cmisBaseType typedef.rootTypeQueryName/]</cmis:baseType> [#-- TODO: remove spec issue 36 --]
<cmis:lastModifiedBy>${xmldate(date)}</cmis:lastModifiedBy> [#-- TODO: remove spec issue 36 --]
<cmis:creationDate>${xmldate(date)}</cmis:creationDate> [#-- TODO: remove spec issue 36 --]
<cmis:queryName>${typedef.objectTypeQueryName}</cmis:queryName>
<cmis:displayName>[#if typedef.objectTypeDisplayName??]${typedef.objectTypeDisplayName?xml}[/#if]</cmis:displayName>
<cmis:baseTypeQueryName>${typedef.rootTypeQueryName}</cmis:baseTypeQueryName>
<cmis:parentId>${typedef.parentTypeId!""}</cmis:parentId>
<cmis:description>[#if typedef.description??]${typedef.description?xml}[/#if]</cmis:description>
<cmis:isCreatable>${typedef.creatable?string}</cmis:isCreatable>
<cmis:isFileable>${typedef.fileable?string}</cmis:isFileable>
<cmis:isQueryable>${typedef.queryable?string}</cmis:isQueryable>
<cmis:isControllable>${typedef.controllable?string}</cmis:isControllable>
<cmis:isVersionable>${typedef.versionable?string}</cmis:isVersionable>
<cmis:contentStreamAllowed>[@cmisContentStreamAllowed typedef.contentStreamAllowed/]</cmis:contentStreamAllowed> [#-- TODO: spec issue 37 --]
[#if includeProperties]
[#list typedef.propertyDefinitions?values as propdef]
[#if includeInheritedProperties || !propdef.inherited]
[@propdefCMISProps propdef/]
[/#if]
[/#list]
[/#if]
</cmis:type>
[/#macro]
[#macro propdefCMISProps propdef]
<cmis:property cmis:id="${propdef.propertyId}">
<cmis:propertyName>${propdef.propertyName}</cmis:propertyName>
<cmis:propertyId>${propdef.propertyId}</cmis:propertyId>
<cmis:displayName>[#if propdef.displayName??]${propdef.displayName?xml}[/#if]</cmis:displayName>
<cmis:description>[#if propdef.description??]${propdef.description?xml}[/#if]</cmis:description>
<cmis:isInherited>${propdef.inherited?string}</cmis:isInherited>
<cmis:propertyType>${propdef.propertyType}</cmis:propertyType>
<cmis:cardinality>[@cmisCardinality propdef.cardinality/]</cmis:cardinality>
[#if propdef.maximumLength != -1]
<cmis:maxLength>${propdef.maximumLength}</cmis:maxLength>
[/#if]
[@cmisChoices propdef.choices/]
<cmis:isOpenChoice>${propdef.openChoice?string}</cmis:isOpenChoice>
<cmis:isRequired>${propdef.required?string}</cmis:isRequired>
<cmis:defaultValue>${propdef.defaultValue!""}</cmis:defaultValue>
<cmis:updateability>[@cmisUpdatability propdef.updatability/]</cmis:updateability> [#-- TODO spec issue 38 --]
<cmis:isQueryable>${propdef.queryable?string}</cmis:isQueryable>
<cmis:isOrderable>${propdef.orderable?string}</cmis:isOrderable>
</cmis:property>
[/#macro]
[#-- TODO: spec issue 40 --]
[#macro cmisBaseType rootType]
[#if rootType = "DOCUMENT_OBJECT_TYPE"]document[#elseif rootType = "FOLDER_OBJECT_TYPE"]folder[#elseif rootType = "RELATIONSHIP_OBJECT_TYPE"]relationship[#elseif rootType = "POLICY_OBJECT_TYPE"]policy[#else][/#if][/#macro]
[#-- TODO: spec issue 37 --]
[#macro cmisContentStreamAllowed allowed]
[#if allowed = "NOT_ALLOWED"]notallowed[#elseif allowed = "ALLOWED"]allowed[#elseif allowed = "REQUIRED"]required[#else][/#if][/#macro]
[#-- TODO: spec issue 37 --]
[#macro cmisCardinality cardinality]
[#if cardinality = "SINGLE_VALUED"]Single[#elseif cardinality = "MULTI_VALUED"]Multi[#else][/#if][/#macro]
[#-- TODO: spec issue 37/38 --]
[#macro cmisUpdatability updatability]
[#if updatability = "READ_ONLY"]ro[#elseif updatability = "READ_AND_WRITE"]rw[#elseif updatability = "READ_AND_WRITE_WHEN_CHECKED_OUT"]checkedout[/#if][/#macro]
[#-- TODO: spec issue 39 --]
[#macro cmisChoices choices]
[#if choices?exists]
[#list choices as choice]
<cmis:choices index="${choice.index}">${choice.value}
[@cmisChoices choice.children/]
</cmis:choices>
[/#list]
[/#if]
[/#macro]
[#-- Helper to render Alfresco content type to Atom content type --] [#-- Helper to render Alfresco content type to Atom content type --]
[#macro contenttype type][#if type == "text/html"]text[#elseif type == "text/xhtml"]xhtml[#elseif type == "text/plain"]text<#else>${type}[/#if][/#macro] [#macro contenttype type][#if type == "text/html"]text[#elseif type == "text/xhtml"]xhtml[#elseif type == "text/plain"]text<#else>${type}[/#if][/#macro]

View File

@@ -1,29 +1,29 @@
<#macro links cursor pageArg="pageNo" skipArg="skipCount"> <#macro links cursor pageNo="pageNo" pageSize="pageSize" skipCount="skipCount" maxItems="maxItems">
<#if cursor.pageType = "PAGE"> <#if cursor.pageType = "PAGE">
<#if cursor.hasFirstPage> <#if cursor.hasFirstPage>
<link rel="first" href="${absurl(encodeuri(scripturl(argreplace(url.args, pageArg, cursor.firstPage))))?xml}" type="${format.type}"/> <link rel="first" href="${absurl(encodeuri(scripturl(argreplace(url.args, pageNo, cursor.firstPage, pageSize, cursor.pageSize))))?xml}" type="${format.type}"/>
</#if> </#if>
<#if cursor.hasLastPage> <#if cursor.hasLastPage>
<link rel="last" href="${absurl(encodeuri(scripturl(argreplace(url.args, pageArg, cursor.lastPage))))?xml}" type="${format.type}"/> <link rel="last" href="${absurl(encodeuri(scripturl(argreplace(url.args, pageNo, cursor.lastPage, pageSize, cursor.pageSize))))?xml}" type="${format.type}"/>
</#if> </#if>
<#if cursor.hasPrevPage> <#if cursor.hasPrevPage>
<link rel="prev" href="${absurl(encodeuri(scripturl(argreplace(url.args, pageArg, cursor.prevPage))))?xml}" type="${format.type}"/> <link rel="prev" href="${absurl(encodeuri(scripturl(argreplace(url.args, pageNo, cursor.prevPage, pageSize, cursor.pageSize))))?xml}" type="${format.type}"/>
</#if> </#if>
<#if cursor.hasNextPage> <#if cursor.hasNextPage>
<link rel="next" href="${absurl(encodeuri(scripturl(argreplace(url.args, pageArg, cursor.nextPage))))?xml}" type="${format.type}"/> <link rel="next" href="${absurl(encodeuri(scripturl(argreplace(url.args, pageNo, cursor.nextPage, pageSize, cursor.pageSize))))?xml}" type="${format.type}"/>
</#if> </#if>
<#else> <#else>
<#if cursor.hasFirstPage> <#if cursor.hasFirstPage>
<link rel="first" href="${absurl(encodeuri(scripturl(argreplace(url.args, skipArg, cursor.firstPage))))?xml}" type="${format.type}"/> <link rel="first" href="${absurl(encodeuri(scripturl(argreplace(url.args, skipCount, cursor.firstPage, maxItems, cursor.pageSize))))?xml}" type="${format.type}"/>
</#if> </#if>
<#if cursor.hasLastPage> <#if cursor.hasLastPage>
<link rel="last" href="${absurl(encodeuri(scripturl(argreplace(url.args, skipArg, cursor.lastPage))))?xml}" type="${format.type}"/> <link rel="last" href="${absurl(encodeuri(scripturl(argreplace(url.args, skipCount, cursor.lastPage, maxItems, cursor.pageSize))))?xml}" type="${format.type}"/>
</#if> </#if>
<#if cursor.hasPrevPage> <#if cursor.hasPrevPage>
<link rel="prev" href="${absurl(encodeuri(scripturl(argreplace(url.args, skipArg, cursor.prevPage))))?xml}" type="${format.type}"/> <link rel="prev" href="${absurl(encodeuri(scripturl(argreplace(url.args, skipCount, cursor.prevPage, maxItems, cursor.pageSize))))?xml}" type="${format.type}"/>
</#if> </#if>
<#if cursor.hasNextPage> <#if cursor.hasNextPage>
<link rel="next" href="${absurl(encodeuri(scripturl(argreplace(url.args, skipArg, cursor.nextPage))))?xml}" type="${format.type}"/> <link rel="next" href="${absurl(encodeuri(scripturl(argreplace(url.args, skipCount, cursor.nextPage, maxItems, cursor.pageSize))))?xml}" type="${format.type}"/>
</#if> </#if>
</#if> </#if>
</#macro> </#macro>

View File

@@ -4,8 +4,6 @@
<workspace cmis:id="${server.id}"> <workspace cmis:id="${server.id}">
<atom:title>${server.name}</atom:title> <atom:title>${server.name}</atom:title>
<#-- TODO: cmis version -->
<cmis:repositoryInfo> <cmis:repositoryInfo>
<cmis:repositoryId>${server.id}</cmis:repositoryId> <cmis:repositoryId>${server.id}</cmis:repositoryId>
<cmis:repositoryName>${server.name}</cmis:repositoryName> <cmis:repositoryName>${server.name}</cmis:repositoryName>
@@ -35,10 +33,8 @@
<collection href="${absurl(url.serviceContext)}/api/unfiled" cmis:collectionType="unfiled"> <collection href="${absurl(url.serviceContext)}/api/unfiled" cmis:collectionType="unfiled">
<atom:title>unfiled collection</atom:title> <atom:title>unfiled collection</atom:title>
</collection> </collection>
<collection href="${absurl(url.serviceContext)}/api/types" cmis:collectionType="types">
<#-- TODO: collection resources --> <atom:title>type collection</atom:title>
<collection href="http://example.org/cmis/main?types" cmis:collectionType="types">
<atom:title>CMIS Types</atom:title>
</collection> </collection>
</workspace> </workspace>

View File

@@ -8,7 +8,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<feed [@nsLib.feedNS/]> <feed [@nsLib.feedNS/]>
[@feedLib.generic id="urn:uuid:checkedout" title="Checked out Documents" author="${person.properties.userName}"] [@feedLib.generic "urn:uuid:checkedout" "Checked out Documents" "${person.properties.userName}"]
[@pagingLib.links cursor=cursor/] [@pagingLib.links cursor=cursor/]
[/@feedLib.generic] [/@feedLib.generic]

View File

@@ -29,7 +29,7 @@ script:
// TODO: includeAllowableActions // TODO: includeAllowableActions
// retrieve checked-out // retrieve checked-out
var page = paging.createPageOrWindow(args.pageNo, args.pageSize, cmis.findArg(args.skipCount, headers["CMIS-skipCount"]), cmis.findArg(args.maxItems, headers["CMIS-maxItems"])); var page = paging.createPageOrWindow(args, headers);
var paged = cmis.queryCheckedOut(person.properties.userName, model.folder, model.includeDescendants, page); var paged = cmis.queryCheckedOut(person.properties.userName, model.folder, model.includeDescendants, page);
model.results = paged.results; model.results = paged.results;
model.cursor = paged.cursor; model.cursor = paged.cursor;

View File

@@ -1,8 +1,8 @@
<webscript> <webscript>
<shortname>Retrieve list of children</shortname> <shortname>Retrieve list of children</shortname>
<description>Retrieve list of child folders and/or documents</description> <description>Retrieve list of child folders and/or documents</description>
<url>/api/node/{store_type}/{store_id}/{id}/children?types={types}&amp;filter={filter?}&amp;skipCount={skipCount?}&amp;maxChildren={maxChildren?}</url> <url>/api/node/{store_type}/{store_id}/{id}/children?types={types}&amp;filter={filter?}&amp;skipCount={skipCount?}&amp;maxItems={maxItems?}</url>
<url>/api/path/{store_type}/{store_id}/{id}/children?types={types}&amp;filter={filter?}&amp;skipCount={skipCount?}&amp;maxChildren={maxChildren?}</url> <url>/api/path/{store_type}/{store_id}/{id}/children?types={types}&amp;filter={filter?}&amp;skipCount={skipCount?}&amp;maxItems={maxItems?}</url>
<authentication>guest</authentication> <authentication>guest</authentication>
<format default="atomfeed">argument</format> <format default="atomfeed">argument</format>
<family>CMIS</family> <family>CMIS</family>

View File

@@ -24,7 +24,7 @@ script:
// TODO: property filters // TODO: property filters
// retrieve children // retrieve children
var page = paging.createPageOrWindow(args.pageNo, args.pageSize, cmis.findArg(args.skipCount, headers["CMIS-skipCount"]), cmis.findArg(args.maxItems, headers["CMIS-maxItems"])); var page = paging.createPageOrWindow(args, headers);
var paged = cmis.queryChildren(model.node, model.types, page); var paged = cmis.queryChildren(model.node, model.types, page);
model.results = paged.results; model.results = paged.results;
model.cursor = paged.cursor; model.cursor = paged.cursor;

View File

@@ -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]
<?xml version="1.0" encoding="UTF-8"?>
<entry [@nsLib.entryNS/]>
[@entryLib.typedef typedef true includeInheritedProperties/]
</entry>
[/#compress]

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>Retrieve a Type</shortname>
<description>Retrieve a Type Definition</description>
<url>/api/type/{typeId}?includeInheritedProperties={includeInheritedProperties?}</url>
<authentication>user</authentication>
<format default="atomentry"/>
<family>CMIS</family>
</webscript>

View File

@@ -0,0 +1,17 @@
script:
{
// query type
var typeId = url.templateArgs.typeId;
model.typedef = cmis.queryType(typeId);
if (model.typedef === null)
{
status.code = 404;
status.message = "Type " + typeId + " not found";
status.redirect = true;
break script;
}
// handle inherited properties
var includeInheritedProperties = cmis.findArg(args.includeInheritedProperties, headers["CMIS-includeInheritedProperties"]);
model.includeInheritedProperties = includeInheritedProperties == "false" ? false : true;
}

View File

@@ -0,0 +1,26 @@
[#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]
<?xml version="1.0" encoding="UTF-8"?>
<feed [@nsLib.feedNS/]>
[@feedLib.generic "urn:uuid:type-${typedef.objectTypeId}-children" "Child types of ${typedef.objectTypeId}" "${person.properties.userName}"]
[@pagingLib.links cursor=cursor/]
[/@feedLib.generic]
[#list results as child]
<entry>
[@entryLib.typedef typedef=child includeProperties=returnPropertyDefinitions/]
</entry>
[/#list]
[@feedLib.hasMore more=cursor/]
[@pagingLib.opensearch cursor=cursor/]
</feed>
[/#compress]

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>Retrieve list of child Types</shortname>
<description>Retrieve list of all child Types</description>
<url>/api/type/{typeId}/children?includePropertyDefinitions={includePropertyDefinitions?}&amp;skipCount={skipCount?}&amp;maxItems={maxItems?}</url>
<authentication>user</authentication>
<format default="atomfeed"/>
<family>CMIS</family>
</webscript>

View File

@@ -0,0 +1,24 @@
script:
{
// query type
var typeId = url.templateArgs.typeId;
model.typedef = cmis.queryType(typeId);
if (model.typedef === null)
{
status.code = 404;
status.message = "Type " + typeId + " not found";
status.redirect = true;
break script;
}
// query type children
var page = paging.createPageOrWindow(args, headers);
var paged = cmis.queryTypeHierarchy(model.typedef, false, page);
model.results = paged.results;
model.cursor = paged.cursor;
// handle property definitions
// TODO: spec issue 34
var returnPropertyDefinitions = cmis.findArg(args.includePropertyDefinitions, headers["CMIS-includePropertyDefinitions"]);
model.returnPropertyDefinitions = returnPropertyDefinitions == "true" ? true : false;
}

View File

@@ -0,0 +1,26 @@
[#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]
<?xml version="1.0" encoding="UTF-8"?>
<feed [@nsLib.feedNS/]>
[@feedLib.generic "urn:uuid:type-${typedef.objectTypeId}-descendants" "Descendant types of ${typedef.objectTypeId}" "${person.properties.userName}"]
[@pagingLib.links cursor=cursor/]
[/@feedLib.generic]
[#list results as child]
<entry>
[@entryLib.typedef typedef=child includeProperties=returnPropertyDefinitions/]
</entry>
[/#list]
[@feedLib.hasMore more=cursor/]
[@pagingLib.opensearch cursor=cursor/]
</feed>
[/#compress]

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>Retrieve list of descendant Types</shortname>
<description>Retrieve list of all descendant Types</description>
<url>/api/type/{typeId}/descendants?includePropertyDefinitions={includePropertyDefinitions?}&amp;skipCount={skipCount?}&amp;maxItems={maxItems?}</url>
<authentication>user</authentication>
<format default="atomfeed"/>
<family>CMIS</family>
</webscript>

View File

@@ -0,0 +1,24 @@
script:
{
// query type
var typeId = url.templateArgs.typeId;
model.typedef = cmis.queryType(typeId);
if (model.typedef === null)
{
status.code = 404;
status.message = "Type " + typeId + " not found";
status.redirect = true;
break script;
}
// query type descendants
var page = paging.createPageOrWindow(args, headers);
var paged = cmis.queryTypeHierarchy(model.typedef, true, page);
model.results = paged.results;
model.cursor = paged.cursor;
// handle property definitions
// TODO: spec issue 34
var returnPropertyDefinitions = cmis.findArg(args.includePropertyDefinitions, headers["CMIS-includePropertyDefinitions"]);
model.returnPropertyDefinitions = returnPropertyDefinitions == "true" ? true : false;
}

View File

@@ -0,0 +1,27 @@
[#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]
<?xml version="1.0" encoding="UTF-8"?>
<feed [@nsLib.feedNS/]>
[#assign title][#if type = "all"]All Types[#else]Type ${type}[/#if][/#assign]
[@feedLib.generic "urn:uuid:types-${type}" "${title}" "${person.properties.userName}"]
[@pagingLib.links cursor=cursor/]
[/@feedLib.generic]
[#list results as child]
[#-- <entry> TODO: spec issue 40 --]
[@entryLib.typedef typedef=child includeProperties=returnPropertyDefinitions/]
[#-- </entry> TODO: spec issue 40 --]
[/#list]
[@feedLib.hasMore more=cursor/]
[@pagingLib.opensearch cursor=cursor/]
</feed>
[/#compress]

View File

@@ -0,0 +1,8 @@
<webscript>
<shortname>Retrieve list of all Types</shortname>
<description>Retrieve list of all Types</description>
<url>/api/types?type={type?}&amp;includePropertyDefinitions={includePropertyDefinitions?}&amp;skipCount={skipCount?}&amp;maxItems={maxItems?}</url>
<authentication>user</authentication>
<format default="atomfeed"/>
<family>CMIS</family>
</webscript>

View File

@@ -0,0 +1,38 @@
script:
{
// process paging
var page = paging.createPageOrWindow(args, headers);
// query types
// TODO: spec issue 34
var typeId = cmis.findArg(args.type, headers["CMIS-type"]);
if (typeId === null)
{
// query all types
var paged = cmis.queryTypes(page);
model.results = paged.results;
model.cursor = paged.cursor;
model.type = "all";
}
else
{
// 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;
}
var paged = cmis.queryTypeHierarchy(typedef, true, page);
model.results = paged.results;
model.cursor = paged.cursor;
model.type = typeId;
}
// handle property definitions
// TODO: spec issue 34
var returnPropertyDefinitions = cmis.findArg(args.includePropertyDefinitions, headers["CMIS-includePropertyDefinitions"]);
model.returnPropertyDefinitions = returnPropertyDefinitions == "true" ? true : false;
}

View File

@@ -100,6 +100,15 @@
</entry> </entry>
</map> </map>
</property> </property>
<property name="templateObjects">
<map merge="true">
<entry key="cmistypeid">
<bean class="org.alfresco.repo.cmis.rest.CMISTypeIdMethod">
<constructor-arg><ref bean="CMISMapping"/></constructor-arg>
</bean>
</entry>
</map>
</property>
<property name="registryFactory"> <property name="registryFactory">
<bean class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"> <bean class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName"><idref local="webscripts.registry.prototype"/></property> <property name="targetBeanName"><idref local="webscripts.registry.prototype"/></property>
@@ -395,6 +404,7 @@
<property name="serviceRegistry" ref="ServiceRegistry" /> <property name="serviceRegistry" ref="ServiceRegistry" />
<property name="repository" ref="repositoryHelper" /> <property name="repository" ref="repositoryHelper" />
<property name="CMISService" ref="CMISService" /> <property name="CMISService" ref="CMISService" />
<property name="CMISDictionaryService" ref="CMISDictionaryService" />
<property name="paging" ref="webscripts.js.paging" /> <property name="paging" ref="webscripts.js.paging" />
</bean> </bean>

View File

@@ -24,8 +24,15 @@
*/ */
package org.alfresco.repo.cmis.rest; package org.alfresco.repo.cmis.rest;
import java.util.Collection;
import java.util.Iterator;
import org.alfresco.cmis.CMISService; import org.alfresco.cmis.CMISService;
import org.alfresco.cmis.CMISService.TypesFilter; import org.alfresco.cmis.CMISService.TypesFilter;
import org.alfresco.cmis.dictionary.CMISDictionaryService;
import org.alfresco.cmis.dictionary.CMISTypeDefinition;
import org.alfresco.cmis.dictionary.CMISTypeId;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.jscript.BaseScopableProcessorExtension; import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
import org.alfresco.repo.jscript.ScriptNode; import org.alfresco.repo.jscript.ScriptNode;
import org.alfresco.repo.model.Repository; import org.alfresco.repo.model.Repository;
@@ -49,6 +56,7 @@ public class CMISScript extends BaseScopableProcessorExtension
private ServiceRegistry services; private ServiceRegistry services;
private Repository repository; private Repository repository;
private CMISService cmisService; private CMISService cmisService;
private CMISDictionaryService cmisDictionaryService;
private Paging paging; private Paging paging;
@@ -83,7 +91,7 @@ public class CMISScript extends BaseScopableProcessorExtension
} }
/** /**
* Set the CMIS Navigation helper * Set the CMIS Service
* *
* @param cmisService * @param cmisService
*/ */
@@ -91,7 +99,17 @@ public class CMISScript extends BaseScopableProcessorExtension
{ {
this.cmisService = cmisService; this.cmisService = cmisService;
} }
/**
* Set the CMIS Dictionary Service
*
* @param cmisDictionaryService
*/
public void setCMISDictionaryService(CMISDictionaryService cmisDictionaryService)
{
this.cmisDictionaryService = cmisDictionaryService;
}
/** /**
* Gets the supported CMIS Version * Gets the supported CMIS Version
* *
@@ -273,6 +291,83 @@ public class CMISScript extends BaseScopableProcessorExtension
return results; return results;
} }
/**
* Query for all Type Definitions
*
* @param page
* @return paged result set of types
*/
public PagedResults queryTypes(Page page)
{
Collection<CMISTypeId> typeIds = cmisDictionaryService.getAllObjectTypeIds();
Cursor cursor = paging.createCursor(typeIds.size(), page);
// skip
Iterator<CMISTypeId> iterTypeIds = typeIds.iterator();
for (int i = 0; i < cursor.getStartRow(); i++)
{
iterTypeIds.next();
}
// get types for page
CMISTypeDefinition[] types = new CMISTypeDefinition[cursor.getRowCount()];
for (int i = cursor.getStartRow(); i <= cursor.getEndRow(); i++)
{
types[i - cursor.getStartRow()] = cmisDictionaryService.getType(iterTypeIds.next());
}
PagedResults results = paging.createPagedResults(types, cursor);
return results;
}
/**
* Query for all Type Definitions in a type hierarchy
*
* @param page
* @return paged result set of types
*/
public PagedResults queryTypeHierarchy(CMISTypeDefinition typedef, boolean descendants, Page page)
{
Collection<CMISTypeId> typeIds = cmisDictionaryService.getChildTypeIds(typedef.getObjectTypeId(), descendants);
Cursor cursor = paging.createCursor(typeIds.size(), page);
// skip
Iterator<CMISTypeId> iterTypeIds = typeIds.iterator();
for (int i = 0; i < cursor.getStartRow(); i++)
{
iterTypeIds.next();
}
// get types for page
CMISTypeDefinition[] types = new CMISTypeDefinition[cursor.getRowCount()];
for (int i = cursor.getStartRow(); i <= cursor.getEndRow(); i++)
{
types[i - cursor.getStartRow()] = cmisDictionaryService.getType(iterTypeIds.next());
}
PagedResults results = paging.createPagedResults(types, cursor);
return results;
}
/**
* Query for all Type Definitions
*
* @param page
* @return paged result set of types
*/
public CMISTypeDefinition queryType(String typeId)
{
try
{
CMISTypeId cmisTypeId = cmisDictionaryService.getCMISMapping().getCmisTypeId(typeId);
return cmisDictionaryService.getType(cmisTypeId);
}
catch(AlfrescoRuntimeException e)
{
return null;
}
}
/** /**
* Resolve to a Types Filter * Resolve to a Types Filter
* *

View File

@@ -26,7 +26,9 @@ package org.alfresco.repo.cmis.rest;
import java.io.StringReader; import java.io.StringReader;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.alfresco.util.GUID; import org.alfresco.util.GUID;
import org.alfresco.web.scripts.Format; import org.alfresco.web.scripts.Format;
@@ -132,6 +134,15 @@ public class CMISTest extends BaseCMISWebScriptTest
return rootHREF; return rootHREF;
} }
private IRI getTypesCollection(Service service)
{
Collection root = service.getCollection("Main Repository", "type collection");
assertNotNull(root);
IRI rootHREF = root.getHref();
assertNotNull(rootHREF);
return rootHREF;
}
private Entry createFolder(IRI parent, String name) private Entry createFolder(IRI parent, String name)
throws Exception throws Exception
{ {
@@ -247,8 +258,7 @@ public class CMISTest extends BaseCMISWebScriptTest
public void testRepository() public void testRepository()
throws Exception throws Exception
{ {
Service service = getRepository(); IRI rootHREF = getRootCollection(getRepository());
IRI rootHREF = getRootCollection(service);
sendRequest(new GetRequest(rootHREF.toString()), 200, getAtomValidator()); sendRequest(new GetRequest(rootHREF.toString()), 200, getAtomValidator());
} }
@@ -331,7 +341,7 @@ public class CMISTest extends BaseCMISWebScriptTest
assertNotNull(documentRes); assertNotNull(documentRes);
String documentXML = documentRes.getContentAsString(); String documentXML = documentRes.getContentAsString();
assertNotNull(documentXML); assertNotNull(documentXML);
IRI checkedoutHREF = getCheckedOutCollection(service); IRI checkedoutHREF = getCheckedOutCollection(getRepository());
Response pwcRes = sendRequest(new PostRequest(checkedoutHREF.toString(), documentXML, Format.ATOMENTRY.mimetype()), 201, getAtomValidator()); Response pwcRes = sendRequest(new PostRequest(checkedoutHREF.toString(), documentXML, Format.ATOMENTRY.mimetype()), 201, getAtomValidator());
assertNotNull(pwcRes); assertNotNull(pwcRes);
Entry pwc = abdera.parseEntry(new StringReader(pwcRes.getContentAsString()), null); Entry pwc = abdera.parseEntry(new StringReader(pwcRes.getContentAsString()), null);
@@ -344,10 +354,49 @@ public class CMISTest extends BaseCMISWebScriptTest
assertNotNull(children.getEntry(document2.getId().toString())); assertNotNull(children.getEntry(document2.getId().toString()));
assertNotNull(children.getEntry(document3.getId().toString())); assertNotNull(children.getEntry(document3.getId().toString()));
assertNull(children.getEntry(pwc.getId().toString())); assertNull(children.getEntry(pwc.getId().toString()));
// TODO: paging
} }
public void testChildrenPaging()
throws Exception
{
// create multiple children
Set<IRI> docIds = new HashSet<IRI>();
Entry testFolder = createTestFolder("testChildrenPaging");
Link childrenLink = testFolder.getLink(CMISConstants.REL_CHILDREN);
assertNotNull(childrenLink);
for (int i = 0; i < 15; i++)
{
Entry document = createDocument(childrenLink.getHref(), "testChildrenPaging" + i);
assertNotNull(document);
docIds.add(document.getId());
}
assertEquals(15, docIds.size());
// get children, ensure they exist (but not private working copy)
int nextCount = 0;
Map<String, String> args = new HashMap<String, String>();
args.put("maxItems", "4");
IRI childrenHREF = childrenLink.getHref();
while (childrenHREF != null)
{
nextCount++;
Feed types = getFeed(childrenHREF, args);
assertNotNull(types);
assertEquals(nextCount < 4 ? 4 : 3, types.getEntries().size());
for (Entry entry : types.getEntries())
{
docIds.remove(entry.getId());
}
// next page
Link nextLink = types.getLink("next");
childrenHREF = (nextLink != null) ? nextLink.getHref() : null;
args = null;
};
assertEquals(4, nextCount);
assertEquals(0, docIds.size());
}
public void testGetParent() public void testGetParent()
throws Exception throws Exception
{ {
@@ -480,7 +529,7 @@ public class CMISTest extends BaseCMISWebScriptTest
// retrieve checkouts within scope of test checkout folder // retrieve checkouts within scope of test checkout folder
Service repository = getRepository(); Service repository = getRepository();
assertNotNull(repository); assertNotNull(repository);
IRI checkedoutHREF = getCheckedOutCollection(service); IRI checkedoutHREF = getCheckedOutCollection(getRepository());
Map<String, String> args = new HashMap<String, String>(); Map<String, String> args = new HashMap<String, String>();
args.put("folderId", scopeId); args.put("folderId", scopeId);
Feed checkedout = getFeed(new IRI(checkedoutHREF.toString()), args); Feed checkedout = getFeed(new IRI(checkedoutHREF.toString()), args);
@@ -503,7 +552,7 @@ public class CMISTest extends BaseCMISWebScriptTest
assertNotNull(documentXML); assertNotNull(documentXML);
// checkout // checkout
IRI checkedoutHREF = getCheckedOutCollection(service); IRI checkedoutHREF = getCheckedOutCollection(getRepository());
Response pwcRes = sendRequest(new PostRequest(checkedoutHREF.toString(), documentXML, Format.ATOMENTRY.mimetype()), 201, getAtomValidator()); Response pwcRes = sendRequest(new PostRequest(checkedoutHREF.toString(), documentXML, Format.ATOMENTRY.mimetype()), 201, getAtomValidator());
assertNotNull(pwcRes); assertNotNull(pwcRes);
// TODO: test private working copy properties // TODO: test private working copy properties
@@ -533,7 +582,7 @@ public class CMISTest extends BaseCMISWebScriptTest
assertNotNull(xml); assertNotNull(xml);
// checkout // checkout
IRI checkedoutHREF = getCheckedOutCollection(service); IRI checkedoutHREF = getCheckedOutCollection(getRepository());
Response pwcRes = sendRequest(new PostRequest(checkedoutHREF.toString(), xml, Format.ATOMENTRY.mimetype()), 201, getAtomValidator()); Response pwcRes = sendRequest(new PostRequest(checkedoutHREF.toString(), xml, Format.ATOMENTRY.mimetype()), 201, getAtomValidator());
assertNotNull(pwcRes); assertNotNull(pwcRes);
String pwcXml = pwcRes.getContentAsString(); String pwcXml = pwcRes.getContentAsString();
@@ -578,7 +627,7 @@ public class CMISTest extends BaseCMISWebScriptTest
assertNotNull(xml); assertNotNull(xml);
// checkout // checkout
IRI checkedoutHREF = getCheckedOutCollection(service); IRI checkedoutHREF = getCheckedOutCollection(getRepository());
Response pwcRes = sendRequest(new PostRequest(checkedoutHREF.toString(), xml, Format.ATOMENTRY.mimetype()), 201, getAtomValidator()); Response pwcRes = sendRequest(new PostRequest(checkedoutHREF.toString(), xml, Format.ATOMENTRY.mimetype()), 201, getAtomValidator());
assertNotNull(pwcRes); assertNotNull(pwcRes);
Entry pwc = abdera.parseEntry(new StringReader(pwcRes.getContentAsString()), null); Entry pwc = abdera.parseEntry(new StringReader(pwcRes.getContentAsString()), null);
@@ -651,7 +700,7 @@ public class CMISTest extends BaseCMISWebScriptTest
assertNotNull(xml); assertNotNull(xml);
// checkout // checkout
IRI checkedoutHREF = getCheckedOutCollection(service); IRI checkedoutHREF = getCheckedOutCollection(getRepository());
Response pwcRes = sendRequest(new PostRequest(checkedoutHREF.toString(), xml, Format.ATOMENTRY.mimetype()), 201, getAtomValidator()); Response pwcRes = sendRequest(new PostRequest(checkedoutHREF.toString(), xml, Format.ATOMENTRY.mimetype()), 201, getAtomValidator());
assertNotNull(pwcRes); assertNotNull(pwcRes);
Entry pwc = abdera.parseEntry(new StringReader(pwcRes.getContentAsString()), null); Entry pwc = abdera.parseEntry(new StringReader(pwcRes.getContentAsString()), null);
@@ -712,7 +761,7 @@ public class CMISTest extends BaseCMISWebScriptTest
String xml = documentRes.getContentAsString(); String xml = documentRes.getContentAsString();
assertNotNull(xml); assertNotNull(xml);
IRI checkedoutHREF = getCheckedOutCollection(service); IRI checkedoutHREF = getCheckedOutCollection(getRepository());
for (int i = 0; i < NUMBER_OF_VERSIONS; i++) for (int i = 0; i < NUMBER_OF_VERSIONS; i++)
{ {
// checkout // checkout
@@ -751,7 +800,52 @@ public class CMISTest extends BaseCMISWebScriptTest
} }
} }
public void testGetAllTypeDefinitions()
throws Exception
{
IRI typesHREF = getTypesCollection(getRepository());
Feed types = getFeed(typesHREF);
assertNotNull(types);
Feed typesWithProps = getFeed(typesHREF);
assertNotNull(typesWithProps);
// TODO: spec issue 40
for (Entry type : types.getEntries())
{
Entry retrievedType = getEntry(type.getSelfLink().getHref());
assertEquals(type.getId(), retrievedType.getId());
assertEquals(type.getTitle(), retrievedType.getTitle());
// TODO: type specific properties - extension to Abdera
}
}
public void testGetHierarchyTypeDefinitions()
throws Exception
{
IRI typesHREF = getTypesCollection(getRepository());
Map<String, String> args = new HashMap<String, String>();
args.put("type", "FOLDER_OBJECT_TYPE");
args.put("includePropertyDefinitions", "true");
args.put("maxItems", "5");
while (typesHREF != null)
{
Feed types = getFeed(typesHREF, args);
// TODO: spec issue 40
for (Entry type : types.getEntries())
{
Entry retrievedType = getEntry(type.getSelfLink().getHref());
assertEquals(type.getId(), retrievedType.getId());
assertEquals(type.getTitle(), retrievedType.getTitle());
// TODO: type specific properties - extension to Abdera
}
// next page
Link nextLink = types.getLink("next");
typesHREF = (nextLink != null) ? nextLink.getHref() : null;
args.remove("maxItems");
};
}
// public void testUnfiled() // public void testUnfiled()
// { // {
// } // }

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.cmis.rest;
import java.util.List;
import org.alfresco.cmis.dictionary.CMISMapping;
import org.alfresco.cmis.dictionary.CMISTypeId;
import org.alfresco.repo.jscript.ScriptNode;
import org.alfresco.repo.template.TemplateNode;
import org.alfresco.service.namespace.QName;
import freemarker.ext.beans.BeanModel;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException;
/**
* Custom FreeMarker Template language method.
* <p>
* Retrieve the CMIS Type Id for an Alfresco node
* <p>
* Usage: cmisTypeId(ScriptNode node)
* cmisTypeId(QName nodeType)
*
* @author davidc
*/
public final class CMISTypeIdMethod implements TemplateMethodModelEx
{
private CMISMapping mappingService;
/**
* Construct
*/
public CMISTypeIdMethod(CMISMapping mappingService)
{
this.mappingService = mappingService;
}
/**
* @see freemarker.template.TemplateMethodModel#exec(java.util.List)
*/
public Object exec(List args) throws TemplateModelException
{
CMISTypeId result = null;
if (args.size() == 1)
{
Object arg0 = args.get(0);
if (arg0 instanceof BeanModel)
{
// extract node type qname
QName nodeType = null;
Object wrapped = ((BeanModel)arg0).getWrappedObject();
if (wrapped != null)
{
if (wrapped instanceof TemplateNode)
{
nodeType = ((TemplateNode)wrapped).getType();
}
else if (wrapped instanceof QName)
{
nodeType = (QName)wrapped;
}
}
// convert to CMIS type id
if (nodeType != null)
{
result = mappingService.getCmisTypeId(nodeType);
}
}
}
return result;
}
}

View File

@@ -180,7 +180,6 @@ public class RepositoryContainer extends AbstractRuntimeContainer implements Ten
Map<String, Object> params = new HashMap<String, Object>(); Map<String, Object> params = new HashMap<String, Object>();
params.putAll(super.getTemplateParameters()); params.putAll(super.getTemplateParameters());
params.put(TemplateService.KEY_IMAGE_RESOLVER, imageResolver.getImageResolver()); params.put(TemplateService.KEY_IMAGE_RESOLVER, imageResolver.getImageResolver());
params.put("cropContent", new CropContentMethod());
addRepoParameters(params); addRepoParameters(params);
return params; return params;
} }

View File

@@ -156,5 +156,5 @@ public class TestWebScriptRepoServer extends TestWebScriptServer
} }
}, username); }, username);
} }
} }

View File

@@ -24,6 +24,8 @@
*/ */
package org.alfresco.repo.web.util.paging; package org.alfresco.repo.web.util.paging;
import java.util.Map;
/** /**
* Paging. A utility for maintaining paged indexes for a collection of N items. * Paging. A utility for maintaining paged indexes for a collection of N items.
* *
@@ -134,6 +136,90 @@ public class Paging
{ {
return zeroBasedRow; return zeroBasedRow;
} }
/**
* Create a Page or Window from standardised request arguments / headers
*
* For Paged based index (take precedence over window based index, if both are specified):
*
* - request args
* pageNo => page number index
* pageSize => size of page
*
* For Window based index (as defined by CMIS):
*
* - request args (take precedence over header values if both are specified)
* skipCount => row number start index
* maxItems => size of page
*
* - header values
* CMIS-skipCount => row number start index
* CMIS-maxItems => size of page
*
* @param args request args
* @param headers request headers
* @return page (if pageNumber driven) or window (if skipCount driven)
*/
public Page createPageOrWindow(Map<String, String> args, Map<String, String> headers)
{
// page number
String strPageNo = args.get("pageNo");
Integer pageNo = null;
if (strPageNo != null)
{
try
{
pageNo = new Integer(strPageNo);
}
catch(NumberFormatException e) {};
}
// page size
String strPageSize = args.get("pageSize");
Integer pageSize = null;
if (strPageSize != null)
{
try
{
pageSize = new Integer(strPageSize);
}
catch(NumberFormatException e) {};
}
// skip count
String strSkipCount = args.get("skipCount");
if (strSkipCount == null)
{
strSkipCount = (headers == null) ? null : headers.get("CMIS-skipCount");
}
Integer skipCount = null;
if (strSkipCount != null)
{
try
{
skipCount = new Integer(strSkipCount);
}
catch(NumberFormatException e) {};
}
// max items
String strMaxItems = args.get("maxItems");
if (strMaxItems == null)
{
strMaxItems = (headers == null) ? null : headers.get("CMIS-maxItems");
}
Integer maxItems = null;
if (strMaxItems != null)
{
try
{
maxItems = new Integer(strMaxItems);
}
catch(NumberFormatException e) {};
}
return createPageOrWindow(pageNo, pageSize, skipCount, maxItems);
}
/** /**
* Create a Page or Window * Create a Page or Window
@@ -146,13 +232,13 @@ public class Paging
*/ */
public Page createPageOrWindow(Integer pageNumber, Integer pageSize, Integer skipCount, Integer maxItems) public Page createPageOrWindow(Integer pageNumber, Integer pageSize, Integer skipCount, Integer maxItems)
{ {
if (pageNumber != null) if (pageNumber != null || pageSize != null)
{ {
return createPage(pageNumber, pageSize == null ? 0 : pageSize); return createPage(pageNumber == null ? isZeroBasedPage() ? 0 : 1 : pageNumber, pageSize == null ? 0 : pageSize);
} }
else if (skipCount != null) else if (skipCount != null || maxItems != null)
{ {
return createWindow(skipCount, maxItems == null ? 0 : maxItems); return createWindow(skipCount == null ? isZeroBasedRow() ? 0 : 1 : skipCount, maxItems == null ? 0 : maxItems);
} }
return createUnlimitedPage(); return createUnlimitedPage();
} }