You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by ri...@apache.org on 2013/07/03 04:39:57 UTC

svn commit: r1499181 - in /chemistry/phpclient/trunk/atom: ./ cmis-lib.php cmis/ cmis/cmis_repository_wrapper.php cmis/cmis_service.php

Author: richardm
Date: Wed Jul  3 02:39:56 2013
New Revision: 1499181

URL: http://svn.apache.org/r1499181
Log:
Refactoring and adding CMIS 1.1 stubs

Added:
    chemistry/phpclient/trunk/atom/
    chemistry/phpclient/trunk/atom/cmis/
    chemistry/phpclient/trunk/atom/cmis-lib.php
    chemistry/phpclient/trunk/atom/cmis/cmis_repository_wrapper.php
    chemistry/phpclient/trunk/atom/cmis/cmis_service.php

Added: chemistry/phpclient/trunk/atom/cmis-lib.php
URL: http://svn.apache.org/viewvc/chemistry/phpclient/trunk/atom/cmis-lib.php?rev=1499181&view=auto
==============================================================================
--- chemistry/phpclient/trunk/atom/cmis-lib.php (added)
+++ chemistry/phpclient/trunk/atom/cmis-lib.php Wed Jul  3 02:39:56 2013
@@ -0,0 +1,18 @@
+<?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.
+require_once('cmis/cmis_service.php');
\ No newline at end of file

Added: chemistry/phpclient/trunk/atom/cmis/cmis_repository_wrapper.php
URL: http://svn.apache.org/viewvc/chemistry/phpclient/trunk/atom/cmis/cmis_repository_wrapper.php?rev=1499181&view=auto
==============================================================================
--- chemistry/phpclient/trunk/atom/cmis/cmis_repository_wrapper.php (added)
+++ chemistry/phpclient/trunk/atom/cmis/cmis_repository_wrapper.php Wed Jul  3 02:39:56 2013
@@ -0,0 +1,764 @@
+<?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 CmisNotImplementedException extends Exception {}
+class CmisConstraintException extends Exception {}
+class CmisRuntimeException extends Exception {}
+
+/**
+ * @internal
+ */
+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
+    
+    
+    /**
+     * @internal
+     */
+    var $url;
+    
+    /**
+     * @internal
+     */
+    var $username;
+    
+    /**
+     * @internal
+     */
+    var $password;
+    
+    /**
+     * @internal
+     */
+    var $authenticated;
+    
+    /**
+     * @internal
+     */
+    var $workspace;
+    
+    /**
+     * @internal
+     */
+    var $last_request;
+    
+    /**
+     * @internal
+     */
+    var $do_not_urlencode;
+    
+    /**
+     * @internal
+     */
+    protected $_addlCurlOptions = array();
+    
+    
+    /**
+     * @internal
+     */
+    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",
+        
+    );
+
+	/**
+	 * @internal
+	 */
+    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);
+    }
+	/**
+	 * @internal
+	 */
+    static function getAsArray($prop) {
+    	if ($prop == null) {
+			return array();
+		} elseif (!is_array($prop)) {
+			return array($prop);
+		} else {
+			return($prop);
+		}
+		    	
+    }
+
+	/**
+	 * @internal
+	 */
+    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;
+        }
+    }
+    
+	/**
+	 * @internal
+	 */
+    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);
+            }
+    }
+
+	/**
+	 * @internal
+	 */
+    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);
+        }
+    }
+
+	/**
+	 * @internal
+	 */
+    function doGet($url)
+    {
+        $retval = $this->doRequest($url);
+        if ($retval->code != HTTP_OK)
+        {
+            throw $this->convertStatusCode($retval->code, $retval->body);
+        }
+        return $retval;
+    }
+
+	/**
+	 * @internal
+	 */
+    function doDelete($url)
+    {
+        $retval = $this->doRequest($url, "DELETE");
+        if ($retval->code != HTTP_NO_CONTENT)
+        {
+            throw $this->convertStatusCode($retval->code, $retval->body);
+        }
+        return $retval;
+    }
+
+	/**
+	 * @internal
+	 */
+    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;
+    }
+
+	/**
+	 * @internal
+	 */
+    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;
+    }
+
+	/**
+	 * @internal
+	 */
+    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
+	/**
+	 * @internal
+	 */
+    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);
+
+    }
+
+	/**
+	 * @internal
+	 */
+    static function doXQuery($xmldata, $xquery)
+    {
+        $doc = new DOMDocument();
+        $doc->loadXML($xmldata);
+        return CMISRepositoryWrapper :: doXQueryFromNode($doc, $xquery);
+    }
+
+	/**
+	 * @internal
+	 */
+    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);
+
+    }
+
+	/**
+	 * @internal
+	 */
+    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;
+    }
+
+	/**
+	 * @internal
+	 */
+	static function extractAllowableActions($xmldata)
+    {
+        $doc = new DOMDocument();
+        $doc->loadXML($xmldata);
+        return CMISRepositoryWrapper :: extractAllowableActionsFromNode($doc);
+    }
+
+	/**
+	 * @internal
+	 */
+    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;
+    }
+
+	/**
+	 * @internal
+	 */
+    static function extractObject($xmldata)
+    {
+        $doc = new DOMDocument();
+        $doc->loadXML($xmldata);
+        return CMISRepositoryWrapper :: extractObjectFromNode($doc);
+
+    }
+
+	/**
+	 * @internal
+	 */
+    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;
+    }
+    
+	/**
+	 * @internal
+	 */
+    function handleSpaces($path)
+    {
+        return $this->do_not_urlencode ? $path : rawurlencode($path);
+    }
+
+	/**
+	 * @internal
+	 */
+    static function extractTypeDef($xmldata)
+    {
+        $doc = new DOMDocument();
+        $doc->loadXML($xmldata);
+        return CMISRepositoryWrapper :: extractTypeDefFromNode($doc);
+
+    }
+
+	/**
+	 * @internal
+	 */
+    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 (isset($childern_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 :: 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;
+    }
+
+	/**
+	 * @internal
+	 */
+    static function extractObjectFeed($xmldata)
+    {
+        //Assumes only one workspace for now
+        $doc = new DOMDocument();
+        $doc->loadXML($xmldata);
+        return CMISRepositoryWrapper :: extractObjectFeedFromNode($doc);
+    }
+
+	/**
+	 * @internal
+	 */
+    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;
+    }
+
+	/**
+	 * @internal
+	 */
+    static function extractTypeFeed($xmldata)
+    {
+        //Assumes only one workspace for now
+        $doc = new DOMDocument();
+        $doc->loadXML($xmldata);
+        return CMISRepositoryWrapper :: extractTypeFeedFromNode($doc);
+    }
+
+	/**
+	 * @internal
+	 */
+    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;
+    }
+
+	/**
+	 * @internal
+	 */
+    static function extractWorkspace($xmldata)
+    {
+        //Assumes only one workspace for now
+        $doc = new DOMDocument();
+        $doc->loadXML($xmldata);
+        return CMISRepositoryWrapper :: extractWorkspaceFromNode($doc);
+    }
+
+	/**
+	 * @internal
+	 */
+    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;
+    }
+}

