blob: c2a6d274b6ee52ffab5f7953f22c4f535066d219 [file] [log] [blame]
<?php
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 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.
define("HTTP_OK", 200);
define("HTTP_CREATED", 201);
define("HTTP_ACCEPTED", 202);
define("HTTP_NONAUTHORITATIVE_INFORMATION", 203);
define("HTTP_NO_CONTENT", 204);
define("HTTP_RESET_CONTENT", 205);
define("HTTP_PARTIAL_CONTENT", 206);
define("HTTP_MULTIPLE_CHOICES", 300);
define("HTTP_BAD_REQUEST", 400); // invalidArgument, filterNotValid
define("HTTP_UNAUTHORIZED", 401);
define("HTTP_FORBIDDEN", 403); // permissionDenied, streamNotSupported
define("HTTP_NOT_FOUND", 404); // objectNotFound
define("HTTP_METHOD_NOT_ALLOWED", 405); // notSupported
define("HTTP_NOT_ACCEPTABLE", 406);
define("HTTP_PROXY_AUTHENTICATION_REQUIRED", 407);
define("xHTTP_REQUEST_TIMEOUT", 408); //Had to change this b/c HTTP_REQUEST_TIMEOUT conflicts with definition in Drupal 7
define("HTTP_CONFLICT", 409); // constraint, contentAlreadyExists, versioning, updateConflict, nameConstraintViolation
define("HTTP_UNSUPPORTED_MEDIA_TYPE", 415);
define("HTTP_UNPROCESSABLE_ENTITY", 422);
define("HTTP_INTERNAL_SERVER_ERROR", 500); // runtime, storage
class CmisInvalidArgumentException extends Exception {}
class CmisObjectNotFoundException extends Exception {}
class CmisPermissionDeniedException extends Exception {}
class CmisNotSupportedException extends Exception {}
class CmisConstraintException extends Exception {}
class CmisRuntimeException extends Exception {}
class CMISRepositoryWrapper
{
// Handles --
// Workspace -- but only endpoints with a single repo
// Entry -- but only for objects
// Feeds -- but only for non-hierarchical feeds
// Does not handle --
// -- Hierarchical Feeds
// -- Types
// -- Others?
// Only Handles Basic Auth
// Very Little Error Checking
// Does not work against pre CMIS 1.0 Repos
var $url;
var $username;
var $password;
var $authenticated;
var $workspace;
var $last_request;
var $do_not_urlencode;
protected $_addlCurlOptions = array();
static $namespaces = array (
"cmis" => "http://docs.oasis-open.org/ns/cmis/core/200908/",
"cmisra" => "http://docs.oasis-open.org/ns/cmis/restatom/200908/",
"atom" => "http://www.w3.org/2005/Atom",
"app" => "http://www.w3.org/2007/app",
);
function __construct($url, $username = null, $password = null, $options = null, array $addlCurlOptions = array())
{
if (is_array($options) && $options["config:do_not_urlencode"]) {
$this->do_not_urlencode=true;
}
$this->_addlCurlOptions = $addlCurlOptions; // additional cURL options
$this->connect($url, $username, $password, $options);
}
static function getOpUrl($url, $options = null)
{
if (is_array($options) && (count($options) > 0))
{
$needs_question = strstr($url, "?") === false;
return $url . ($needs_question ? "?" : "&") . http_build_query($options);
} else
{
return $url;
}
}
function convertStatusCode($code, $message)
{
switch ($code) {
case HTTP_BAD_REQUEST:
return new CmisInvalidArgumentException($message, $code);
case HTTP_NOT_FOUND:
return new CmisObjectNotFoundException($message, $code);
case HTTP_FORBIDDEN:
return new CmisPermissionDeniedException($message, $code);
case HTTP_METHOD_NOT_ALLOWED:
return new CmisNotSupportedException($message, $code);
case HTTP_CONFLICT:
return new CmisConstraintException($message, $code);
default:
return new CmisRuntimeException($message, $code);
}
}
function connect($url, $username, $password, $options)
{
// TODO: Make this work with cookies
$this->url = $url;
$this->username = $username;
$this->password = $password;
$this->auth_options = $options;
$this->authenticated = false;
$retval = $this->doGet($this->url);
if ($retval->code == HTTP_OK || $retval->code == HTTP_CREATED)
{
$this->authenticated = true;
$this->workspace = CMISRepositoryWrapper :: extractWorkspace($retval->body);
}
}
function doGet($url)
{
$retval = $this->doRequest($url);
if ($retval->code != HTTP_OK)
{
throw $this->convertStatusCode($retval->code, $retval->body);
}
return $retval;
}
function doDelete($url)
{
$retval = $this->doRequest($url, "DELETE");
if ($retval->code != HTTP_NO_CONTENT)
{
throw $this->convertStatusCode($retval->code, $retval->body);
}
return $retval;
}
function doPost($url, $content, $contentType, $charset = null)
{
$retval = $this->doRequest($url, "POST", $content, $contentType);
if ($retval->code != HTTP_CREATED)
{
throw $this->convertStatusCode($retval->code, $retval->body);
}
return $retval;
}
function doPut($url, $content, $contentType, $charset = null)
{
$retval = $this->doRequest($url, "PUT", $content, $contentType);
if (($retval->code < HTTP_OK) || ($retval->code >= HTTP_MULTIPLE_CHOICES))
{
throw $this->convertStatusCode($retval->code, $retval->body);
}
return $retval;
}
function doRequest($url, $method = "GET", $content = null, $contentType = null, $charset = null)
{
// Process the HTTP request
// 'til now only the GET request has been tested
// Does not URL encode any inputs yet
if (is_array($this->auth_options))
{
$url = CMISRepositoryWrapper :: getOpUrl($url, $this->auth_options);
}
$session = curl_init($url);
curl_setopt($session, CURLOPT_HEADER, false);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
if ($this->username)
{
curl_setopt($session, CURLOPT_USERPWD, $this->username . ":" . $this->password);
}
curl_setopt($session, CURLOPT_CUSTOMREQUEST, $method);
if ($contentType)
{
curl_setopt($session, CURLOPT_HTTPHEADER, array (
"Content-Type: " . $contentType
));
}
if ($content)
{
curl_setopt($session, CURLOPT_POSTFIELDS, $content);
}
if ($method == "POST")
{
curl_setopt($session, CURLOPT_POST, true);
}
// apply addl. cURL options
// WARNING: this may override previously set options
if (count($this->_addlCurlOptions)) {
foreach ($this->_addlCurlOptions as $key => $value) {
curl_setopt($session, $key, $value);
}
}
//TODO: Make this storage optional
$retval = new stdClass();
$retval->url = $url;
$retval->method = $method;
$retval->content_sent = $content;
$retval->content_type_sent = $contentType;
$retval->body = curl_exec($session);
$retval->code = curl_getinfo($session, CURLINFO_HTTP_CODE);
$retval->content_type = curl_getinfo($session, CURLINFO_CONTENT_TYPE);
$retval->content_length = curl_getinfo($session, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
curl_close($session);
$this->last_request = $retval;
return $retval;
}
function getLastRequest()
{
return $this->last_request;
}
function getLastRequestBody()
{
return $this->last_request->body;
}
function getLastRequestCode()
{
return $this->last_request->code;
}
function getLastRequestContentType()
{
return $this->last_request->content_type;
}
function getLastRequestContentLength()
{
return $this->last_request->content_length;
}
function getLastRequestURL()
{
return $this->last_request->url;
}
function getLastRequestMethod()
{
return $this->last_request->method;
}
function getLastRequestContentTypeSent()
{
return $this->last_request->content_type_sent;
}
function getLastRequestContentSent()
{
return $this->last_request->content_sent;
}
// Static Utility Functions
static function processTemplate($template, $values = array ())
{
// Fill in the blanks --
$retval = $template;
if (is_array($values))
{
foreach ($values as $name => $value)
{
$retval = str_replace("{" . $name . "}", $value, $retval);
}
}
// Fill in any unpoupated variables with ""
return preg_replace("/{[a-zA-Z0-9_]+}/", "", $retval);
}
static function doXQuery($xmldata, $xquery)
{
$doc = new DOMDocument();
$doc->loadXML($xmldata);
return CMISRepositoryWrapper :: doXQueryFromNode($doc, $xquery);
}
static function doXQueryFromNode($xmlnode, $xquery)
{
// Perform an XQUERY on a NODE
// Register the 4 CMIS namespaces
//THis may be a hopeless HACK!
//TODO: Review
if (!($xmlnode instanceof DOMDocument)) {
$xdoc=new DOMDocument();
$xnode = $xdoc->importNode($xmlnode,true);
$xdoc->appendChild($xnode);
$xpath = new DomXPath($xdoc);
} else {
$xpath = new DomXPath($xmlnode);
}
foreach (CMISRepositoryWrapper :: $namespaces as $nspre => $nsuri)
{
$xpath->registerNamespace($nspre, $nsuri);
}
return $xpath->query($xquery);
}
static function getLinksArray($xmlnode)
{
// Gets the links of an object or a workspace
// Distinguishes between the two "down" links
// -- the children link is put into the associative array with the "down" index
// -- the descendants link is put into the associative array with the "down-tree" index
// These links are distinquished by the mime type attribute, but these are probably the only two links that share the same rel ..
// so this was done as a one off
$links = array ();
$link_nodes = $xmlnode->getElementsByTagName("link");
foreach ($link_nodes as $ln)
{
if ($ln->attributes->getNamedItem("rel")->nodeValue == "down" && $ln->attributes->getNamedItem("type")->nodeValue == "application/cmistree+xml")
{
//Descendents and Childredn share same "rel" but different document type
$links["down-tree"] = $ln->attributes->getNamedItem("href")->nodeValue;
} else
{
$links[$ln->attributes->getNamedItem("rel")->nodeValue] = $ln->attributes->getNamedItem("href")->nodeValue;
}
}
return $links;
}
static function extractAllowableActions($xmldata)
{
$doc = new DOMDocument();
$doc->loadXML($xmldata);
return CMISRepositoryWrapper :: extractAllowableActionsFromNode($doc);
}
static function extractAllowableActionsFromNode($xmlnode)
{
$result = array();
$allowableActions = $xmlnode->getElementsByTagName("allowableActions");
if ($allowableActions->length > 0) {
foreach($allowableActions->item(0)->childNodes as $action)
{
if (isset($action->localName)) {
$result[$action->localName] = (preg_match("/^true$/i", $action->nodeValue) > 0);
}
}
}
return $result;
}
static function extractObject($xmldata)
{
$doc = new DOMDocument();
$doc->loadXML($xmldata);
return CMISRepositoryWrapper :: extractObjectFromNode($doc);
}
static function extractObjectFromNode($xmlnode)
{
// Extracts the contents of an Object and organizes them into:
// -- Links
// -- Properties
// -- the Object ID
// RRM -- NEED TO ADD ALLOWABLEACTIONS
$retval = new stdClass();
$retval->links = CMISRepositoryWrapper :: getLinksArray($xmlnode);
$retval->properties = array ();
$prop_nodes = $xmlnode->getElementsByTagName("object")->item(0)->getElementsByTagName("properties")->item(0)->childNodes;
foreach ($prop_nodes as $pn)
{
if ($pn->attributes)
{
$propDefId = $pn->attributes->getNamedItem("propertyDefinitionId");
// TODO: Maybe use ->length=0 to even detect null values
if (!is_null($propDefId) && $pn->getElementsByTagName("value") && $pn->getElementsByTagName("value")->item(0))
{
if ($pn->getElementsByTagName("value")->length > 1) {
$retval->properties[$propDefId->nodeValue] = array();
for ($idx=0;$idx < $pn->getElementsByTagName("value")->length;$idx++) {
$retval->properties[$propDefId->nodeValue][$idx] = $pn->getElementsByTagName("value")->item($idx)->nodeValue;
}
} else {
$retval->properties[$propDefId->nodeValue] = $pn->getElementsByTagName("value")->item(0)->nodeValue;
}
}
}
}
$retval->uuid = $xmlnode->getElementsByTagName("id")->item(0)->nodeValue;
$retval->id = $retval->properties["cmis:objectId"];
//TODO: RRM FIX THIS
$children_node = $xmlnode->getElementsByTagName("children");
if (is_object($children_node)) {
$children_feed_c = $children_node->item(0);
}
if (is_object($children_feed_c)) {
$children_feed_l = $children_feed_c->getElementsByTagName("feed");
}
if (isset($children_feed_l) && is_object($children_feed_l) && is_object($children_feed_l->item(0))) {
$children_feed = $children_feed_l->item(0);
$children_doc = new DOMDocument();
$xnode = $children_doc->importNode($children_feed,true); // Avoid Wrong Document Error
$children_doc->appendChild($xnode);
$retval->children = CMISRepositoryWrapper :: extractObjectFeedFromNode($children_doc);
}
$retval->allowableActions = CMISRepositoryWrapper :: extractAllowableActionsFromNode($xmlnode);
return $retval;
}
function handleSpaces($path)
{
return $this->do_not_urlencode ? $path : rawurlencode($path);
}
static function extractTypeDef($xmldata)
{
$doc = new DOMDocument();
$doc->loadXML($xmldata);
return CMISRepositoryWrapper :: extractTypeDefFromNode($doc);
}
static function extractTypeDefFromNode($xmlnode)
{
// Extracts the contents of an Object and organizes them into:
// -- Links
// -- Properties
// -- the Object ID
// RRM -- NEED TO ADD ALLOWABLEACTIONS
$retval = new stdClass();
$retval->links = CMISRepositoryWrapper :: getLinksArray($xmlnode);
$retval->properties = array ();
$retval->attributes = array ();
$result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmisra:type/*");
foreach ($result as $node)
{
if ((substr($node->nodeName, 0, 13) == "cmis:property") && (substr($node->nodeName, -10) == "Definition"))
{
$id = $node->getElementsByTagName("id")->item(0)->nodeValue;
$cardinality = $node->getElementsByTagName("cardinality")->item(0)->nodeValue;
$propertyType = $node->getElementsByTagName("propertyType")->item(0)->nodeValue;
// Stop Gap for now
$retval->properties[$id] = array (
"cmis:propertyType" => $propertyType,
"cmis:cardinality" => $cardinality,
);
} else
{
$retval->attributes[$node->nodeName] = $node->nodeValue;
}
$retval->id = $retval->attributes["cmis:id"];
}
//TODO: RRM FIX THIS
$children_node = $xmlnode->getElementsByTagName("children");
if (is_object($children_node)) {
$children_feed_c = $children_node->item(0);
}
if (is_object($children_feed_c)) {
$children_feed_l = $children_feed_c->getElementsByTagName("feed");
}
if (is_object($children_feed_l) && is_object($children_feed_l->item(0))) {
$children_feed = $children_feed_l->item(0);
$children_doc = new DOMDocument();
$xnode = $children_doc->importNode($children_feed,true); // Avoid Wrong Document Error
$children_doc->appendChild($xnode);
$retval->children = CMISRepositoryWrapper :: extractTypeFeedFromNode($children_doc);
}
/*
*
$prop_nodes = $xmlnode->getElementsByTagName("object")->item(0)->getElementsByTagName("properties")->item(0)->childNodes;
foreach ($prop_nodes as $pn) {
if ($pn->attributes) {
$retval->properties[$pn->attributes->getNamedItem("propertyDefinitionId")->nodeValue] = $pn->getElementsByTagName("value")->item(0)->nodeValue;
}
}
$retval->uuid=$xmlnode->getElementsByTagName("id")->item(0)->nodeValue;
$retval->id=$retval->properties["cmis:objectId"];
*/
return $retval;
}
static function extractObjectFeed($xmldata)
{
//Assumes only one workspace for now
$doc = new DOMDocument();
$doc->loadXML($xmldata);
return CMISRepositoryWrapper :: extractObjectFeedFromNode($doc);
}
static function extractObjectFeedFromNode($xmlnode)
{
// Process a feed and extract the objects
// Does not handle hierarchy
// Provides two arrays
// -- one sequential array (a list)
// -- one hash table indexed by objectID
// and a property "numItems" that holds the total number of items available.
$retval = new stdClass();
// extract total number of items
$numItemsNode = CMISRepositoryWrapper::doXQueryFromNode($xmlnode, "/atom:feed/cmisra:numItems");
$retval->numItems = $numItemsNode->length ? (int) $numItemsNode->item(0)->nodeValue : -1; // set to negative value if info is not available
$retval->objectList = array ();
$retval->objectsById = array ();
$result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "/atom:feed/atom:entry");
foreach ($result as $node)
{
$obj = CMISRepositoryWrapper :: extractObjectFromNode($node);
$retval->objectsById[$obj->id] = $obj;
$retval->objectList[] = & $retval->objectsById[$obj->id];
}
return $retval;
}
static function extractTypeFeed($xmldata)
{
//Assumes only one workspace for now
$doc = new DOMDocument();
$doc->loadXML($xmldata);
return CMISRepositoryWrapper :: extractTypeFeedFromNode($doc);
}
static function extractTypeFeedFromNode($xmlnode)
{
// Process a feed and extract the objects
// Does not handle hierarchy
// Provides two arrays
// -- one sequential array (a list)
// -- one hash table indexed by objectID
$retval = new stdClass();
$retval->objectList = array ();
$retval->objectsById = array ();
$result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "/atom:feed/atom:entry");
foreach ($result as $node)
{
$obj = CMISRepositoryWrapper :: extractTypeDefFromNode($node);
$retval->objectsById[$obj->id] = $obj;
$retval->objectList[] = & $retval->objectsById[$obj->id];
}
return $retval;
}
static function extractWorkspace($xmldata)
{
//Assumes only one workspace for now
$doc = new DOMDocument();
$doc->loadXML($xmldata);
return CMISRepositoryWrapper :: extractWorkspaceFromNode($doc);
}
static function extractWorkspaceFromNode($xmlnode)
{
// Assumes only one workspace for now
// Load up the workspace object with arrays of
// links
// URI Templates
// Collections
// Capabilities
// General Repository Information
$retval = new stdClass();
$retval->links = CMISRepositoryWrapper :: getLinksArray($xmlnode);
$retval->uritemplates = array ();
$retval->collections = array ();
$retval->capabilities = array ();
$retval->repositoryInfo = array ();
$retval->permissions = array();
$retval->permissionsMapping = array();
$result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmisra:uritemplate");
foreach ($result as $node)
{
$retval->uritemplates[$node->getElementsByTagName("type")->item(0)->nodeValue] = $node->getElementsByTagName("template")->item(0)->nodeValue;
}
$result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//app:collection");
foreach ($result as $node)
{
$retval->collections[$node->getElementsByTagName("collectionType")->item(0)->nodeValue] = $node->attributes->getNamedItem("href")->nodeValue;
}
$result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmis:capabilities/*");
foreach ($result as $node)
{
$retval->capabilities[$node->nodeName] = $node->nodeValue;
}
$result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmisra:repositoryInfo/*[name()!='cmis:capabilities' and name()!='cmis:aclCapability']");
foreach ($result as $node)
{
$retval->repositoryInfo[$node->nodeName] = $node->nodeValue;
}
$result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmis:aclCapability/cmis:permissions");
foreach ($result as $node)
{
$retval->permissions[$node->getElementsByTagName("permission")->item(0)->nodeValue] = $node->getElementsByTagName("description")->item(0)->nodeValue;
}
$result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmis:aclCapability/cmis:mapping");
foreach ($result as $node)
{
$key = $node->getElementsByTagName("key")->item(0)->nodeValue;
$values = array();
foreach ($node->getElementsByTagName("permission") as $value)
{
array_push($values, $value->nodeValue);
}
$retval->permissionsMapping[$key] = $values;
}
$result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmis:aclCapability/*[name()!='cmis:permissions' and name()!='cmis:mapping']");
foreach ($result as $node)
{
$retval->repositoryInfo[$node->nodeName] = $node->nodeValue;
}
return $retval;
}
}
// Option Contants for Array Indexing
// -- Generally optional flags that control how much information is returned
// -- Change log token is an anomoly -- but included in URL as parameter
define("OPT_MAX_ITEMS", "maxItems");
define("OPT_SKIP_COUNT", "skipCount");
define("OPT_FILTER", "filter");
define("OPT_INCLUDE_PROPERTY_DEFINITIONS", "includePropertyDefinitions");
define("OPT_INCLUDE_RELATIONSHIPS", "includeRelationships");
define("OPT_INCLUDE_POLICY_IDS", "includePolicyIds");
define("OPT_RENDITION_FILTER", "renditionFilter");
define("OPT_INCLUDE_ACL", "includeACL");
define("OPT_INCLUDE_ALLOWABLE_ACTIONS", "includeAllowableActions");
define("OPT_DEPTH", "depth");
define("OPT_CHANGE_LOG_TOKEN", "changeLogToken");
define("LINK_ALLOWABLE_ACTIONS", "http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions");
define("MIME_ATOM_XML", 'application/atom+xml');
define("MIME_ATOM_XML_ENTRY", 'application/atom+xml;type=entry');
define("MIME_ATOM_XML_FEED", 'application/atom+xml;type=feed');
define("MIME_CMIS_TREE", 'application/cmistree+xml');
define("MIME_CMIS_QUERY", 'application/cmisquery+xml');
// Many Links have a pattern to them based upon objectId -- but can that be depended upon?
class CMISService extends CMISRepositoryWrapper
{
var $_link_cache;
var $_title_cache;
var $_objTypeId_cache;
var $_type_cache;
function __construct($url, $username, $password, $options = null, array $addlCurlOptions = array())
{
parent :: __construct($url, $username, $password, $options, $addlCurlOptions);
$this->_link_cache = array ();
$this->_title_cache = array ();
$this->_objTypeId_cache = array ();
$this->_type_cache = array ();
}
// Utility Methods -- Added Titles
// Should refactor to allow for single object
function cacheObjectInfo($obj)
{
$this->_link_cache[$obj->id] = $obj->links;
$this->_title_cache[$obj->id] = $obj->properties["cmis:name"]; // Broad Assumption Here?
$this->_objTypeId_cache[$obj->id] = $obj->properties["cmis:objectTypeId"];
}
function cacheFeedInfo($objs)
{
foreach ($objs->objectList as $obj)
{
$this->cacheObjectInfo($obj);
}
}
function cacheTypeFeedInfo($typs)
{
foreach ($typs->objectList as $typ)
{
$this->cacheTypeInfo($typ);
}
}
function cacheTypeInfo($tDef)
{
// TODO: Fix Type Caching with missing properties
$this->_type_cache[$tDef->id] = $tDef;
}
function getPropertyType($typeId, $propertyId)
{
if ($this->_type_cache[$typeId]->properties)
{
return $this->_type_cache[$typeId]->properties[$propertyId]["cmis:propertyType"];
}
$obj = $this->getTypeDefinition($typeId);
return $obj->properties[$propertyId]["cmis:propertyType"];
}
function getObjectType($objectId)
{
if ($this->_objTypeId_cache[$objectId])
{
return $this->_objTypeId_cache[$objectId];
}
$obj = $this->getObject($objectId);
return $obj->properties["cmis:objectTypeId"];
}
function getTitle($objectId)
{
if ($this->_title_cache[$objectId])
{
return $this->_title_cache[$objectId];
}
$obj = $this->getObject($objectId);
return $obj->properties["cmis:name"];
}
function getTypeLink($typeId, $linkName)
{
if ($this->_type_cache[$typeId]->links)
{
return $this->_type_cache[$typeId]->links[$linkName];
}
$typ = $this->getTypeDefinition($typeId);
return $typ->links[$linkName];
}
function getLink($objectId, $linkName)
{
if ($this->_link_cache[$objectId][$linkName])
{
return $this->_link_cache[$objectId][$linkName];
}
$obj = $this->getObject($objectId);
return $obj->links[$linkName];
}
// Repository Services
function getRepositories()
{
throw new Exception("Not Implemented");
}
function getRepositoryInfo()
{
return $this->workspace;
}
function getTypeDescendants($typeId=null, $depth, $options = array ())
{
// TODO: Refactor Type Entries Caching
$varmap = $options;
if ($typeId) {
$hash_values = $options;
$hash_values['depth'] = $depth;
$myURL = $this->getTypeLink($typeId, "down-tree");
$myURL = CMISRepositoryWrapper :: getOpUrl ($myURL, $hash_values);
} else {
$myURL = $this->processTemplate($this->workspace->collections['http://docs.oasis-open.org/ns/cmis/link/200908/typedescendants'], $varmap);
}
$ret = $this->doGet($myURL);
$typs = $this->extractTypeFeed($ret->body);
$this->cacheTypeFeedInfo($typs);
return $typs;
}
function getTypeChildren($typeId=null, $options = array ())
{
// TODO: Refactor Type Entries Caching
$varmap = $options;
if ($typeId) {
$myURL = $this->getTypeLink($typeId, "down");
//TODO: Need GenURLQueryString Utility
} else {
//TODO: Need right URL
$myURL = $this->processTemplate($this->workspace->collections['types'], $varmap);
}
$ret = $this->doGet($myURL);
$typs = $this->extractTypeFeed($ret->body);
$this->cacheTypeFeedInfo($typs);
return $typs;
}
function getTypeDefinition($typeId, $options = array ())
{ // Nice to have
$varmap = $options;
$varmap["id"] = $typeId;
$myURL = $this->processTemplate($this->workspace->uritemplates['typebyid'], $varmap);
$ret = $this->doGet($myURL);
$obj = $this->extractTypeDef($ret->body);
$this->cacheTypeInfo($obj);
return $obj;
}
function getObjectTypeDefinition($objectId)
{ // Nice to have
$myURL = $this->getLink($objectId, "describedby");
$ret = $this->doGet($myURL);
$obj = $this->extractTypeDef($ret->body);
$this->cacheTypeInfo($obj);
return $obj;
}
//Navigation Services
function getFolderTree($folderId, $depth, $options = array ())
{
$hash_values = $options;
$hash_values['depth'] = $depth;
$myURL = $this->getLink($folderId, "http://docs.oasis-open.org/ns/cmis/link/200908/foldertree");
$myURL = CMISRepositoryWrapper :: getOpUrl ($myURL, $hash_values);
$ret = $this->doGet($myURL);
$objs = $this->extractObjectFeed($ret->body);
$this->cacheFeedInfo($objs);
return $objs;
}
function getDescendants($folderId, $depth, $options = array ())
{ // Nice to have
$hash_values = $options;
$hash_values['depth'] = $depth;
$myURL = $this->getLink($folderId, "down-tree");
$myURL = CMISRepositoryWrapper :: getOpUrl ($myURL, $hash_values);
$ret = $this->doGet($myURL);
$objs = $this->extractObjectFeed($ret->body);
$this->cacheFeedInfo($objs);
return $objs;
}
function getChildren($folderId, $options = array ())
{
$myURL = $this->getLink($folderId, "down");
//TODO: Need GenURLQueryString Utility
$ret = $this->doGet($myURL);
$objs = $this->extractObjectFeed($ret->body);
$this->cacheFeedInfo($objs);
return $objs;
}
function getFolderParent($folderId, $options = array ())
{ //yes
$myURL = $this->getLink($folderId, "up");
//TODO: Need GenURLQueryString Utility
$ret = $this->doGet($myURL);
$obj = $this->extractObjectEntry($ret->body);
$this->cacheObjectInfo($obj);
return $obj;
}
function getObjectParents($objectId, $options = array ())
{ // yes
$myURL = $this->getLink($objectId, "up");
//TODO: Need GenURLQueryString Utility
$ret = $this->doGet($myURL);
$objs = $this->extractObjectFeed($ret->body);
$this->cacheFeedInfo($objs);
return $objs;
}
function getCheckedOutDocs($options = array ())
{
$obj_url = $this->workspace->collections['checkedout'];
$ret = $this->doGet($obj_url);
$objs = $this->extractObjectFeed($ret->body);
$this->cacheFeedInfo($objs);
return $objs;
}
//Discovery Services
static function getQueryTemplate()
{
ob_start();
echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . "\n";
?>
<cmis:query xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/"
xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:app="http://www.w3.org/2007/app"
xmlns:cmisra="http://docs.oasisopen.org/ns/cmis/restatom/200908/">
<cmis:statement>{q}</cmis:statement>
<cmis:searchAllVersions>{searchAllVersions}</cmis:searchAllVersions>
<cmis:includeAllowableActions>{includeAllowableActions}</cmis:includeAllowableActions>
<cmis:includeRelationships>{includeRelationships}</cmis:includeRelationships>
<cmis:renditionFilter>{renditionFilter}</cmis:renditionFilter>
<cmis:maxItems>{maxItems}</cmis:maxItems>
<cmis:skipCount>{skipCount}</cmis:skipCount>
</cmis:query>
<?php
return ob_get_clean();
}
function query($statement, $options = array ())
{
static $query_template;
if (!isset ($query_template))
{
$query_template = CMISService :: getQueryTemplate();
}
$hash_values = $options;
$hash_values['q'] = $statement;
$post_value = CMISRepositoryWrapper :: processTemplate($query_template, $hash_values);
$ret = $this->doPost($this->workspace->collections['query'], $post_value, MIME_CMIS_QUERY);
$objs = $this->extractObjectFeed($ret->body);
$this->cacheFeedInfo($objs);
return $objs;
}
function getContentChanges()
{
throw new Exception("Not Implemented");
}
//Object Services
static function getEntryTemplate()
{
ob_start();
echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . "\n";
?>
<atom:entry xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/"
xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:app="http://www.w3.org/2007/app"
xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/">
<atom:title>{title}</atom:title>
{SUMMARY}
{CONTENT}
<cmisra:object><cmis:properties>{PROPERTIES}</cmis:properties></cmisra:object>
</atom:entry>
<?php
return ob_get_clean();
}
static function getPropertyTemplate()
{
ob_start();
?>
<cmis:property{propertyType} propertyDefinitionId="{propertyId}">
<cmis:value>{properties}</cmis:value>
</cmis:property{propertyType}>
<?php
return ob_get_clean();
}
function processPropertyTemplates($objectType, $propMap)
{
static $propTemplate;
static $propertyTypeMap;
if (!isset ($propTemplate))
{
$propTemplate = CMISService :: getPropertyTemplate();
}
if (!isset ($propertyTypeMap))
{ // Not sure if I need to do this like this
$propertyTypeMap = array (
"integer" => "Integer",
"boolean" => "Boolean",
"datetime" => "DateTime",
"decimal" => "Decimal",
"html" => "Html",
"id" => "Id",
"string" => "String",
"url" => "Url",
"xml" => "Xml",
);
}
$propertyContent = "";
$hash_values = array ();
foreach ($propMap as $propId => $propValue)
{
$hash_values['propertyType'] = $propertyTypeMap[$this->getPropertyType($objectType, $propId)];
$hash_values['propertyId'] = $propId;
if (is_array($propValue))
{
$first_one = true;
$hash_values['properties'] = "";
foreach ($propValue as $val)
{
//This is a bit of a hack
if ($first_one)
{
$first_one = false;
} else
{
$hash_values['properties'] .= "</cmis:value>\n<cmis:value>";
}
$hash_values['properties'] .= $val;
}
} else
{
$hash_values['properties'] = $propValue;
}
//echo "HASH:\n";
//print_r(array("template" =>$propTemplate, "Hash" => $hash_values));
$propertyContent .= CMISRepositoryWrapper :: processTemplate($propTemplate, $hash_values);
}
return $propertyContent;
}
static function getContentEntry($content, $content_type = "application/octet-stream")
{
static $contentTemplate;
if (!isset ($contentTemplate))
{
$contentTemplate = CMISService :: getContentTemplate();
}
if ($content)
{
return CMISRepositoryWrapper :: processTemplate($contentTemplate, array (
"content" => base64_encode($content
), "content_type" => $content_type));
} else
{
return "";
}
}
static function getSummaryTemplate()
{
ob_start();
?>
<atom:summary>{summary}</atom:summary>
<?php
return ob_get_clean();
}
static function getContentTemplate()
{
ob_start();
?>
<cmisra:content>
<cmisra:mediatype>
{content_type}
</cmisra:mediatype>
<cmisra:base64>
{content}
</cmisra:base64>
</cmisra:content>
<?php
return ob_get_clean();
}
static function createAtomEntry($name, $properties)
{
}
function getObject($objectId, $options = array ())
{
$varmap = $options;
$varmap["id"] = $objectId;
$obj_url = $this->processTemplate($this->workspace->uritemplates['objectbyid'], $varmap);
$ret = $this->doGet($obj_url);
$obj = $this->extractObject($ret->body);
$this->cacheObjectInfo($obj);
return $obj;
}
function getObjectByPath($path, $options = array ())
{
$varmap = $options;
$varmap["path"] = $this->handleSpaces($path);
$obj_url = $this->processTemplate($this->workspace->uritemplates['objectbypath'], $varmap);
$ret = $this->doGet($obj_url);
$obj = $this->extractObject($ret->body);
$this->cacheObjectInfo($obj);
return $obj;
}
function getProperties($objectId, $options = array ())
{
// May need to set the options array default --
return $this->getObject($objectId, $options);
}
function getAllowableActions($objectId, $options = array ())
{
$myURL = $this->getLink($objectId, LINK_ALLOWABLE_ACTIONS);
$ret = $this->doGet($myURL);
$result = $this->extractAllowableActions($ret->body);
return $result;
}
function getRenditions($objectId, $options = array (
OPT_RENDITION_FILTER => "*"
))
{
return getObject($objectId, $options);
}
function getContentStream($objectId, $options = array ())
{ // Yes
$myURL = $this->getLink($objectId, "edit-media");
$ret = $this->doGet($myURL);
// doRequest stores the last request information in this object
return $ret->body;
}
function postObject($folderId, $objectName, $objectType, $properties = array (), $content = null, $content_type = "application/octet-stream", $options = array ())
{ // Yes
$myURL = $this->getLink($folderId, "down");
// TODO: Need Proper Query String Handling
// Assumes that the 'down' link does not have a querystring in it
$myURL = CMISRepositoryWrapper :: getOpUrl($myURL, $options);
static $entry_template;
if (!isset ($entry_template))
{
$entry_template = CMISService :: getEntryTemplate();
}
if (is_array($properties))
{
$hash_values = $properties;
} else
{
$hash_values = array ();
}
if (!isset ($hash_values["cmis:objectTypeId"]))
{
$hash_values["cmis:objectTypeId"] = $objectType;
}
$properties_xml = $this->processPropertyTemplates($hash_values["cmis:objectTypeId"], $hash_values);
if (is_array($options))
{
$hash_values = $options;
} else
{
$hash_values = array ();
}
$hash_values["PROPERTIES"] = $properties_xml;
$hash_values["SUMMARY"] = CMISService :: getSummaryTemplate();
if ($content)
{
$hash_values["CONTENT"] = CMISService :: getContentEntry($content, $content_type);
}
if (!isset ($hash_values['title']))
{
$hash_values['title'] = $objectName;
}
if (!isset ($hash_values['summary']))
{
$hash_values['summary'] = $objectName;
}
$post_value = CMISRepositoryWrapper :: processTemplate($entry_template, $hash_values);
$ret = $this->doPost($myURL, $post_value, MIME_ATOM_XML_ENTRY);
// print "DO_POST\n";
// print_r($ret);
$obj = $this->extractObject($ret->body);
$this->cacheObjectInfo($obj);
return $obj;
}
function createDocument($folderId, $fileName, $properties = array (), $content = null, $content_type = "application/octet-stream", $options = array ())
{ // Yes
return $this->postObject($folderId, $fileName, "cmis:document", $properties, $content, $content_type, $options);
}
function createDocumentFromSource()
{ //Yes?
throw new CmisNotSupportedException("createDocumentFromSource is not supported by the AtomPub binding!");
}
function createFolder($folderId, $folderName, $properties = array (), $options = array ())
{ // Yes
return $this->postObject($folderId, $folderName, "cmis:folder", $properties, null, null, $options);
}
function createRelationship()
{ // Not in first Release
throw new Exception("Not Implemented");
}
function createPolicy()
{ // Not in first Release
throw new Exception("Not Implemented");
}
function updateProperties($objectId, $properties = array (), $options = array ())
{ // Yes
$varmap = $options;
$varmap["id"] = $objectId;
$objectName = $this->getTitle($objectId);
$objectType = $this->getObjectType($objectId);
$obj_url = $this->getLink($objectId, "edit");
$obj_url = CMISRepositoryWrapper :: getOpUrl($obj_url, $options);
static $entry_template;
if (!isset ($entry_template))
{
$entry_template = CMISService :: getEntryTemplate();
}
if (is_array($properties))
{
$hash_values = $properties;
} else
{
$hash_values = array ();
}
$properties_xml = $this->processPropertyTemplates($objectType, $hash_values);
if (is_array($options))
{
$hash_values = $options;
} else
{
$hash_values = array ();
}
$hash_values["PROPERTIES"] = $properties_xml;
$hash_values["SUMMARY"] = CMISService :: getSummaryTemplate();
if (!isset ($hash_values['title']))
{
$hash_values['title'] = $objectName;
}
if (!isset ($hash_values['summary']))
{
$hash_values['summary'] = $objectName;
}
$put_value = CMISRepositoryWrapper :: processTemplate($entry_template, $hash_values);
// print $put_value; // RRM DEBUG
$ret = $this->doPut($obj_url, $put_value, MIME_ATOM_XML_ENTRY);
$obj = $this->extractObject($ret->body);
$this->cacheObjectInfo($obj);
return $obj;
}
function moveObject($objectId, $targetFolderId, $sourceFolderId, $options = array ())
{ //yes
$options['sourceFolderId'] = $sourceFolderId;
return $this->postObject($targetFolderId, $this->getTitle($objectId), $this->getObjectType($objectId), array (
"cmis:objectId" => $objectId
), null, null, $options);
}
function deleteObject($objectId, $options = array ())
{ //Yes
$varmap = $options;
$varmap["id"] = $objectId;
$obj_url = $this->getLink($objectId, "edit");
$ret = $this->doDelete($obj_url);
return;
}
function deleteTree()
{ // Nice to have
throw new Exception("Not Implemented");
}
function setContentStream($objectId, $content, $content_type, $options = array ())
{ //Yes
$myURL = $this->getLink($objectId, "edit-media");
$ret = $this->doPut($myURL, $content, $content_type);
}
function deleteContentStream($objectId, $options = array ())
{ //yes
$myURL = $this->getLink($objectId, "edit-media");
$ret = $this->doDelete($myURL);
return;
}
//Versioning Services
function getPropertiesOfLatestVersion($objectId, $major =false, $options = array ())
{
return $this->getObjectOfLatestVersion($objectId, $major, $options);
}
function getObjectOfLatestVersion($objectId, $major = false, $options = array ())
{
return $this->getObject($objectId, $options); // Won't be able to handle major/minor distinction
// Need to add this -- "current-version"
/*
* Headers: CMIS-filter, CMIS-returnVersion (enumReturnVersion)
* HTTP Arguments: filter, returnVersion
* Enum returnVersion: This, Latest, Major
*/
}
function getAllVersions()
{
throw new Exception("Not Implemented");
}
function checkOut()
{
throw new Exception("Not Implemented");
}
function checkIn()
{
throw new Exception("Not Implemented");
}
function cancelCheckOut()
{
throw new Exception("Not Implemented");
}
function deleteAllVersions()
{
throw new Exception("Not Implemented");
}
//Relationship Services
function getObjectRelationships()
{
// get stripped down version of object (for the links) and then get the relationships?
// Low priority -- can get all information when getting object
throw new Exception("Not Implemented");
}
//Multi-Filing Services
function addObjectToFolder()
{ // Probably
throw new Exception("Not Implemented");
}
function removeObjectFromFolder()
{ //Probably
throw new Exception("Not Implemented");
}
//Policy Services
function getAppliedPolicies()
{
throw new Exception("Not Implemented");
}
function applyPolicy()
{
throw new Exception("Not Implemented");
}
function removePolicy()
{
throw new Exception("Not Implemented");
}
//ACL Services
function getACL()
{
throw new Exception("Not Implemented");
}
function applyACL()
{
throw new Exception("Not Implemented");
}
}