2 # Licensed to the Apache Software Foundation (ASF) under one
3 # or more contributor license agreements. See the NOTICE file
4 # distributed with this work for additional information
5 # regarding copyright ownership. The ASF licenses this file
6 # to you under the Apache License, Version 2.0 (the
7 # "License"); you may not use this file except in compliance
8 # with the License. You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing,
13 # software distributed under the License is distributed on an
14 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 # KIND, either express or implied. See the License for the
16 # specific language governing permissions and limitations
19 define("HTTP_OK", 200);
20 define("HTTP_CREATED", 201);
21 define("HTTP_ACCEPTED", 202);
22 define("HTTP_NONAUTHORITATIVE_INFORMATION", 203);
23 define("HTTP_NO_CONTENT", 204);
24 define("HTTP_RESET_CONTENT", 205);
25 define("HTTP_PARTIAL_CONTENT", 206);
26 define("HTTP_MULTIPLE_CHOICES", 300);
27 define("HTTP_BAD_REQUEST", 400); // invalidArgument, filterNotValid
28 define("HTTP_UNAUTHORIZED", 401);
29 define("HTTP_FORBIDDEN", 403); // permissionDenied, streamNotSupported
30 define("HTTP_NOT_FOUND", 404); // objectNotFound
31 define("HTTP_METHOD_NOT_ALLOWED", 405); // notSupported
32 define("HTTP_NOT_ACCEPTABLE", 406);
33 define("HTTP_PROXY_AUTHENTICATION_REQUIRED", 407);
34 define("xHTTP_REQUEST_TIMEOUT", 408); //Had to change this b/c HTTP_REQUEST_TIMEOUT conflicts with definition in Drupal 7
35 define("HTTP_CONFLICT", 409); // constraint, contentAlreadyExists, versioning, updateConflict, nameConstraintViolation
36 define("HTTP_UNSUPPORTED_MEDIA_TYPE", 415);
37 define("HTTP_UNPROCESSABLE_ENTITY", 422);
38 define("HTTP_INTERNAL_SERVER_ERROR", 500); // runtime, storage
40 class CmisInvalidArgumentException extends Exception {}
41 class CmisObjectNotFoundException extends Exception {}
42 class CmisPermissionDeniedException extends Exception {}
43 class CmisNotSupportedException extends Exception {}
44 class CmisConstraintException extends Exception {}
45 class CmisRuntimeException extends Exception {}
47 class CMISRepositoryWrapper
50 // Workspace -- but only endpoints with a single repo
51 // Entry -- but only for objects
52 // Feeds -- but only for non-hierarchical feeds
54 // -- Hierarchical Feeds
57 // Only Handles Basic Auth
58 // Very Little Error Checking
59 // Does not work against pre CMIS 1.0 Repos
67 var $do_not_urlencode;
68 protected $_addlCurlOptions = array();
70 static $namespaces = array (
71 "cmis" => "http://docs.oasis-open.org/ns/cmis/core/200908/",
72 "cmisra" => "http://docs.oasis-open.org/ns/cmis/restatom/200908/",
73 "atom" => "http://www.w3.org/2005/Atom",
74 "app" => "http://www.w3.org/2007/app",
78 function __construct($url, $username = null, $password = null, $options = null, array $addlCurlOptions = array())
80 if (is_array($options) && $options["config:do_not_urlencode"]) {
81 $this->do_not_urlencode=true;
83 $this->_addlCurlOptions = $addlCurlOptions; // additional cURL options
85 $this->connect($url, $username, $password, $options);
88 static function getOpUrl($url, $options = null)
90 if (is_array($options) && (count($options) > 0))
92 $needs_question = strstr($url, "?") === false;
93 return $url . ($needs_question ? "?" : "&") . http_build_query($options);
100 function convertStatusCode($code, $message)
103 case HTTP_BAD_REQUEST:
104 return new CmisInvalidArgumentException($message, $code);
106 return new CmisObjectNotFoundException($message, $code);
108 return new CmisPermissionDeniedException($message, $code);
109 case HTTP_METHOD_NOT_ALLOWED:
110 return new CmisNotSupportedException($message, $code);
112 return new CmisConstraintException($message, $code);
114 return new CmisRuntimeException($message, $code);
118 function connect($url, $username, $password, $options)
120 // TODO: Make this work with cookies
122 $this->username = $username;
123 $this->password = $password;
124 $this->auth_options = $options;
125 $this->authenticated = false;
126 $retval = $this->doGet($this->url);
127 if ($retval->code == HTTP_OK || $retval->code == HTTP_CREATED)
129 $this->authenticated = true;
130 $this->workspace = CMISRepositoryWrapper :: extractWorkspace($retval->body);
136 $retval = $this->doRequest($url);
137 if ($retval->code != HTTP_OK)
139 throw $this->convertStatusCode($retval->code, $retval->body);
144 function doDelete($url)
146 $retval = $this->doRequest($url, "DELETE");
147 if ($retval->code != HTTP_NO_CONTENT)
149 throw $this->convertStatusCode($retval->code, $retval->body);
154 function doPost($url, $content, $contentType, $charset = null)
156 $retval = $this->doRequest($url, "POST", $content, $contentType);
157 if ($retval->code != HTTP_CREATED)
159 throw $this->convertStatusCode($retval->code, $retval->body);
164 function doPut($url, $content, $contentType, $charset = null)
166 $retval = $this->doRequest($url, "PUT", $content, $contentType);
167 if (($retval->code < HTTP_OK) || ($retval->code >= HTTP_MULTIPLE_CHOICES))
169 throw $this->convertStatusCode($retval->code, $retval->body);
174 function doRequest($url, $method = "GET", $content = null, $contentType = null, $charset = null)
176 // Process the HTTP request
177 // 'til now only the GET request has been tested
178 // Does not URL encode any inputs yet
179 if (is_array($this->auth_options))
181 $url = CMISRepositoryWrapper :: getOpUrl($url, $this->auth_options);
183 $session = curl_init($url);
184 curl_setopt($session, CURLOPT_HEADER, false);
185 curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
188 curl_setopt($session, CURLOPT_USERPWD, $this->username . ":" . $this->password);
190 curl_setopt($session, CURLOPT_CUSTOMREQUEST, $method);
193 curl_setopt($session, CURLOPT_HTTPHEADER, array (
194 "Content-Type: " . $contentType
199 curl_setopt($session, CURLOPT_POSTFIELDS, $content);
201 if ($method == "POST")
203 curl_setopt($session, CURLOPT_POST, true);
206 // apply addl. cURL options
207 // WARNING: this may override previously set options
208 if (count($this->_addlCurlOptions)) {
209 foreach ($this->_addlCurlOptions as $key => $value) {
210 curl_setopt($session, $key, $value);
215 //TODO: Make this storage optional
216 $retval = new stdClass();
218 $retval->method = $method;
219 $retval->content_sent = $content;
220 $retval->content_type_sent = $contentType;
221 $retval->body = curl_exec($session);
222 $retval->code = curl_getinfo($session, CURLINFO_HTTP_CODE);
223 $retval->content_type = curl_getinfo($session, CURLINFO_CONTENT_TYPE);
224 $retval->content_length = curl_getinfo($session, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
225 curl_close($session);
226 $this->last_request = $retval;
230 function getLastRequest()
232 return $this->last_request;
235 function getLastRequestBody()
237 return $this->last_request->body;
240 function getLastRequestCode()
242 return $this->last_request->code;
245 function getLastRequestContentType()
247 return $this->last_request->content_type;
250 function getLastRequestContentLength()
252 return $this->last_request->content_length;
255 function getLastRequestURL()
257 return $this->last_request->url;
260 function getLastRequestMethod()
262 return $this->last_request->method;
265 function getLastRequestContentTypeSent()
267 return $this->last_request->content_type_sent;
270 function getLastRequestContentSent()
272 return $this->last_request->content_sent;
275 // Static Utility Functions
276 static function processTemplate($template, $values = array ())
278 // Fill in the blanks --
280 if (is_array($values))
282 foreach ($values as $name => $value)
284 $retval = str_replace("{" . $name . "}", $value, $retval);
287 // Fill in any unpoupated variables with ""
288 return preg_replace("/{[a-zA-Z0-9_]+}/", "", $retval);
292 static function doXQuery($xmldata, $xquery)
294 $doc = new DOMDocument();
295 $doc->loadXML($xmldata);
296 return CMISRepositoryWrapper :: doXQueryFromNode($doc, $xquery);
299 static function doXQueryFromNode($xmlnode, $xquery)
301 // Perform an XQUERY on a NODE
302 // Register the 4 CMIS namespaces
303 //THis may be a hopeless HACK!
305 if (!($xmlnode instanceof DOMDocument)) {
306 $xdoc=new DOMDocument();
307 $xnode = $xdoc->importNode($xmlnode,true);
308 $xdoc->appendChild($xnode);
309 $xpath = new DomXPath($xdoc);
311 $xpath = new DomXPath($xmlnode);
313 foreach (CMISRepositoryWrapper :: $namespaces as $nspre => $nsuri)
315 $xpath->registerNamespace($nspre, $nsuri);
317 return $xpath->query($xquery);
320 static function getLinksArray($xmlnode)
322 // Gets the links of an object or a workspace
323 // Distinguishes between the two "down" links
324 // -- the children link is put into the associative array with the "down" index
325 // -- the descendants link is put into the associative array with the "down-tree" index
326 // These links are distinguished by the mime type attribute, but these are probably the only two links that share the same rel ..
327 // so this was done as a one off
329 $link_nodes = $xmlnode->getElementsByTagName("link");
330 foreach ($link_nodes as $ln)
332 if ($ln->attributes->getNamedItem("rel")->nodeValue == "down" && $ln->attributes->getNamedItem("type")->nodeValue == "application/cmistree+xml")
334 //Descendents and children share same "rel" but different document type.
335 $links["down-tree"] = $ln->attributes->getNamedItem("href")->nodeValue;
338 $links[$ln->attributes->getNamedItem("rel")->nodeValue] = $ln->attributes->getNamedItem("href")->nodeValue;
343 static function extractAllowableActions($xmldata)
345 $doc = new DOMDocument();
346 $doc->loadXML($xmldata);
347 return CMISRepositoryWrapper :: extractAllowableActionsFromNode($doc);
349 static function extractAllowableActionsFromNode($xmlnode)
352 $allowableActions = $xmlnode->getElementsByTagName("allowableActions");
353 if ($allowableActions->length > 0) {
354 foreach($allowableActions->item(0)->childNodes as $action)
356 if (isset($action->localName)) {
357 $result[$action->localName] = (preg_match("/^true$/i", $action->nodeValue) > 0);
363 static function extractObject($xmldata)
365 $doc = new DOMDocument();
366 $doc->loadXML($xmldata);
367 return CMISRepositoryWrapper :: extractObjectFromNode($doc);
370 static function extractObjectFromNode($xmlnode)
372 // Extracts the contents of an Object and organizes them into:
376 // RRM -- NEED TO ADD ALLOWABLEACTIONS
377 $retval = new stdClass();
378 $retval->links = CMISRepositoryWrapper :: getLinksArray($xmlnode);
379 $retval->properties = array ();
380 $prop_nodes = $xmlnode->getElementsByTagName("object")->item(0)->getElementsByTagName("properties")->item(0)->childNodes;
381 foreach ($prop_nodes as $pn)
385 $propDefId = $pn->attributes->getNamedItem("propertyDefinitionId");
386 // TODO: Maybe use ->length=0 to even detect null values
387 if (!is_null($propDefId) && $pn->getElementsByTagName("value") && $pn->getElementsByTagName("value")->item(0))
389 if ($pn->getElementsByTagName("value")->length > 1) {
390 $retval->properties[$propDefId->nodeValue] = array();
391 for ($idx=0;$idx < $pn->getElementsByTagName("value")->length;$idx++) {
392 $retval->properties[$propDefId->nodeValue][$idx] = $pn->getElementsByTagName("value")->item($idx)->nodeValue;
395 $retval->properties[$propDefId->nodeValue] = $pn->getElementsByTagName("value")->item(0)->nodeValue;
400 $retval->uuid = $xmlnode->getElementsByTagName("id")->item(0)->nodeValue;
401 $retval->id = $retval->properties["cmis:objectId"];
403 $children_node = $xmlnode->getElementsByTagName("children");
404 if (is_object($children_node)) {
405 $children_feed_c = $children_node->item(0);
407 if (is_object($children_feed_c)) {
408 $children_feed_l = $children_feed_c->getElementsByTagName("feed");
410 if (isset($children_feed_l) && is_object($children_feed_l) && is_object($children_feed_l->item(0))) {
411 $children_feed = $children_feed_l->item(0);
412 $children_doc = new DOMDocument();
413 $xnode = $children_doc->importNode($children_feed,true); // Avoid Wrong Document Error
414 $children_doc->appendChild($xnode);
415 $retval->children = CMISRepositoryWrapper :: extractObjectFeedFromNode($children_doc);
417 $retval->allowableActions = CMISRepositoryWrapper :: extractAllowableActionsFromNode($xmlnode);
421 function handleSpaces($path)
423 return $this->do_not_urlencode ? $path : rawurlencode($path);
426 static function extractTypeDef($xmldata)
428 $doc = new DOMDocument();
429 $doc->loadXML($xmldata);
430 return CMISRepositoryWrapper :: extractTypeDefFromNode($doc);
433 static function extractTypeDefFromNode($xmlnode)
435 // Extracts the contents of an Object and organizes them into:
439 // RRM -- NEED TO ADD ALLOWABLEACTIONS
440 $retval = new stdClass();
441 $retval->links = CMISRepositoryWrapper :: getLinksArray($xmlnode);
442 $retval->properties = array ();
443 $retval->attributes = array ();
444 $result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmisra:type/*");
445 foreach ($result as $node)
447 if ((substr($node->nodeName, 0, 13) == "cmis:property") && (substr($node->nodeName, -10) == "Definition"))
449 $id = $node->getElementsByTagName("id")->item(0)->nodeValue;
450 $cardinality = $node->getElementsByTagName("cardinality")->item(0)->nodeValue;
451 $propertyType = $node->getElementsByTagName("propertyType")->item(0)->nodeValue;
453 $retval->properties[$id] = array (
454 "cmis:propertyType" => $propertyType,
455 "cmis:cardinality" => $cardinality,
460 $retval->attributes[$node->nodeName] = $node->nodeValue;
462 $retval->id = $retval->attributes["cmis:id"];
465 $children_node = $xmlnode->getElementsByTagName("children");
466 if (is_object($children_node)) {
467 $children_feed_c = $children_node->item(0);
469 if (is_object($children_feed_c)) {
470 $children_feed_l = $children_feed_c->getElementsByTagName("feed");
472 if (is_object($children_feed_l) && is_object($children_feed_l->item(0))) {
473 $children_feed = $children_feed_l->item(0);
474 $children_doc = new DOMDocument();
475 $xnode = $children_doc->importNode($children_feed,true); // Avoid Wrong Document Error
476 $children_doc->appendChild($xnode);
477 $retval->children = CMISRepositoryWrapper :: extractTypeFeedFromNode($children_doc);
485 $prop_nodes = $xmlnode->getElementsByTagName("object")->item(0)->getElementsByTagName("properties")->item(0)->childNodes;
486 foreach ($prop_nodes as $pn) {
487 if ($pn->attributes) {
488 $retval->properties[$pn->attributes->getNamedItem("propertyDefinitionId")->nodeValue] = $pn->getElementsByTagName("value")->item(0)->nodeValue;
491 $retval->uuid=$xmlnode->getElementsByTagName("id")->item(0)->nodeValue;
492 $retval->id=$retval->properties["cmis:objectId"];
497 static function extractObjectFeed($xmldata)
499 //Assumes only one workspace for now
500 $doc = new DOMDocument();
501 $doc->loadXML($xmldata);
502 return CMISRepositoryWrapper :: extractObjectFeedFromNode($doc);
504 static function extractObjectFeedFromNode($xmlnode)
506 // Process a feed and extract the objects
507 // Does not handle hierarchy
508 // Provides two arrays
509 // -- one sequential array (a list)
510 // -- one hash table indexed by objectID
511 // and a property "numItems" that holds the total number of items available.
512 $retval = new stdClass();
513 // extract total number of items
514 $numItemsNode = CMISRepositoryWrapper::doXQueryFromNode($xmlnode, "/atom:feed/cmisra:numItems");
515 $retval->numItems = $numItemsNode->length ? (int) $numItemsNode->item(0)->nodeValue : -1; // set to negative value if info is not available
517 $retval->objectList = array ();
518 $retval->objectsById = array ();
519 $result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "/atom:feed/atom:entry");
520 foreach ($result as $node)
522 $obj = CMISRepositoryWrapper :: extractObjectFromNode($node);
523 $retval->objectsById[$obj->id] = $obj;
524 $retval->objectList[] = & $retval->objectsById[$obj->id];
529 static function extractTypeFeed($xmldata)
531 //Assumes only one workspace for now
532 $doc = new DOMDocument();
533 $doc->loadXML($xmldata);
534 return CMISRepositoryWrapper :: extractTypeFeedFromNode($doc);
536 static function extractTypeFeedFromNode($xmlnode)
538 // Process a feed and extract the objects
539 // Does not handle hierarchy
540 // Provides two arrays
541 // -- one sequential array (a list)
542 // -- one hash table indexed by objectID
543 $retval = new stdClass();
544 $retval->objectList = array ();
545 $retval->objectsById = array ();
546 $result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "/atom:feed/atom:entry");
547 foreach ($result as $node)
549 $obj = CMISRepositoryWrapper :: extractTypeDefFromNode($node);
550 $retval->objectsById[$obj->id] = $obj;
551 $retval->objectList[] = & $retval->objectsById[$obj->id];
556 static function extractWorkspace($xmldata)
558 //Assumes only one workspace for now
559 $doc = new DOMDocument();
560 $doc->loadXML($xmldata);
561 return CMISRepositoryWrapper :: extractWorkspaceFromNode($doc);
563 static function extractWorkspaceFromNode($xmlnode)
565 // Assumes only one workspace for now
566 // Load up the workspace object with arrays of
571 // General Repository Information
572 $retval = new stdClass();
573 $retval->links = CMISRepositoryWrapper :: getLinksArray($xmlnode);
574 $retval->uritemplates = array ();
575 $retval->collections = array ();
576 $retval->capabilities = array ();
577 $retval->repositoryInfo = array ();
578 $retval->permissions = array();
579 $retval->permissionsMapping = array();
580 $result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmisra:uritemplate");
581 foreach ($result as $node)
583 $retval->uritemplates[$node->getElementsByTagName("type")->item(0)->nodeValue] = $node->getElementsByTagName("template")->item(0)->nodeValue;
585 $result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//app:collection");
586 foreach ($result as $node)
588 $retval->collections[$node->getElementsByTagName("collectionType")->item(0)->nodeValue] = $node->attributes->getNamedItem("href")->nodeValue;
590 $result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmis:capabilities/*");
591 foreach ($result as $node)
593 $retval->capabilities[$node->nodeName] = $node->nodeValue;
595 $result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmisra:repositoryInfo/*[name()!='cmis:capabilities' and name()!='cmis:aclCapability']");
596 foreach ($result as $node)
598 $retval->repositoryInfo[$node->nodeName] = $node->nodeValue;
600 $result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmis:aclCapability/cmis:permissions");
601 foreach ($result as $node)
603 $retval->permissions[$node->getElementsByTagName("permission")->item(0)->nodeValue] = $node->getElementsByTagName("description")->item(0)->nodeValue;
605 $result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmis:aclCapability/cmis:mapping");
606 foreach ($result as $node)
608 $key = $node->getElementsByTagName("key")->item(0)->nodeValue;
610 foreach ($node->getElementsByTagName("permission") as $value)
612 array_push($values, $value->nodeValue);
614 $retval->permissionsMapping[$key] = $values;
616 $result = CMISRepositoryWrapper :: doXQueryFromNode($xmlnode, "//cmis:aclCapability/*[name()!='cmis:permissions' and name()!='cmis:mapping']");
617 foreach ($result as $node)
619 $retval->repositoryInfo[$node->nodeName] = $node->nodeValue;
626 // Option Contants for Array Indexing
627 // -- Generally optional flags that control how much information is returned
628 // -- Change log token is an anomoly -- but included in URL as parameter
629 define("OPT_MAX_ITEMS", "maxItems");
630 define("OPT_SKIP_COUNT", "skipCount");
631 define("OPT_FILTER", "filter");
632 define("OPT_INCLUDE_PROPERTY_DEFINITIONS", "includePropertyDefinitions");
633 define("OPT_INCLUDE_RELATIONSHIPS", "includeRelationships");
634 define("OPT_INCLUDE_POLICY_IDS", "includePolicyIds");
635 define("OPT_RENDITION_FILTER", "renditionFilter");
636 define("OPT_INCLUDE_ACL", "includeACL");
637 define("OPT_INCLUDE_ALLOWABLE_ACTIONS", "includeAllowableActions");
638 define("OPT_DEPTH", "depth");
639 define("OPT_CHANGE_LOG_TOKEN", "changeLogToken");
641 define("LINK_ALLOWABLE_ACTIONS", "http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions");
643 define("MIME_ATOM_XML", 'application/atom+xml');
644 define("MIME_ATOM_XML_ENTRY", 'application/atom+xml;type=entry');
645 define("MIME_ATOM_XML_FEED", 'application/atom+xml;type=feed');
646 define("MIME_CMIS_TREE", 'application/cmistree+xml');
647 define("MIME_CMIS_QUERY", 'application/cmisquery+xml');
649 // Many Links have a pattern to them based upon objectId -- but can that be depended upon?
651 class CMISService extends CMISRepositoryWrapper
655 var $_objTypeId_cache;
657 function __construct($url, $username, $password, $options = null, array $addlCurlOptions = array())
659 parent :: __construct($url, $username, $password, $options, $addlCurlOptions);
660 $this->_link_cache = array ();
661 $this->_title_cache = array ();
662 $this->_objTypeId_cache = array ();
663 $this->_type_cache = array ();
666 // Utility Methods -- Added Titles
667 // Should refactor to allow for single object
668 function cacheObjectInfo($obj)
670 $this->_link_cache[$obj->id] = $obj->links;
671 $this->_title_cache[$obj->id] = $obj->properties["cmis:name"]; // Broad Assumption Here?
672 $this->_objTypeId_cache[$obj->id] = $obj->properties["cmis:objectTypeId"];
675 function cacheFeedInfo($objs)
677 foreach ($objs->objectList as $obj)
679 $this->cacheObjectInfo($obj);
683 function cacheTypeFeedInfo($typs)
685 foreach ($typs->objectList as $typ)
687 $this->cacheTypeInfo($typ);
691 function cacheTypeInfo($tDef)
693 // TODO: Fix Type Caching with missing properties
694 $this->_type_cache[$tDef->id] = $tDef;
697 function getPropertyType($typeId, $propertyId)
699 if ($this->_type_cache[$typeId]->properties)
701 return $this->_type_cache[$typeId]->properties[$propertyId]["cmis:propertyType"];
703 $obj = $this->getTypeDefinition($typeId);
704 return $obj->properties[$propertyId]["cmis:propertyType"];
707 function getObjectType($objectId)
709 if ($this->_objTypeId_cache[$objectId])
711 return $this->_objTypeId_cache[$objectId];
713 $obj = $this->getObject($objectId);
714 return $obj->properties["cmis:objectTypeId"];
717 function getTitle($objectId)
719 if ($this->_title_cache[$objectId])
721 return $this->_title_cache[$objectId];
723 $obj = $this->getObject($objectId);
724 return $obj->properties["cmis:name"];
727 function getTypeLink($typeId, $linkName)
729 if ($this->_type_cache[$typeId]->links)
731 return $this->_type_cache[$typeId]->links[$linkName];
733 $typ = $this->getTypeDefinition($typeId);
734 return $typ->links[$linkName];
737 function getLink($objectId, $linkName)
739 if ($this->_link_cache[$objectId][$linkName])
741 return $this->_link_cache[$objectId][$linkName];
743 $obj = $this->getObject($objectId);
744 return $obj->links[$linkName];
747 // Repository Services
748 function getRepositories()
750 throw Exception("Not Implemented");
753 function getRepositoryInfo()
755 return $this->workspace;
758 function getTypeDescendants($typeId=null, $depth, $options = array ())
760 // TODO: Refactor Type Entries Caching
763 $hash_values = $options;
764 $hash_values['depth'] = $depth;
765 $myURL = $this->getTypeLink($typeId, "down-tree");
766 $myURL = CMISRepositoryWrapper :: getOpUrl ($myURL, $hash_values);
768 $myURL = $this->processTemplate($this->workspace->collections['http://docs.oasis-open.org/ns/cmis/link/200908/typedescendants'], $varmap);
770 $ret = $this->doGet($myURL);
771 $typs = $this->extractTypeFeed($ret->body);
772 $this->cacheTypeFeedInfo($typs);
776 function getTypeChildren($typeId=null, $options = array ())
778 // TODO: Refactor Type Entries Caching
781 $myURL = $this->getTypeLink($typeId, "down");
782 //TODO: Need GenURLQueryString Utility
784 //TODO: Need right URL
785 $myURL = $this->processTemplate($this->workspace->collections['types'], $varmap);
787 $ret = $this->doGet($myURL);
788 $typs = $this->extractTypeFeed($ret->body);
789 $this->cacheTypeFeedInfo($typs);
793 function getTypeDefinition($typeId, $options = array ())
796 $varmap["id"] = $typeId;
797 $myURL = $this->processTemplate($this->workspace->uritemplates['typebyid'], $varmap);
798 $ret = $this->doGet($myURL);
799 $obj = $this->extractTypeDef($ret->body);
800 $this->cacheTypeInfo($obj);
804 function getObjectTypeDefinition($objectId)
806 $myURL = $this->getLink($objectId, "describedby");
807 $ret = $this->doGet($myURL);
808 $obj = $this->extractTypeDef($ret->body);
809 $this->cacheTypeInfo($obj);
812 //Navigation Services
813 function getFolderTree($folderId, $depth, $options = array ())
815 $hash_values = $options;
816 $hash_values['depth'] = $depth;
817 $myURL = $this->getLink($folderId, "http://docs.oasis-open.org/ns/cmis/link/200908/foldertree");
818 $myURL = CMISRepositoryWrapper :: getOpUrl ($myURL, $hash_values);
819 $ret = $this->doGet($myURL);
820 $objs = $this->extractObjectFeed($ret->body);
821 $this->cacheFeedInfo($objs);
825 function getDescendants($folderId, $depth, $options = array ())
827 $hash_values = $options;
828 $hash_values['depth'] = $depth;
829 $myURL = $this->getLink($folderId, "down-tree");
830 $myURL = CMISRepositoryWrapper :: getOpUrl ($myURL, $hash_values);
831 $ret = $this->doGet($myURL);
832 $objs = $this->extractObjectFeed($ret->body);
833 $this->cacheFeedInfo($objs);
837 function getChildren($folderId, $options = array ())
839 $myURL = $this->getLink($folderId, "down");
840 //TODO: Need GenURLQueryString Utility
841 $ret = $this->doGet($myURL);
842 $objs = $this->extractObjectFeed($ret->body);
843 $this->cacheFeedInfo($objs);
847 function getFolderParent($folderId, $options = array ())
849 $myURL = $this->getLink($folderId, "up");
850 //TODO: Need GenURLQueryString Utility
851 $ret = $this->doGet($myURL);
852 $obj = $this->extractObjectEntry($ret->body);
853 $this->cacheObjectInfo($obj);
857 function getObjectParents($objectId, $options = array ())
859 $myURL = $this->getLink($objectId, "up");
860 //TODO: Need GenURLQueryString Utility
861 $ret = $this->doGet($myURL);
862 $objs = $this->extractObjectFeed($ret->body);
863 $this->cacheFeedInfo($objs);
867 function getCheckedOutDocs($options = array ())
869 $obj_url = $this->workspace->collections['checkedout'];
870 $ret = $this->doGet($obj_url);
871 $objs = $this->extractObjectFeed($ret->body);
872 $this->cacheFeedInfo($objs);
878 static function getQueryTemplate()
881 echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . "\n";
883 <cmis:query xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/"
884 xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"
885 xmlns:atom="http://www.w3.org/2005/Atom"
886 xmlns:app="http://www.w3.org/2007/app"
887 xmlns:cmisra="http://docs.oasisopen.org/ns/cmis/restatom/200908/">
888 <cmis:statement>{q}</cmis:statement>
889 <cmis:searchAllVersions>{searchAllVersions}</cmis:searchAllVersions>
890 <cmis:includeAllowableActions>{includeAllowableActions}</cmis:includeAllowableActions>
891 <cmis:includeRelationships>{includeRelationships}</cmis:includeRelationships>
892 <cmis:renditionFilter>{renditionFilter}</cmis:renditionFilter>
893 <cmis:maxItems>{maxItems}</cmis:maxItems>
894 <cmis:skipCount>{skipCount}</cmis:skipCount>
898 return ob_get_clean();
900 function query($statement, $options = array ())
902 static $query_template;
903 if (!isset ($query_template))
905 $query_template = CMISService :: getQueryTemplate();
907 $hash_values = $options;
908 $hash_values['q'] = $statement;
909 $post_value = CMISRepositoryWrapper :: processTemplate($query_template, $hash_values);
910 $ret = $this->doPost($this->workspace->collections['query'], $post_value, MIME_CMIS_QUERY);
911 $objs = $this->extractObjectFeed($ret->body);
912 $this->cacheFeedInfo($objs);
916 function getContentChanges()
918 throw Exception("Not Implemented");
922 static function getEntryTemplate()
925 echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . "\n";
927 <atom:entry xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/"
928 xmlns:cmism="http://docs.oasis-open.org/ns/cmis/messaging/200908/"
929 xmlns:atom="http://www.w3.org/2005/Atom"
930 xmlns:app="http://www.w3.org/2007/app"
931 xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/">
932 <atom:title>{title}</atom:title>
935 <cmisra:object><cmis:properties>{PROPERTIES}</cmis:properties></cmisra:object>
939 return ob_get_clean();
942 static function getPropertyTemplate()
946 <cmis:property{propertyType} propertyDefinitionId="{propertyId}">
947 <cmis:value>{properties}</cmis:value>
948 </cmis:property{propertyType}>
951 return ob_get_clean();
954 function processPropertyTemplates($objectType, $propMap)
956 static $propTemplate;
957 static $propertyTypeMap;
958 if (!isset ($propTemplate))
960 $propTemplate = CMISService :: getPropertyTemplate();
962 if (!isset ($propertyTypeMap))
963 { // Not sure if I need to do this like this
964 $propertyTypeMap = array (
965 "integer" => "Integer",
966 "boolean" => "Boolean",
967 "datetime" => "DateTime",
968 "decimal" => "Decimal",
971 "string" => "String",
977 $propertyContent = "";
978 $hash_values = array ();
979 foreach ($propMap as $propId => $propValue)
981 $hash_values['propertyType'] = $propertyTypeMap[$this->getPropertyType($objectType, $propId)];
982 $hash_values['propertyId'] = $propId;
983 if (is_array($propValue))
986 $hash_values['properties'] = "";
987 foreach ($propValue as $val)
989 //This is a bit of a hack
995 $hash_values['properties'] .= "</cmis:value>\n<cmis:value>";
997 $hash_values['properties'] .= $val;
1001 $hash_values['properties'] = $propValue;
1004 //print_r(array("template" =>$propTemplate, "Hash" => $hash_values));
1005 $propertyContent .= CMISRepositoryWrapper :: processTemplate($propTemplate, $hash_values);
1007 return $propertyContent;
1010 static function getContentEntry($content, $content_type = "application/octet-stream")
1012 static $contentTemplate;
1013 if (!isset ($contentTemplate))
1015 $contentTemplate = CMISService :: getContentTemplate();
1019 return CMISRepositoryWrapper :: processTemplate($contentTemplate, array (
1020 "content" => base64_encode($content
1021 ), "content_type" => $content_type));
1028 static function getSummaryTemplate()
1032 <atom:summary>{summary}</atom:summary>
1035 return ob_get_clean();
1038 static function getContentTemplate()
1052 return ob_get_clean();
1054 static function createAtomEntry($name, $properties)
1058 function getObject($objectId, $options = array ())
1061 $varmap["id"] = $objectId;
1062 $obj_url = $this->processTemplate($this->workspace->uritemplates['objectbyid'], $varmap);
1063 $ret = $this->doGet($obj_url);
1064 $obj = $this->extractObject($ret->body);
1065 $this->cacheObjectInfo($obj);
1069 function getObjectByPath($path, $options = array ())
1072 $varmap["path"] = $this->handleSpaces($path);
1073 $obj_url = $this->processTemplate($this->workspace->uritemplates['objectbypath'], $varmap);
1074 $ret = $this->doGet($obj_url);
1075 $obj = $this->extractObject($ret->body);
1076 $this->cacheObjectInfo($obj);
1080 function getProperties($objectId, $options = array ())
1082 // May need to set the options array default --
1083 return $this->getObject($objectId, $options);
1086 function getAllowableActions($objectId, $options = array ())
1088 $myURL = $this->getLink($objectId, LINK_ALLOWABLE_ACTIONS);
1089 $ret = $this->doGet($myURL);
1090 $result = $this->extractAllowableActions($ret->body);
1094 function getRenditions($objectId, $options = array (
1095 OPT_RENDITION_FILTER => "*"
1098 return getObject($objectId, $options);
1101 function getContentStream($objectId, $options = array ())
1103 $myURL = $this->getLink($objectId, "edit-media");
1104 $ret = $this->doGet($myURL);
1105 // doRequest stores the last request information in this object
1109 function postObject($folderId, $objectName, $objectType, $properties = array (), $content = null, $content_type = "application/octet-stream", $options = array ())
1111 $myURL = $this->getLink($folderId, "down");
1112 // TODO: Need Proper Query String Handling
1113 // Assumes that the 'down' link does not have a querystring in it
1114 $myURL = CMISRepositoryWrapper :: getOpUrl($myURL, $options);
1115 static $entry_template;
1116 if (!isset ($entry_template))
1118 $entry_template = CMISService :: getEntryTemplate();
1120 if (is_array($properties))
1122 $hash_values = $properties;
1125 $hash_values = array ();
1127 if (!isset ($hash_values["cmis:objectTypeId"]))
1129 $hash_values["cmis:objectTypeId"] = $objectType;
1131 $properties_xml = $this->processPropertyTemplates($hash_values["cmis:objectTypeId"], $hash_values);
1132 if (is_array($options))
1134 $hash_values = $options;
1137 $hash_values = array ();
1139 $hash_values["PROPERTIES"] = $properties_xml;
1140 $hash_values["SUMMARY"] = CMISService :: getSummaryTemplate();
1143 $hash_values["CONTENT"] = CMISService :: getContentEntry($content, $content_type);
1145 if (!isset ($hash_values['title']))
1147 $hash_values['title'] = $objectName;
1149 if (!isset ($hash_values['summary']))
1151 $hash_values['summary'] = $objectName;
1153 $post_value = CMISRepositoryWrapper :: processTemplate($entry_template, $hash_values);
1154 $ret = $this->doPost($myURL, $post_value, MIME_ATOM_XML_ENTRY);
1155 // print "DO_POST\n";
1157 $obj = $this->extractObject($ret->body);
1158 $this->cacheObjectInfo($obj);
1162 function createDocument($folderId, $fileName, $properties = array (), $content = null, $content_type = "application/octet-stream", $options = array ())
1164 return $this->postObject($folderId, $fileName, "cmis:document", $properties, $content, $content_type, $options);
1167 function createDocumentFromSource()
1169 throw new CmisNotSupportedException("createDocumentFromSource is not supported by the AtomPub binding!");
1172 function createFolder($folderId, $folderName, $properties = array (), $options = array ())
1174 return $this->postObject($folderId, $folderName, "cmis:folder", $properties, null, null, $options);
1177 function createRelationship()
1178 { // Not in first Release
1179 throw Exception("Not Implemented");
1182 function createPolicy()
1183 { // Not in first Release
1184 throw Exception("Not Implemented");
1187 function updateProperties($objectId, $properties = array (), $options = array ())
1190 $varmap["id"] = $objectId;
1191 $objectName = $this->getTitle($objectId);
1192 $objectType = $this->getObjectType($objectId);
1193 $obj_url = $this->getLink($objectId, "edit");
1194 $obj_url = CMISRepositoryWrapper :: getOpUrl($obj_url, $options);
1195 static $entry_template;
1196 if (!isset ($entry_template))
1198 $entry_template = CMISService :: getEntryTemplate();
1200 if (is_array($properties))
1202 $hash_values = $properties;
1205 $hash_values = array ();
1207 $properties_xml = $this->processPropertyTemplates($objectType, $hash_values);
1208 if (is_array($options))
1210 $hash_values = $options;
1213 $hash_values = array ();
1215 $hash_values["PROPERTIES"] = $properties_xml;
1216 $hash_values["SUMMARY"] = CMISService :: getSummaryTemplate();
1217 if (!isset ($hash_values['title']))
1219 $hash_values['title'] = $objectName;
1221 if (!isset ($hash_values['summary']))
1223 $hash_values['summary'] = $objectName;
1225 $put_value = CMISRepositoryWrapper :: processTemplate($entry_template, $hash_values);
1226 // print $put_value; // RRM DEBUG
1227 $ret = $this->doPut($obj_url, $put_value, MIME_ATOM_XML_ENTRY);
1228 $obj = $this->extractObject($ret->body);
1229 $this->cacheObjectInfo($obj);
1233 function moveObject($objectId, $targetFolderId, $sourceFolderId, $options = array ())
1235 $options['sourceFolderId'] = $sourceFolderId;
1236 return $this->postObject($targetFolderId, $this->getTitle($objectId), $this->getObjectType($objectId), array (
1237 "cmis:objectId" => $objectId
1238 ), null, null, $options);
1241 function deleteObject($objectId, $options = array ())
1244 $varmap["id"] = $objectId;
1245 $obj_url = $this->getLink($objectId, "edit");
1246 $ret = $this->doDelete($obj_url);
1250 function deleteTree()
1252 throw Exception("Not Implemented");
1255 function setContentStream($objectId, $content, $content_type, $options = array ())
1257 $myURL = $this->getLink($objectId, "edit-media");
1258 $ret = $this->doPut($myURL, $content, $content_type);
1261 function deleteContentStream($objectId, $options = array ())
1263 $myURL = $this->getLink($objectId, "edit-media");
1264 $ret = $this->doDelete($myURL);
1268 //Versioning Services
1269 function getPropertiesOfLatestVersion($objectId, $major =false, $options = array ())
1271 return $this->getObjectOfLatestVersion($objectId, $major, $options);
1274 function getObjectOfLatestVersion($objectId, $major = false, $options = array ())
1276 return $this->getObject($objectId, $options); // Won't be able to handle major/minor distinction
1277 // Need to add this -- "current-version"
1279 * Headers: CMIS-filter, CMIS-returnVersion (enumReturnVersion)
1280 * HTTP Arguments: filter, returnVersion
1281 * Enum returnVersion: This, Latest, Major
1285 function getAllVersions()
1287 throw Exception("Not Implemented");
1292 throw Exception("Not Implemented");
1297 throw Exception("Not Implemented");
1300 function cancelCheckOut()
1302 throw Exception("Not Implemented");
1305 function deleteAllVersions()
1307 throw Exception("Not Implemented");
1310 //Relationship Services
1311 function getObjectRelationships()
1313 // get stripped down version of object (for the links) and then get the relationships?
1314 // Low priority -- can get all information when getting object
1315 throw Exception("Not Implemented");
1318 //Multi-Filing Services
1319 function addObjectToFolder()
1321 throw Exception("Not Implemented");
1324 function removeObjectFromFolder()
1326 throw Exception("Not Implemented");
1330 function getAppliedPolicies()
1332 throw Exception("Not Implemented");
1335 function applyPolicy()
1337 throw Exception("Not Implemented");
1340 function removePolicy()
1342 throw Exception("Not Implemented");
1348 throw Exception("Not Implemented");
1353 throw Exception("Not Implemented");