Added: chemistry/phpclient/trunk/atom/cmis/cmis_service.php
URL: http://svn.apache.org/viewvc/chemistry/phpclient/trunk/atom/cmis/cmis_service.php?rev=1499181&view=auto
==============================================================================
--- chemistry/phpclient/trunk/atom/cmis/cmis_service.php (added)
+++ chemistry/phpclient/trunk/atom/cmis/cmis_service.php Wed Jul  3 02:39:56 2013
@@ -0,0 +1,1230 @@
+<?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.
+require_once ('cmis_repository_wrapper.php');
+
+// 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("OPT_CHECK_IN_COMMENT", "checkinComment");
+define("OPT_CHECK_IN", "checkin");
+define("OPT_MAJOR_VERSION", "major");
+
+define("COLLECTION_ROOT_FOLDER","root");
+define("COLLECTION_TYPES","types");
+define("COLLECTION_CHECKED_OUT","checkedout");
+define("COLLECTION_QUERY","query");
+define("COLLECTION_UNFILED","unfiled");
+
+define("URI_TEMPLATE_OBJECT_BY_ID","objectbyid");
+define("URI_TEMPLATE_OBJECT_BY_PATH","objectbypath");
+define("URI_TEMPLATE_TYPE_BY_ID","typebyid");
+define("URI_TEMPLATE_QUERY","query");
+
+define("LINK_SELF", "self");
+define("LINK_SERVICE","service");
+define("LINK_DESCRIBED_BY", "describedby");
+define("LINK_VIA","via");
+define("LINK_EDIT_MEDIA", "edit-media");
+define("LINK_EDIT","edit");
+define("LINK_ALTERNATE", "alternate");
+define("LINK_FIRST","first");
+define("LINK_PREVIOUS", "previous");
+define("LINK_NEXT","next");
+define("LINK_LAST", "last");
+define("LINK_UP","up");
+define("LINK_DOWN", "down");
+define("LINK_DOWN_TREE","down-tree");
+define("LINK_VERSION_HISTORY","version-history");
+define("LINK_CURRENT_VERSION", "current-version");
+
+
+define("LINK_ALLOWABLE_ACTIONS", "http://docs.oasis-open.org/ns/cmis/link/200908/allowableactions");
+define("LINK_RELATIONSHIPS","http://docs.oasis-open.org/ns/cmis/link/200908/relationships");
+define("LINK_SOURCE","http://docs.oasis-open.org/ns/cmis/link/200908/source");
+define("LINK_TARGET","http://docs.oasis-open.org/ns/cmis/link/200908/target");
+define("LINK_POLICIES", "http://docs.oasis-open.org/ns/cmis/link/200908/policies");
+define("LINK_ACL","http://docs.oasis-open.org/ns/cmis/link/200908/acl");
+define("LINK_CHANGES","http://docs.oasis-open.org/ns/cmis/link/200908/changes");
+define("LINK_FOLDER_TREE","http://docs.oasis-open.org/ns/cmis/link/200908/foldertree");
+define("LINK_ROOT_DESCENDANTS","http://docs.oasis-open.org/ns/cmis/link/200908/rootdescendants");
+define("LINK_TYPE_DESCENDANTS","http://docs.oasis-open.org/ns/cmis/link/200908/typedescendants");
+
+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?
+/**
+ * CMIS Service
+ * 
+ * @api CMIS
+ * @since CMIS-1.0
+ */
+class CMISService extends CMISRepositoryWrapper {
+    
+    /**
+     * @internal
+     */
+	var $_link_cache;
+    
+    /**
+     * @internal
+     */
+	var $_title_cache;
+    
+    /**
+     * @internal
+     */
+	var $_objTypeId_cache;
+    
+    /**
+     * @internal
+     */
+	var $_type_cache;
+    
+    /**
+     * @internal
+     */
+	var $_changeToken_cache;
+    
+	/**
+	 * Construct a new CMISService Connector
+	 * 
+	 * @param String $url Endpoint URL
+	 * @param String $username Username
+	 * @param String $password Password
+	 * @param mixed[] $options Connection Options
+	 * @param mixed[] $addlCurlOptions Additional CURL Options
+	 * @api CMIS-Service
+	 * @since CMIS-1.0
+	 */
+	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 ();
+		$this->_changeToken_cache = array ();
+	}
+
+	// Utility Methods -- Added Titles
+	// Should refactor to allow for single object	
+    
+    /**
+     * @internal
+     */
+	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"];
+		if (isset($obj->properties["cmis:changeToken"])) {
+			$this->_changeToken_cache[$obj->id] = $obj->properties["cmis:changeToken"];
+		}
+	}
+
+	/**
+	 * Get an Object's property and return it as an array 
+	 * 
+	 * This returns an array even if it is a scalar or null
+	 * 
+	 * @todo Allow the getProperty method to query the object type information and
+	 * return multivalue properties as arrays even if empty or if only a single value
+	 * is present.
+	 * @param Object $obj Object
+	 * @param String $propName Property Name
+	 * @returns mixed[]
+	 * @api CMIS-Helper
+	 * @since CMIS-1.0
+	 */
+	function getMultiValuedProp($obj,$propName) {
+		if (isset($obj->properties[$propName])) {
+			return CMISRepositoryWrapper::getAsArray($obj->properties[$propName]);
+		}
+		return array();
+	}
+
+	/**
+	 * @internal
+	 */
+	function cacheFeedInfo($objs) {
+		foreach ($objs->objectList as $obj) {
+			$this->cacheObjectInfo($obj);
+		}
+	}
+
+	/**
+	 * @internal
+	 */
+	function cacheTypeFeedInfo($typs) {
+		foreach ($typs->objectList as $typ) {
+			$this->cacheTypeInfo($typ);
+		}
+	}
+
+	/**
+	 * @internal
+	 */
+	function cacheTypeInfo($tDef) {
+		// TODO: Fix Type Caching with missing properties
+		$this->_type_cache[$tDef->id] = $tDef;
+	}
+
+	/**
+	 * @internal
+	 */
+	function getPropertyType($typeId, $propertyId) {
+		if (isset($this->_type_cache[$typeId])) {
+			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"];
+	}
+
+	/**
+	 * @internal
+	 */
+	function getObjectType($objectId) {
+		if ($this->_objTypeId_cache[$objectId]) {
+			return $this->_objTypeId_cache[$objectId];
+		}
+		$obj = $this->getObject($objectId);
+		return $obj->properties["cmis:objectTypeId"];
+	}
+
+	/**
+	 * @internal
+	 */
+	function getTitle($objectId) {
+		if ($this->_title_cache[$objectId]) {
+			return $this->_title_cache[$objectId];
+		}
+		$obj = $this->getObject($objectId);
+		return $obj->properties["cmis:name"];
+	}
+
+	/**
+	 * @internal
+	 */
+	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];
+	}
+
+	/**
+	 * @internal
+	 */
+	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
+	// TODO: Need to fix this for multiple repositories
+	/**
+	 * Get an Object by Object Id
+	 * @api CMIS-RepositoryServices-NotImplemented
+	 * @since CMIS-1.0
+	 */
+	function getRepositories() {
+		throw CmisNotImplementedException("getRepositories");
+	}
+
+	/**
+	 * Get Repository Information
+	 * @returns Object
+	 * @api CMIS-RepositoryServices
+	 * @since CMIS-1.0
+	 */
+	function getRepositoryInfo() {
+		return $this->workspace;
+	}
+
+	/**
+	 * Get a set of object-types that are descendants of the specified type
+	 * 
+	 * If typeId is null, then the repository MUST return all types and ignore the depth parameter.
+	 *  
+	 * @param String $typeId The typeId of an object-type specified in the repository
+	 * @param $depth the number of levels in the hierarchy to return (-1 == all)
+	 * @returns Object The set of descendant object-types defined for the given typeId.
+	 * @api CMIS-RepositoryServices
+	 * @since CMIS-1.0
+	 */
+	function getTypeDescendants($typeId = null, $depth, $options = array ()) {
+		// TODO: Refactor Type Entries Caching
+		$varmap = $options;
+		if ($typeId) {
+			$hash_values = $options;
+			$hash_values[OPT_DEPTH] = $depth;
+			$myURL = $this->getTypeLink($typeId, LINK_DOWN_TREE);
+			$myURL = CMISRepositoryWrapper :: getOpUrl($myURL, $hash_values);
+		} else {
+			$myURL = $this->processTemplate($this->workspace->links[LINK_TYPE_DESCENDANTS], $varmap);
+		}
+		$ret = $this->doGet($myURL);
+		$typs = $this->extractTypeFeed($ret->body);
+		$this->cacheTypeFeedInfo($typs);
+		return $typs;
+	}
+
+	/**
+	 * Get a list of object-types that are children of the specified type
+	 * 
+	 * If typeId is null, then the repository MUST return all base object-types.
+	 *  
+	 * @param String $typeId The typeId of an object-type specified in the repository
+	 * @returns Object The list of child object-types defined for the given typeId.
+	 * @api CMIS-RepositoryServices
+	 * @since CMIS-1.0
+	 */
+	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;
+	}
+
+	/**
+	 * Gets the definition of the specified object-type.
+	 *  
+	 * @param String $typeId Object Type Id
+	 * @returns Object Type Definition of the Specified Object
+	 * @api CMIS-RepositoryServices
+	 * @since CMIS-1.0
+	 */
+	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;
+	}
+
+	/**
+	 * Get an Object's Property Type by Object Id
+	 * @param String $objectId Object Id
+	 * @returns Object Type Definition of the Specified Object
+	 * @api CMIS-Helper
+	 * @since CMIS-1.0
+	 */
+	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;
+	}
+	//Repository Services -- New for 1.1
+	/**
+	 * Creates a new type definition.
+	 * 
+	 * Creates a new type definition that is a subtype of an existing specified parent type.
+	 * Only properties that are new to this type (not inherited) are passed to this service.
+	 *
+	 * @param String $objectType A type definition object with the property definitions that are to change.
+	 * @returns Object Type Definition of the Specified Object
+	 * @api CMIS-RepositoryServices-NotImplemented
+	 * @since CMIS-1.1
+	 */
+	function createType($objectType) {
+		throw CmisNotImplementedException("createType");		
+	}
+
+	/**
+	 * Updates a type definition
+	 * 
+	 * If you add an optional property to a type in error. There is no way to remove it/correct it - without
+	 * deleting the type.
+	 * 
+	 * @param String $objectType A type definition object with the property definitions that are to change.
+	 * @returns Object The updated object-type including all property definitions.
+	 * @api CMIS-RepositoryServices-NotImplemented
+	 * @since CMIS-1.1
+	 */
+	function updateType($objectType) {
+		throw CmisNotImplementedException("updateType");		
+	}
+
+	/**
+	 * Deletes a type definition
+	 * 
+	 * If there are object instances present of the type being deleted then this operation MUST fail.
+	 *
+	 * @param String $typeId The typeId of an object-type specified in the repository.
+	 * @api CMIS-RepositoryServices-NotImplemented
+	 * @since CMIS-1.1
+	 */
+	function deleteType($typeId) {
+		throw CmisNotImplementedException("deleteType");		
+	}
+	//Navigation Services
+	/**
+	 * Get the list of descendant folders contained in the specified folder.
+	 * 
+	 * @param String $folderId the Object ID of the folder
+	 * @param String $depth The number of levels of depth in the folder hierarchy from which to return results (-1 == ALL).
+	 * @returns Object[] A tree of the child objects for the specified folder.
+	 * @api CMIS-NavigationServices
+	 * @since CMIS-1.0
+	 */
+	function getFolderTree($folderId, $depth, $options = array ()) {
+		$hash_values = $options;
+		$hash_values[OPT_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;
+	}
+
+	/**
+	 * Get the list of descendant objects contained in the specified folder.
+	 * 
+	 * @param String $folderId the Object ID of the folder
+	 * @param String $depth The number of levels of depth in the folder hierarchy from which to return results (-1 == ALL).
+	 * @returns Object[] A tree of the child objects for the specified folder.
+	 * @api CMIS-NavigationServices
+	 * @since CMIS-1.0
+	 */
+	function getDescendants($folderId, $depth, $options = array ()) { // Nice to have
+		$hash_values = $options;
+		$hash_values[OPT_DEPTH] = $depth;
+		$myURL = $this->getLink($folderId, LINK_DOWN_TREE);
+		$myURL = CMISRepositoryWrapper :: getOpUrl($myURL, $hash_values);
+		$ret = $this->doGet($myURL);
+		$objs = $this->extractObjectFeed($ret->body);
+		$this->cacheFeedInfo($objs);
+		return $objs;
+	}
+
+	/**
+	 * Get the list of child objects contained in the specified folder.
+	 * 
+	 * @param String $folderId the Object ID of the folder
+	 * @returns Object[] A list of the child objects for the specified folder.
+	 * @api CMIS-NavigationServices
+	 * @since CMIS-1.0
+	 */
+	function getChildren($folderId, $options = array ()) {
+		$myURL = $this->getLink($folderId, LINK_DOWN);
+		//TODO: Need GenURLQueryString Utility
+		$ret = $this->doGet($myURL);
+		$objs = $this->extractObjectFeed($ret->body);
+		$this->cacheFeedInfo($objs);
+		return $objs;
+	}
+
+	/**
+	 * Get the parent folder of the specified folder.
+	 * 
+	 * @param String $folderId the Object ID of the folder
+	 * @returns Object the parent folder.
+	 * @api CMIS-NavigationServices
+	 * @since CMIS-1.0
+	 */
+	function getFolderParent($folderId, $options = array ()) { //yes
+		$myURL = $this->getLink($folderId, LINK_UP);
+		//TODO: Need GenURLQueryString Utility
+		$ret = $this->doGet($myURL);
+		$obj = $this->extractObjectEntry($ret->body);
+		$this->cacheObjectInfo($obj);
+		return $obj;
+	}
+
+	/**
+	 * Get the parent folder(s) for the specified fileable object.
+	 * 
+	 * @param String $objectId the Object ID of the Object
+	 * @returns Object[] list of the parent folder(s) of the specified object.
+	 * @api CMIS-NavigationServices
+	 * @since CMIS-1.0
+	 */
+	function getObjectParents($objectId, $options = array ()) { // yes
+		$myURL = $this->getLink($objectId, LINK_UP);
+		//TODO: Need GenURLQueryString Utility
+		$ret = $this->doGet($myURL);
+		$objs = $this->extractObjectFeed($ret->body);
+		$this->cacheFeedInfo($objs);
+		return $objs;
+	}
+
+	/**
+	 * Get the list of documents that are checked out that the user has access to..
+	 * 
+	 * @returns Object[] list of checked out documents.
+	 * @api CMIS-NavigationServices
+	 * @since CMIS-1.0
+	 */
+	function getCheckedOutDocs($options = array ()) {
+		$obj_url = $this->workspace->collections[COLLECTION_CHECKED_OUT];
+		$ret = $this->doGet($obj_url);
+		$objs = $this->extractObjectFeed($ret->body);
+		$this->cacheFeedInfo($objs);
+		return $objs;
+	}
+
+	//Discovery Services
+	/**
+	 * @internal
+	 */
+	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();
+	}
+
+	/**
+	 * Execute a CMIS Query
+	 * @param String $statement Query Statement
+	 * @param mixed[] $options Options
+	 * @returns Object[] List of object propery values from query
+	 * @api CMIS-DiscoveryServices
+	 * @since CMIS-1.0
+	 */
+	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;
+	}
+
+	/**
+	 * @internal
+	 */
+	function checkURL($url,$functionName=null) {
+		if (!$url) {
+			throw new CmisNotSupportedException($functionName?$functionName:"UnspecifiedMethod");
+		}
+	}
+
+	/**
+	 * Get Content Changes
+	 * @param mixed[] $options Options
+	 * @returns Object[] List of Change Events
+	 * @api CMIS-DiscoveryServices
+	 * @since CMIS-1.0
+	 */
+	function getContentChanges($options = array()) {
+		$myURL =  CMISRepositoryWrapper :: processTemplate($this->workspace->links[LINK_CHANGES],$options);
+		$this->checkURL($myURL,"getContentChanges");
+		$ret = $this->doGet($myURL);
+		$objs = $this->extractObjectFeed($ret->body);
+		$this->cacheFeedInfo($objs);
+		return $objs;
+	}
+
+	//Object Services
+	/**
+	 * @internal
+	 */
+	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();
+	}
+
+    
+    /**
+     * @internal
+     */
+	static function getPropertyTemplate() {
+		ob_start();
+?>
+		<cmis:property{propertyType} propertyDefinitionId="{propertyId}">
+			<cmis:value>{properties}</cmis:value>
+		</cmis:property{propertyType}>
+<?php
+
+
+		return ob_get_clean();
+	}
+
+    
+    /**
+     * @internal
+     */
+	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;
+	}
+	/**
+	 * @internal
+	 */
+	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 "";
+		}
+	}
+
+	/**
+	 * @internal
+	 */
+	static function getSummaryTemplate() {
+		ob_start();
+?>
+		<atom:summary>{summary}</atom:summary>
+<?php
+
+
+		return ob_get_clean();
+	}
+
+	/**
+	 * @internal
+	 */
+	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();
+	}
+	/**
+	 * @internal
+	 */
+	static function createAtomEntry($name, $properties) {
+
+	}
+
+	/**
+	 * Get an Object by Object Id
+	 * @param String $objectId Object ID
+	 * @param mixed[] $options Options
+	 * @returns Object
+	 * @api CMIS-ObjectServices
+	 * @since CMIS-1.0
+	 */
+	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;
+	}
+
+	/**
+	 * Get an Object by its Path
+	 * @param String $path Path To Object
+	 * @param mixed[] $options Options
+	 * @returns Object
+	 * @api CMIS-ObjectServices
+	 * @since CMIS-1.0
+	 */
+	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;
+	}
+
+	/**
+	 * Get an Object's Properties by Object Id
+	 * @param String $objectId Object Id
+	 * @param mixed[] $options Options
+	 * @returns Object
+	 * @api CMIS-ObjectServices
+	 * @since CMIS-1.0
+	 */
+	function getProperties($objectId, $options = array ()) {
+		// May need to set the options array default -- 
+		return $this->getObject($objectId, $options);
+	}
+
+	/**
+	 * Get an Object's Allowable Actions
+	 * @param String $objectId Object Id
+	 * @param mixed[] $options Options
+	 * @returns mixed[]
+	 * @api CMIS-ObjectServices
+	 * @since CMIS-1.0
+	 */
+	function getAllowableActions($objectId, $options = array ()) {
+		$myURL = $this->getLink($objectId, LINK_ALLOWABLE_ACTIONS);
+		$ret = $this->doGet($myURL);
+		$result = $this->extractAllowableActions($ret->body);
+		return $result;
+	}
+
+	/**
+	 * Get the list of associated renditions for the specified object
+	 * 
+	 * Only rendition attributes are returned, not rendition stream.
+	 * @param String $objectId Object Id
+	 * @param mixed[] $options Options
+	 * @returns Object[]
+	 * @api CMIS-ObjectServices
+	 * @since CMIS-1.0
+	 */
+	function getRenditions($objectId, $options = array (
+		OPT_RENDITION_FILTER => "*"
+	)) {
+		return getObject($objectId, $options);
+	}
+
+	/**
+	 * Get an Object's Allowable Actions
+	 * @param String $objectId Object Id
+	 * @param mixed[] $options Options
+	 * @returns String
+	 * @api CMIS-ObjectServices
+	 * @since CMIS-1.0
+	 */
+	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;
+	}
+	
+	/**
+	 * @internal
+	 */
+    function legacyPostObject($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;
+    }
+    
+    /**
+     * @internal
+     */
+	function postObject($folderId, $objectName, $objectType, $properties = array (), $content = null, $content_type = "application/octet-stream", $options = array ()) {
+		$myURL = $this->getLink($folderId, "down");
+		// TODO: Need Proper Query String Handling
+		// Assumes that the 'down' link does not have a querystring in it
+		if (is_array($properties)) {
+			$hash_values = $properties;
+		} else {
+			$hash_values = array ();
+		}
+		if (!isset ($hash_values["cmis:objectTypeId"])) {
+			$hash_values["cmis:objectTypeId"] = $objectType;
+		}
+		if (!isset ($hash_values['title'])) {
+			$hash_values['title'] = $objectName;
+		}
+		if (!isset ($hash_values['summary'])) {
+			$hash_values['summary'] = $objectName;
+		}
+		$this->postEntry($myURL, $hash_values);
+	}
+    
+    /**
+     * @internal
+     */
+	function postEntry($url, $properties = array (), $content = null, $content_type = "application/octet-stream", $options = array ()) {
+		// TODO: Fix Hack HERE -- get type if it is there otherwise retrieve it --
+		$objectType ="";
+		if (isset($properties['cmis:objectTypeId'])) {
+			$objType = $properties['cmis:objectTypeId'];
+		} else if (isset($properties["cmis:objectId"])) {
+			$objType=$this->getObjectType($properties["cmis:objectId"]);			
+		}
+		$myURL = CMISRepositoryWrapper :: getOpUrl($url, $options);
+		//DEBUG
+		print("DEBUG: postEntry: myURL = " . $myURL);
+		static $entry_template;
+		if (!isset ($entry_template)) {
+			$entry_template = CMISService :: getEntryTemplate();
+		}
+		print("DEBUG: postEntry: entry_template = " . $entry_template);		
+		$properties_xml = $this->processPropertyTemplates($objType, $properties);
+		print("DEBUG: postEntry: properties_xml = " . $properties_xml);		
+		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);
+		}
+		print("DEBUG: postEntry: hash_values = " . print_r($hash_values,true));		
+		$post_value = CMISRepositoryWrapper :: processTemplate($entry_template, $hash_values);
+		print("DEBUG: postEntry: post_value = " . $post_value);		
+		$ret = $this->doPost($myURL, $post_value, MIME_ATOM_XML_ENTRY);
+		$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->legacyPostObject($folderId, $folderName, "cmis:folder", $properties, null, null, $options);
+	}
+
+	function createRelationship() { // Not in first Release
+		throw CmisNotImplementedException("createRelationship");
+	}
+
+	function createPolicy() { // Not in first Release
+		throw CmisNotImplementedException("createPolicy");
+	}
+	
+	function createItem() {
+		throw CmisNotImplementedException("createItem");
+	}
+
+	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 ();
+		}
+		if ($this->_changeToken_cache[$objectId] != null) {
+			$properties['cmis:changeToken'] = $this->_changeToken_cache[$objectId];
+		}
+		
+		$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;
+	}
+	
+	// New for 1.1
+	function bulkUpdateProperties() {
+		throw CmisNotImplementedException("bulkUpdateProperties");		
+	}
+
+	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);
+	}
+
+	/**
+	 * Delete an Object
+	 * @param String $objectId Object ID
+	 * @param mixed[] $options Options
+	 * @api CMIS-ObjectServices
+	 * @since CMIS-1.0
+	 */
+	function deleteObject($objectId, $options = array ()) { //Yes
+		$varmap = $options;
+		$varmap["id"] = $objectId;
+		$obj_url = $this->getLink($objectId, "edit");
+		$ret = $this->doDelete($obj_url);
+		return;
+	}
+
+	/**
+	 * Delete an Object Tree
+	 * @param String $folderId Folder Object ID
+	 * @param mixed[] $options Options
+	 * @return Object[] Array of problem objects
+	 * @api CMIS-ObjectServices
+	 * @since CMIS-1.0
+	 */
+	function deleteTree($folderId, $options = array ()) { // Nice to have
+		$hash_values = $options;
+		$myURL = $this->getLink($folderId, LINK_DOWN_TREE);
+		$myURL = CMISRepositoryWrapper :: getOpUrl($myURL, $hash_values);
+		$ret = $this->doDelete($myURL);
+		//List of problem objects
+		$objs = $this->extractObjectFeed($ret->body);
+		$this->cacheFeedInfo($objs);
+		return $objs;
+	}
+
+	/**
+	 * Set an Objects Content Stream
+	 * @param String $objectId Object ID
+	 * @param String $content Content to be appended
+	 * @param String $content_type Content Mime Type
+	 * @param mixed[] $options Options
+	 * @returns Object
+	 * @api CMIS-ObjectServices
+	 * @since CMIS-1.0
+	 */
+	function setContentStream($objectId, $content, $content_type, $options = array ()) { //Yes
+		$myURL = $this->getLink($objectId, "edit-media");
+		$ret = $this->doPut($myURL, $content, $content_type);
+	}
+
+	// New for 1.1
+	/**
+	 * Append Content to an Objects Content Stream
+	 * @param String $objectId Object ID
+	 * @param String $content Content to be appended
+	 * @param String $content_type Content Mime Type
+	 * @param mixed[] $options Options
+	 * @returns Object
+	 * @api CMIS-ObjectServices-NotImplemented
+	 * @since CMIS-1.0
+	 */
+	function appendContentStream($objectId, $content, $content_type, $options = array ()) { //Yes
+		throw CmisNotImplementedException("appendContentStream");
+	}
+
+	/**
+	 * Delete an Objects Content Stream
+	 * @param String $objectId Object ID
+	 * @param mixed[] $options Options
+	 * @api CMIS-ObjectServices
+	 * @since CMIS-1.0
+	 */
+	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 CmisNotImplementedException("getAllVersions");
+	}
+
+	/**
+	 * Checkout
+	 * @param String $objectId Object ID
+	 * @param mixed[] $options Options
+	 * @return Object The working copy
+	 * @api CMIS-VersionServices
+	 * @since CMIS-1.0
+	 */
+	function checkOut($objectId,$options = array()) {
+		$myURL = $this->workspace->collections[COLLECTION_CHECKED_OUT];
+		$myURL = CMISRepositoryWrapper :: getOpUrl($myURL, $options);
+		$ret = $this->postEntry($myURL,  array ("cmis:objectId" => $objectId));
+		$obj = $this->extractObject($ret->body);
+		$this->cacheObjectInfo($obj);
+		return $obj;
+	}
+
+	/**
+	 * Checkin
+	 * @param String $objectId Object ID
+	 * @param mixed[] $options Options
+	 * @return Object The checked in object
+	 * @api CMIS-VersionServices
+	 * @since CMIS-1.0
+	 */
+	function checkIn($objectId,$options = array()) {
+		$myURL = $this->workspace->collections[COLLECTION_CHECKED_OUT];
+		$myURL = CMISRepositoryWrapper :: getOpUrl($myURL, $options);
+		$ret = $this->postEntry($myURL,  array ("cmis:objectId" => $objectId));
+		$obj = $this->extractObject($ret->body);
+		$this->cacheObjectInfo($obj);
+		return $obj;
+	}
+
+	/**
+	 * Cancel Checkout
+	 * @param String $objectId Object ID
+	 * @param mixed[] $options Options
+	 * @api CMIS-VersionServices
+	 * @since CMIS-1.0
+	 */
+	function cancelCheckOut($objectId,$options = array()) {
+		// TODO: Look at links "via" and "working-copy"
+		$varmap = $options;
+		$varmap["id"] = $objectId;
+		$via = $this->getLink($objectId,"via");
+		print("DEBUG: cancelCheckOut VIA="+$via);
+		if (!$via) {
+			throw new CmisInvalidArgumentException("Not a WORKING COPY!");
+		}
+		$obj_url = $this->getLink($objectId, "edit");
+		$ret = $this->doDelete($obj_url);
+		return;
+	}
+
+	function deleteAllVersions() {
+		throw CmisNotImplementedException("deleteAllVersions");
+	}
+
+	//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 CmisNotImplementedException("getObjectRelationships");
+	}
+
+	//Multi-Filing ServicesRelation
+	function addObjectToFolder($objectId, $targetFolderId, $options = array ()) { // Probably
+		return $this->postObject($targetFolderId, $this->getTitle($objectId), $this->getObjectType($objectId), array (
+			"cmis:objectId" => $objectId
+		), null, null, $options);
+	}
+
+	function removeObjectFromFolder($objectId, $targetFolderId, $options = array ()) { //Probably
+		$hash_values = $options;
+		$myURL = $this->workspace->collections['unfiled'];
+		$myURL = CMISRepositoryWrapper :: getOpUrl($myURL, $hash_values);
+		$ret = $this->postEntry($myURL,  array ("cmis:objectId" => $objectId),null,null,array("removeFrom" => $targetFolderId));
+		$obj = $this->extractObject($ret->body);
+		$this->cacheObjectInfo($obj);
+		return $obj;
+	}
+
+	//Policy Services
+	function getAppliedPolicies() {
+		throw CmisNotImplementedException("getAppliedPolicies");
+	}
+
+	function applyPolicy() {
+		throw CmisNotImplementedException("applyPolicy");
+	}
+
+	function removePolicy() {
+		throw CmisNotImplementedException("removePolicy");
+	}
+
+	//ACL Services
+	function getACL() {
+		throw CmisNotImplementedException("getACL");
+	}
+
+	function applyACL() {
+		throw CmisNotImplementedException("applyACL");
+	}
+}
\ No newline at end of file