Commit 1f10b1709cd50df4a45d89facbe0c4e53a2b3653

Authored by Paul Barrett
1 parent 9f69bab5

Added AtomPub level implementation for checkout related functions

Story ID:1093055. CMIS Checkout

In Progress

Committed by: Paul Barrett

Reviewed by: Jarrett Jordaan
ktapi/ktapi.inc.php
@@ -3254,6 +3254,8 @@ class KTAPI @@ -3254,6 +3254,8 @@ class KTAPI
3254 * @param boolean $userSpecific limit to current user 3254 * @param boolean $userSpecific limit to current user
3255 * @return $checkedout An array of checked out documents 3255 * @return $checkedout An array of checked out documents
3256 */ 3256 */
  3257 + // TODO determine whether the listing is showing docs the user should not be able to see
  3258 + // (when not restricting to docs checked out by that user)
3257 public function get_checkedout_docs($userSpecific = true) 3259 public function get_checkedout_docs($userSpecific = true)
3258 { 3260 {
3259 $checkedout = array(); 3261 $checkedout = array();
lib/api/ktcmis/classes/CMISDocumentPropertyCollection.inc.php
@@ -66,6 +66,14 @@ class CMISDocumentPropertyCollection extends CMISPropertyCollection { @@ -66,6 +66,14 @@ class CMISDocumentPropertyCollection extends CMISPropertyCollection {
66 function __construct() 66 function __construct()
67 { 67 {
68 parent::__construct(); 68 parent::__construct();
  69 + self::$propertyTypes = array_merge(self::$propertyTypes, array('ContentStreamAllowed' => 'propertyString',
  70 + 'ContentStreamLength' => 'propertyInteger',
  71 + 'ContentStreamMimeType' => 'propertyString',
  72 + 'ContentStreamFilename' => 'propertyString',
  73 + 'ContentStreamUri' => 'propertyUri',
  74 + 'IsVersionSeriesCheckedOut' => 'propertyBoolean',
  75 + 'VersionSeriesCheckedOutBy' => 'propertyString',
  76 + 'VersionSeriesCheckedOutId' => 'propertyId'));
69 } 77 }
70 78
71 } 79 }
lib/api/ktcmis/classes/CMISPropertyCollection.inc.php
@@ -55,29 +55,25 @@ abstract class CMISPropertyCollection { @@ -55,29 +55,25 @@ abstract class CMISPropertyCollection {
55 static $LastModificationDate; 55 static $LastModificationDate;
56 static $ChangeToken; 56 static $ChangeToken;
57 // TODO these definitions belong in their own classe definition (see property type definions,) but here will do for now 57 // TODO these definitions belong in their own classe definition (see property type definions,) but here will do for now
58 - static $propertyTypes; 58 + static public $propertyTypes;
59 59
60 function __construct() 60 function __construct()
61 { 61 {
62 - $this->propertyTypes = array('ObjectId' => 'propertyId',  
63 - 'BaseType' => 'propertyString',  
64 - 'ObjectTypeId' => 'propertyString',  
65 - 'CreatedBy' => 'propertyString',  
66 - 'CreationDate' => 'propertyDateTime',  
67 - 'LastModifiedBy' => 'propertyString',  
68 - 'LastModificationDate' => 'propertyDateTime',  
69 - 'Name' => 'propertyString',  
70 - 'ContentStreamAllowed' => 'propertyString',  
71 - 'ContentStreamLength' => 'propertyInteger',  
72 - 'ContentStreamMimeType' => 'propertyString',  
73 - 'ContentStreamFilename' => 'propertyString',  
74 - 'ContentStreamUri' => 'propertyUri',  
75 - 'Uri' => 'propertyUri',  
76 - 'AllowedChildObjectTypeIds' => 'propertyId',  
77 - 'CreatedBy' => 'propertyString',  
78 - 'CreationDate' => 'propertyDateTime',  
79 - 'ChangeToken' => 'propertyString',  
80 - 'ParentId' => 'propertyId'); 62 + self::$propertyTypes = array('ObjectId' => 'propertyId',
  63 + 'Author' => 'propertyString',
  64 + 'BaseType' => 'propertyString',
  65 + 'ObjectTypeId' => 'propertyString',
  66 + 'CreatedBy' => 'propertyString',
  67 + 'CreationDate' => 'propertyDateTime',
  68 + 'LastModifiedBy' => 'propertyString',
  69 + 'LastModificationDate' => 'propertyDateTime',
  70 + 'Name' => 'propertyString',
  71 + 'Uri' => 'propertyUri',
  72 + 'AllowedChildObjectTypeIds' => 'propertyId',
  73 + 'CreatedBy' => 'propertyString',
  74 + 'CreationDate' => 'propertyDateTime',
  75 + 'ChangeToken' => 'propertyString',
  76 + 'ParentId' => 'propertyId');
81 } 77 }
82 78
83 /** 79 /**
lib/api/ktcmis/ktcmis.inc.php
@@ -458,12 +458,12 @@ class KTNavigationService extends KTCMISBase { @@ -458,12 +458,12 @@ class KTNavigationService extends KTCMISBase {
458 "message" => "Failed getting list of checked out documents" 458 "message" => "Failed getting list of checked out documents"
459 ); 459 );
460 } 460 }
461 - 461 +
462 // convert to array format for external code 462 // convert to array format for external code
463 $co = array(); 463 $co = array();
464 - foreach ($checkedout as $document) 464 + foreach ($checkedout as $documentProperties)
465 { 465 {
466 - $co[] = $document->getProperty('ObjectId'); 466 + $co[] = CMISUtil::createObjectPropertiesEntry($documentProperties);;
467 } 467 }
468 468
469 return array( 469 return array(
lib/api/ktcmis/services/CMISNavigationService.inc.php
@@ -268,7 +268,7 @@ class CMISNavigationService { @@ -268,7 +268,7 @@ class CMISNavigationService {
268 foreach($results as $document) 268 foreach($results as $document)
269 { 269 {
270 $CMISDocument = new CMISDocumentObject($document->getId(), $this->ktapi); 270 $CMISDocument = new CMISDocumentObject($document->getId(), $this->ktapi);
271 - $checkedout[] = $CMISDocument; 271 + $checkedout[] = $CMISDocument->getProperties();
272 } 272 }
273 273
274 return $checkedout; 274 return $checkedout;
lib/api/ktcmis/util/CMISUtil.inc.php
@@ -270,7 +270,20 @@ class CMISUtil { @@ -270,7 +270,20 @@ class CMISUtil {
270 static public function createObjectPropertiesEntry($properties) 270 static public function createObjectPropertiesEntry($properties)
271 { 271 {
272 $object = array(); 272 $object = array();
  273 +
  274 + foreach(CMISPropertyCollection::$propertyTypes as $property => $type)
  275 + {
  276 + // hack for Author property
  277 + if ($property == 'Author') {
  278 + $object[$property] = array('value' => $properties->getValue($property));
  279 + }
  280 + else {
  281 + $object['properties'][$property] = array('type' => $type, 'value' => $properties->getValue($property));
  282 + }
  283 + }
273 284
  285 + /* old static method */
  286 + /*
274 $object['Author'] = array('value' => $properties->getValue('Author')); 287 $object['Author'] = array('value' => $properties->getValue('Author'));
275 288
276 $object['properties']['BaseType'] = array('type' => $properties->getFieldType('BaseType'), 289 $object['properties']['BaseType'] = array('type' => $properties->getFieldType('BaseType'),
@@ -331,7 +344,10 @@ class CMISUtil { @@ -331,7 +344,10 @@ class CMISUtil {
331 'value' => $properties->getValue('ContentStreamUri')); 344 'value' => $properties->getValue('ContentStreamUri'));
332 } 345 }
333 } 346 }
  347 + */
334 348
  349 + /* what on earth was this for? */
  350 + /*
335 // if we have found a child/parent with one or more children/parents, recurse into the child/parent object 351 // if we have found a child/parent with one or more children/parents, recurse into the child/parent object
336 if (count($entry['items']) > 0) { 352 if (count($entry['items']) > 0) {
337 $object[$linkText] = CMISUtil::decodeObjectHierarchy($entry['items'], $linkText); 353 $object[$linkText] = CMISUtil::decodeObjectHierarchy($entry['items'], $linkText);
@@ -341,6 +357,7 @@ class CMISUtil { @@ -341,6 +357,7 @@ class CMISUtil {
341 else { 357 else {
342 $object[$linkText] = null; 358 $object[$linkText] = null;
343 } 359 }
  360 + */
344 361
345 return $object; 362 return $object;
346 } 363 }
webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php
@@ -37,7 +37,11 @@ include_once CMIS_ATOM_LIB_FOLDER . 'VersioningService.inc.php'; @@ -37,7 +37,11 @@ include_once CMIS_ATOM_LIB_FOLDER . 'VersioningService.inc.php';
37 include_once 'KT_cmis_atom_service_helper.inc.php'; 37 include_once 'KT_cmis_atom_service_helper.inc.php';
38 38
39 // TODO consider changing all responses from the webservice layer to return PEAR errors or success results instead of the half/half we have at the moment. 39 // TODO consider changing all responses from the webservice layer to return PEAR errors or success results instead of the half/half we have at the moment.
40 -// the half/half occurred because on initial services PEAR Error seemed unnecessary, but it has proven useful for some of the newer functions :) 40 +// the half/half occurred because on initial services PEAR Error seemed unnecessary, but it has proven useful for some of the newer functions
  41 +
  42 +// TODO proper first/last links
  43 +// FIXME any incorrect or missing links
  44 +// FIXME ContentStreamAllowed tag is empty (at least sometimes)
41 45
42 /** 46 /**
43 * AtomPub Service: folder 47 * AtomPub Service: folder
@@ -236,6 +240,7 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { @@ -236,6 +240,7 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service {
236 $this->responseFeed = $feed; 240 $this->responseFeed = $feed;
237 return null; 241 return null;
238 } 242 }
  243 +
239 // list of failed objects? 244 // list of failed objects?
240 if (is_array($response)) 245 if (is_array($response))
241 { 246 {
@@ -277,16 +282,13 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service { @@ -277,16 +282,13 @@ class KT_cmis_atom_service_folder extends KT_cmis_atom_service {
277 */ 282 */
278 private function getFolderChildrenFeed($NavigationService, $repositoryId, $folderId, $folderName, $feedType = 'children') 283 private function getFolderChildrenFeed($NavigationService, $repositoryId, $folderId, $folderName, $feedType = 'children')
279 { 284 {
280 - if ($feedType == 'children')  
281 - { 285 + if ($feedType == 'children') {
282 $entries = $NavigationService->getChildren($repositoryId, $folderId, false, false); 286 $entries = $NavigationService->getChildren($repositoryId, $folderId, false, false);
283 } 287 }
284 - else if ($feedType == 'descendants')  
285 - { 288 + else if ($feedType == 'descendants') {
286 $entries = $NavigationService->getDescendants($repositoryId, $folderId, false, false); 289 $entries = $NavigationService->getDescendants($repositoryId, $folderId, false, false);
287 } 290 }
288 - else  
289 - { 291 + else {
290 // error, we shouldn't be here, if we are then the wrong service/function was called 292 // error, we shouldn't be here, if we are then the wrong service/function was called
291 } 293 }
292 294
@@ -430,7 +432,6 @@ class KT_cmis_atom_service_type extends KT_cmis_atom_service { @@ -430,7 +432,6 @@ class KT_cmis_atom_service_type extends KT_cmis_atom_service {
430 /** 432 /**
431 * AtomPub Service: checkedout 433 * AtomPub Service: checkedout
432 */ 434 */
433 -// NOTE this is always an empty document, underlying API code still to be implemented  
434 class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { 435 class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service {
435 436
436 /** 437 /**
@@ -444,10 +445,11 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { @@ -444,10 +445,11 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service {
444 $repositories = $RepositoryService->getRepositories(); 445 $repositories = $RepositoryService->getRepositories();
445 $repositoryId = $repositories[0]['repositoryId']; 446 $repositoryId = $repositories[0]['repositoryId'];
446 447
447 - $checkedout = $NavigationService->getCheckedoutDocs($repositoryId); 448 + $checkedout = $NavigationService->getCheckedOutDocs($repositoryId);
448 449
449 //Create a new response feed 450 //Create a new response feed
450 $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); 451 $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI);
  452 + $workspace = $feed->getWorkspace();
451 453
452 $feed->newField('title', 'Checked out Documents', $feed); 454 $feed->newField('title', 'Checked out Documents', $feed);
453 455
@@ -460,27 +462,37 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { @@ -460,27 +462,37 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service {
460 462
461 // TODO get actual most recent update time, only use current if no other available 463 // TODO get actual most recent update time, only use current if no other available
462 $feed->appendChild($feed->newElement('updated', KT_cmis_atom_service_helper::formatDatestamp())); 464 $feed->appendChild($feed->newElement('updated', KT_cmis_atom_service_helper::formatDatestamp()));
463 -  
464 - foreach($checkedout as $document)  
465 - {  
466 - $entry = $feed->newEntry();  
467 - $objectElement = $feed->newElement('cmis:object');  
468 - $propertiesElement = $feed->newElement('cmis:properties');  
469 -  
470 - foreach($cmisEntry['properties'] as $propertyName => $property)  
471 - {  
472 - $propElement = $feed->newElement('cmis:' . $property['type']);  
473 - $propElement->appendChild($feed->newAttr('cmis:name', $propertyName));  
474 - $feed->newField('cmis:value', CMISUtil::boolToString($property['value']), $propElement);  
475 - $propertiesElement->appendChild($propElement);  
476 - } 465 +
  466 + $link = $feed->newElement('link');
  467 + $link->appendChild($feed->newAttr('rel', 'self'));
  468 + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/checkedout'));
  469 + $feed->appendChild($link);
  470 +
  471 + $link = $feed->newElement('link');
  472 + $link->appendChild($feed->newAttr('rel','first'));
  473 + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/checkedout/pageNo=1&pageSize=0'));
  474 + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=feed'));
  475 + $feed->appendChild($link);
  476 +
  477 + $link = $feed->newElement('link');
  478 + $link->appendChild($feed->newAttr('rel','last'));
  479 + // TODO set page number correctly - to be done when we support paging the the API
  480 + $link->appendChild($feed->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/checkedout/pageNo=1&pageSize=0'));
  481 + $link->appendChild($feed->newAttr('type', 'application/atom+xml;type=feed'));
  482 + $feed->appendChild($link);
477 483
478 - $objectElement->appendChild($propertiesElement);  
479 - $entry->appendChild($objectElement); 484 + foreach($checkedout as $cmisEntry)
  485 + {
  486 + KT_cmis_atom_service_helper::createObjectEntry($feed, $cmisEntry, $folderName);
  487 +
  488 +// // after each entry, add app:edited tag
  489 +// $feed->newField('app:edited', KT_cmis_atom_service_helper::formatDatestamp(), $feed);
480 } 490 }
481 491
482 - $entry = null;  
483 - $feed->newField('cmis:hasMoreItems', 'false', $entry, true); 492 + $feed->newField('cmis:hasMoreItems', 'false', $feed);
  493 +
  494 +// $entry = null;
  495 +// $feed->newField('cmis:hasMoreItems', 'false', $entry, true);
484 496
485 //Expose the responseFeed 497 //Expose the responseFeed
486 $this->responseFeed = $feed; 498 $this->responseFeed = $feed;
@@ -495,8 +507,8 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { @@ -495,8 +507,8 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service {
495 $repositories = $RepositoryService->getRepositories(); 507 $repositories = $RepositoryService->getRepositories();
496 $repositoryId = $repositories[0]['repositoryId']; 508 $repositoryId = $repositories[0]['repositoryId'];
497 509
498 - $cmisObjectProperties = KT_cmis_atom_service_helper::getCmisProperties($this->parsedXMLContent['@children']['cmis:object']);  
499 - 510 + $cmisObjectProperties = KT_cmis_atom_service_helper::getCmisProperties($this->parsedXMLContent['@children']);
  511 +
500 // check for existing object id as property of submitted object data 512 // check for existing object id as property of submitted object data
501 if (empty($cmisObjectProperties['ObjectId'])) 513 if (empty($cmisObjectProperties['ObjectId']))
502 { 514 {
@@ -518,51 +530,9 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service { @@ -518,51 +530,9 @@ class KT_cmis_atom_service_checkedout extends KT_cmis_atom_service {
518 530
519 $this->setStatus(self::STATUS_CREATED); 531 $this->setStatus(self::STATUS_CREATED);
520 $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $cmisObjectProperties['ObjectId'], 'POST'); 532 $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $cmisObjectProperties['ObjectId'], 'POST');
521 -// $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $newObjectId, 'POST');  
522 533
523 //Expose the responseFeed 534 //Expose the responseFeed
524 $this->responseFeed = $feed; 535 $this->responseFeed = $feed;
525 -  
526 -// $checkedout = $NavigationService->getCheckedoutDocs($repositoryId);  
527 -//  
528 -// //Create a new response feed  
529 -// $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI);  
530 -//  
531 -// $feed->newField('title', 'Checked out Documents', $feed);  
532 -//  
533 -// // TODO dynamic?  
534 -// $feedElement = $feed->newField('author');  
535 -// $element = $feed->newField('name', 'admin', $feedElement);  
536 -// $feed->appendChild($feedElement);  
537 -//  
538 -// $feed->appendChild($feed->newElement('id', 'urn:uuid:checkedout'));  
539 -//  
540 -// // TODO get actual most recent update time, only use current if no other available  
541 -// $feed->appendChild($feed->newElement('updated', KT_cmis_atom_service_helper::formatDatestamp()));  
542 -//  
543 -// foreach($checkedout as $document)  
544 -// {  
545 -// $entry = $feed->newEntry();  
546 -// $objectElement = $feed->newElement('cmis:object');  
547 -// $propertiesElement = $feed->newElement('cmis:properties');  
548 -//  
549 -// foreach($cmisEntry['properties'] as $propertyName => $property)  
550 -// {  
551 -// $propElement = $feed->newElement('cmis:' . $property['type']);  
552 -// $propElement->appendChild($feed->newAttr('cmis:name', $propertyName));  
553 -// $feed->newField('cmis:value', CMISUtil::boolToString($property['value']), $propElement);  
554 -// $propertiesElement->appendChild($propElement);  
555 -// }  
556 -//  
557 -// $objectElement->appendChild($propertiesElement);  
558 -// $entry->appendChild($objectElement);  
559 -// }  
560 -//  
561 -// $entry = null;  
562 -// $feed->newField('cmis:hasMoreItems', 'false', $entry, true);  
563 -//  
564 -// //Expose the responseFeed  
565 -// $this->responseFeed = $feed;  
566 } 536 }
567 537
568 } 538 }
@@ -675,4 +645,64 @@ class KT_cmis_atom_service_document extends KT_cmis_atom_service { @@ -675,4 +645,64 @@ class KT_cmis_atom_service_document extends KT_cmis_atom_service {
675 645
676 } 646 }
677 647
  648 +class KT_cmis_atom_service_pwc extends KT_cmis_atom_service {
  649 +
  650 + /**
  651 + * Deals with GET actions for Private Working Copies.
  652 + * This includes individual Private Working Copy retrieval
  653 + */
  654 + public function GET_action()
  655 + {
  656 + $RepositoryService = new RepositoryService();
  657 + $ObjectService = new ObjectService(KT_cmis_atom_service_helper::getKt());
  658 +
  659 + $repositories = $RepositoryService->getRepositories();
  660 + $repositoryId = $repositories[0]['repositoryId'];
  661 +
  662 + // determine whether we want the Private Working Copy entry feed or the actual physical Private Working Copy content.
  663 + // this depends on $this->params[1]
  664 + if (!empty($this->params[1]))
  665 + {
  666 + $this->getContentStream($ObjectService, $repositoryId);
  667 + return null;
  668 + }
  669 +
  670 + $feed = KT_cmis_atom_service_helper::getObjectFeed($this, $ObjectService, $repositoryId, $this->params[0]);
  671 +
  672 + //Expose the responseFeed
  673 + $this->responseFeed = $feed;
  674 + }
  675 +
  676 + /**
  677 + * Deals with DELETE actions for Private Working Copies.
  678 + * This includes deletion of a specific version of a document (latest version) via deleteObject
  679 + * as well as deleteAllVersions
  680 + *
  681 + * @return 204 on success, 500 on error
  682 + */
  683 + public function DELETE_action()
  684 + {
  685 + // call the cancel checkout function
  686 + $RepositoryService = new RepositoryService();
  687 + $VersioningService = new VersioningService(KT_cmis_atom_service_helper::getKt());
  688 +
  689 + $repositories = $RepositoryService->getRepositories();
  690 + $repositoryId = $repositories[0]['repositoryId'];
  691 +
  692 + $response = $VersioningService->cancelCheckout($repositoryId, $this->params[0]);
  693 +
  694 + if (PEAR::isError($response))
  695 + {
  696 + $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $response->getMessage());
  697 + //Expose the responseFeed
  698 + $this->responseFeed = $feed;
  699 + return null;
  700 + }
  701 +
  702 + $this->setStatus(self::STATUS_NO_CONTENT);
  703 + $this->responseFeed = null;
  704 + }
  705 +
  706 +}
  707 +
678 ?> 708 ?>
679 \ No newline at end of file 709 \ No newline at end of file
webservice/atompub/cmis/KT_cmis_atom_service_helper.inc.php
@@ -75,7 +75,7 @@ class KT_cmis_atom_service_helper { @@ -75,7 +75,7 @@ class KT_cmis_atom_service_helper {
75 { 75 {
76 $field = $response->newElement('content'); 76 $field = $response->newElement('content');
77 $field->appendChild($response->newAttr('type', $cmisEntry['properties']['ContentStreamMimeType']['value'])); 77 $field->appendChild($response->newAttr('type', $cmisEntry['properties']['ContentStreamMimeType']['value']));
78 - $field->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type 78 + $field->appendChild($response->newAttr('src', CMIS_APP_BASE_URI . $workspace . '/' . $type
79 . '/' . $cmisEntry['properties']['ObjectId']['value'] 79 . '/' . $cmisEntry['properties']['ObjectId']['value']
80 . '/' . $cmisEntry['properties']['ContentStreamFilename']['value'])); 80 . '/' . $cmisEntry['properties']['ContentStreamFilename']['value']));
81 $entry->appendChild($field); 81 $entry->appendChild($field);
@@ -190,14 +190,24 @@ class KT_cmis_atom_service_helper { @@ -190,14 +190,24 @@ class KT_cmis_atom_service_helper {
190 } 190 }
191 191
192 // if the document is checked out and this is NOT the PWC, this link MUST be present 192 // if the document is checked out and this is NOT the PWC, this link MUST be present
193 -// if (!empty($cmisEntry['properties']['ContentStreamLength']['value']))  
194 -// {  
195 -// $link = $response->newElement('link');  
196 -// $link->appendChild($response->newAttr('rel', 'stream'));  
197 -// $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type . '/' . $cmisEntry['properties']['ParentId']['value']));  
198 -// $entry->appendChild($link);  
199 -// }  
200 -// 193 + // NOTE at the moment the document and the PWC are the same object, so we always show it for a checked out document
  194 + // TODO separated code for PWC and actual document object
  195 + if (!empty($cmisEntry['properties']['VersionSeriesCheckedOutId']['value']))
  196 + {
  197 + $link = $response->newElement('link');
  198 + $link->appendChild($response->newAttr('rel', 'pwc'));
  199 + $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type
  200 + . '/' . $cmisEntry['properties']['ObjectId']['value']
  201 + . '/' . $cmisEntry['properties']['ContentStreamFilename']['value']));
  202 + $entry->appendChild($link);
  203 + $link = $response->newElement('link');
  204 + $link->appendChild($response->newAttr('rel', 'source'));
  205 + $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type
  206 + . '/' . $cmisEntry['properties']['ObjectId']['value']
  207 + . '/' . $cmisEntry['properties']['ContentStreamFilename']['value']));
  208 + $entry->appendChild($link);
  209 + }
  210 +
201 // $link = $response->newElement('link'); 211 // $link = $response->newElement('link');
202 // $link->appendChild($response->newAttr('rel', 'stream')); 212 // $link->appendChild($response->newAttr('rel', 'stream'));
203 // $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type 213 // $link->appendChild($response->newAttr('href', CMIS_APP_BASE_URI . $workspace . '/' . $type
@@ -253,9 +263,10 @@ class KT_cmis_atom_service_helper { @@ -253,9 +263,10 @@ class KT_cmis_atom_service_helper {
253 // after every entry, append a cmis:terminator tag 263 // after every entry, append a cmis:terminator tag
254 $entry->appendChild($response->newElement('cmis:terminator')); 264 $entry->appendChild($response->newElement('cmis:terminator'));
255 265
256 - if ($method == 'POST') { 266 + // TODO check determination of when to add app:edited tag
  267 +// if ($method == 'POST') {
257 $entry->appendChild($response->newElement('app:edited', self::formatDatestamp())); 268 $entry->appendChild($response->newElement('app:edited', self::formatDatestamp()));
258 - } 269 +// }
259 } 270 }
260 271
261 /** 272 /**
@@ -415,9 +426,17 @@ class KT_cmis_atom_service_helper { @@ -415,9 +426,17 @@ class KT_cmis_atom_service_helper {
415 { 426 {
416 $properties = array(); 427 $properties = array();
417 428
418 - foreach($xmlArray as $xmlElement) 429 + // find cmis:object tag
  430 + $baseCmisObject = KT_cmis_atom_service_helper::findTag('cmis:object', $xmlArray, null, false);
  431 + if(count($baseCmisObject) <= 0)
419 { 432 {
420 - foreach($xmlElement['@children'] as $key => $childElement) 433 + $entryObject = KT_cmis_atom_service_helper::findTag('entry', $xmlArray, null, false);
  434 + $baseCmisObject = KT_cmis_atom_service_helper::findTag('cmis:object', $entryObject['@children'], null, true);
  435 + }
  436 +
  437 + if(count($baseCmisObject)>0)
  438 + {
  439 + foreach($baseCmisObject['@children'] as $key => $childElement)
421 { 440 {
422 if ($key == 'cmis:properties') 441 if ($key == 'cmis:properties')
423 { 442 {
@@ -466,6 +485,34 @@ class KT_cmis_atom_service_helper { @@ -466,6 +485,34 @@ class KT_cmis_atom_service_helper {
466 if (is_null($time)) $time = time(); 485 if (is_null($time)) $time = time();
467 return date('Y-m-d H:i:s', $time); 486 return date('Y-m-d H:i:s', $time);
468 } 487 }
  488 +
  489 + //TODO: Add key information to be able to find the same tag in the original struct (MarkH)
  490 + static public function findTag($tagName=NULL,$xml=array(),$tagArray=NULL,$deep=false){
  491 + $tagArray=is_array($tagArray)?$tagArray:array();
  492 + foreach($xml as $xmlTag=>$content){
  493 + if($xmlTag===$tagName){
  494 + $tagArray[]=$content;
  495 + }
  496 + if($deep){
  497 + foreach($content as $contentTags){
  498 + if(is_array($contentTags['@children'])) {
  499 + if(count($contentTags['@children'])>0) $tagArray=self::findTag($tagName,$contentTags['@children'],$tagArray);
  500 + }
  501 + }
  502 + }
  503 + }
  504 + //TODO: this is very ugly. Change it. (MarkH)
  505 + return self::rebaseArray($tagArray);
  506 + }
  507 +
  508 + static public function rebaseArray($arr=array()){
  509 + //Force Array
  510 + $arr=is_array($arr)?$arr:array();
  511 +
  512 + //Rebase recursively
  513 + if(count($arr)===1)$arr=self::rebaseArray($arr[0]);
  514 + return $arr;
  515 + }
469 516
470 } 517 }
471 518
webservice/atompub/cmis/index.php
@@ -113,6 +113,7 @@ if ($workspace != &#39;servicedocument&#39;) @@ -113,6 +113,7 @@ if ($workspace != &#39;servicedocument&#39;)
113 { 113 {
114 $APP->registerService('dms', 'type', 'KT_cmis_atom_service_type', 'Object Type Entry', null, 'type'); 114 $APP->registerService('dms', 'type', 'KT_cmis_atom_service_type', 'Object Type Entry', null, 'type');
115 $APP->registerService('dms', 'document', 'KT_cmis_atom_service_document', 'Document Entry', null, 'document'); 115 $APP->registerService('dms', 'document', 'KT_cmis_atom_service_document', 'Document Entry', null, 'document');
  116 + $APP->registerService('dms', 'pwc', 'KT_cmis_atom_service_pwc', 'Private Working Copy', null, 'pwc');
116 } 117 }
117 118
118 //Execute the current url/header request 119 //Execute the current url/header request
webservice/classes/atompub/cmis/NavigationService.inc.php
@@ -109,9 +109,9 @@ class NavigationService extends KTNavigationService { @@ -109,9 +109,9 @@ class NavigationService extends KTNavigationService {
109 * @param int $skipCount 109 * @param int $skipCount
110 * @return array $checkedout The collection of checked out documents 110 * @return array $checkedout The collection of checked out documents
111 */ 111 */
112 - function getCheckedoutDocs($repositoryId, $folderId = null, $filter = '', $maxItems = 0, $skipCount = 0) 112 + function getCheckedOutDocs($repositoryId, $folderId = null, $filter = '', $maxItems = 0, $skipCount = 0)
113 { 113 {
114 - $checkedout = parent::getObjectParents($repositoryId, $folderId, $filter, $maxItems, $skipCount); 114 + $result = parent::getCheckedOutDocs($repositoryId, $folderId, $filter, $maxItems, $skipCount);
115 115
116 if ($result['status_code'] == 0) 116 if ($result['status_code'] == 0)
117 { 117 {
webservice/classes/atompub/cmis/VersioningService.inc.php
@@ -48,6 +48,30 @@ class VersioningService extends KTVersioningService { @@ -48,6 +48,30 @@ class VersioningService extends KTVersioningService {
48 return new PEAR_Error($result['message']); 48 return new PEAR_Error($result['message']);
49 } 49 }
50 } 50 }
  51 +
  52 + /**
  53 + * Reverses the effect of a checkout: I.E. deletes the PWC (Private Working Copy) and re-sets the status of the document to "not checked out"
  54 + *
  55 + * @param string $repositoryId
  56 + * @param string $documentId
  57 + * @param string $changeToken [optional]
  58 + */
  59 + // TODO exceptions:
  60 + // • ConstraintViolationException: The Repository SHALL throw this exception if ANY of the following conditions are met:
  61 + // o The Document’s Object-Type definition’s versionable attribute is FALSE.
  62 + // • updateConflictException
  63 + // • versioningException
  64 + public function cancelCheckOut($repositoryId, $documentId, $changeToken = '')
  65 + {
  66 + $result = parent::cancelCheckOut($repositoryId, $documentId, $changeToken);
  67 +
  68 + if ($result['status_code'] == 0) {
  69 + return $result['results'];
  70 + }
  71 + else {
  72 + return new PEAR_Error($result['message']);
  73 + }
  74 + }
51 75
52 } 76 }
53 77