Commit bf8f7fb723625a3f15459805bbe8568fe80921c6
1 parent
fb7f6cc4
CMIS: Added support for return of failed objects when calling deleteTree
Story ID: 966932. In order to be able to delete content using a CMIS client, as a user, I would like KnowledgeTree's CMIS interface to support delete Committed by: Paul Barrett
Showing
5 changed files
with
345 additions
and
3 deletions
ktapi/KTAPIFolder.inc.php
| @@ -798,6 +798,294 @@ class KTAPI_Folder extends KTAPI_FolderItem | @@ -798,6 +798,294 @@ class KTAPI_Folder extends KTAPI_FolderItem | ||
| 798 | 798 | ||
| 799 | return $contents; | 799 | return $contents; |
| 800 | } | 800 | } |
| 801 | + | ||
| 802 | + /** | ||
| 803 | + * Get's a folder listing, recursing to the maximum depth. | ||
| 804 | + * Derived from the get_listing function. | ||
| 805 | + * | ||
| 806 | + * <code> | ||
| 807 | + * $root = $this->ktapi->get_root_folder(); | ||
| 808 | + * $listing = $root->get_full_listing(); | ||
| 809 | + * foreach($listing as $val) { | ||
| 810 | + * if($val['item_type'] == 'F') { | ||
| 811 | + * // It's a folder | ||
| 812 | + * echo $val['title']; | ||
| 813 | + * } | ||
| 814 | + * } | ||
| 815 | + * </code> | ||
| 816 | + * | ||
| 817 | + * @author KnowledgeTree Team | ||
| 818 | + * @access public | ||
| 819 | + * @param string $what | ||
| 820 | + * @return array | ||
| 821 | + */ | ||
| 822 | + function get_full_listing($what='DFS') | ||
| 823 | + { | ||
| 824 | + $what = strtoupper($what); | ||
| 825 | + $read_permission = &KTPermission::getByName(KTAPI_PERMISSION_READ); | ||
| 826 | + $folder_permission = &KTPermission::getByName(KTAPI_PERMISSION_VIEW_FOLDER); | ||
| 827 | + | ||
| 828 | + $config = KTConfig::getSingleton(); | ||
| 829 | + | ||
| 830 | + $wsversion = $config->get('webservice/version', LATEST_WEBSERVICE_VERSION); | ||
| 831 | + | ||
| 832 | + $user = $this->ktapi->get_user(); | ||
| 833 | + | ||
| 834 | + $contents = array(); | ||
| 835 | + | ||
| 836 | + if (strpos($what,'F') !== false) | ||
| 837 | + { | ||
| 838 | + | ||
| 839 | + $folder_children = Folder::getList(array('parent_id = ?', $this->folderid)); | ||
| 840 | + | ||
| 841 | + foreach ($folder_children as $folder) | ||
| 842 | + { | ||
| 843 | + if(KTPermissionUtil::userHasPermissionOnItem($user, $folder_permission, $folder) || | ||
| 844 | + KTPermissionUtil::userHasPermissionOnItem($user, $read_permission, $folder)) | ||
| 845 | + { | ||
| 846 | + $sub_folder = &$this->ktapi->get_folder_by_id($folder->getId()); | ||
| 847 | + if (!PEAR::isError($sub_folder)) | ||
| 848 | + { | ||
| 849 | + $items = $sub_folder->get_full_listing($what); | ||
| 850 | + } | ||
| 851 | + else | ||
| 852 | + { | ||
| 853 | + $items = array(); | ||
| 854 | + } | ||
| 855 | + | ||
| 856 | + $creator = $this->_resolve_user($folder->getCreatorID()); | ||
| 857 | + | ||
| 858 | + | ||
| 859 | + if ($wsversion >= 2) | ||
| 860 | + { | ||
| 861 | + $array = array( | ||
| 862 | + 'id' => (int) $folder->getId(), | ||
| 863 | + 'item_type' => 'F', | ||
| 864 | + | ||
| 865 | + 'custom_document_no'=>'n/a', | ||
| 866 | + 'oem_document_no'=>'n/a', | ||
| 867 | + | ||
| 868 | + 'title' => $folder->getName(), | ||
| 869 | + 'document_type' => 'n/a', | ||
| 870 | + 'filename' => $folder->getName(), | ||
| 871 | + 'filesize' => 'n/a', | ||
| 872 | + | ||
| 873 | + 'created_by' => is_null($creator)?'n/a':$creator->getName(), | ||
| 874 | + 'created_date' => 'n/a', | ||
| 875 | + | ||
| 876 | + 'checked_out_by' => 'n/a', | ||
| 877 | + 'checked_out_date' => 'n/a', | ||
| 878 | + | ||
| 879 | + 'modified_by' => 'n/a', | ||
| 880 | + 'modified_date' => 'n/a', | ||
| 881 | + | ||
| 882 | + 'owned_by' => 'n/a', | ||
| 883 | + | ||
| 884 | + 'version' => 'n/a', | ||
| 885 | + | ||
| 886 | + 'is_immutable'=> 'n/a', | ||
| 887 | + 'permissions' => KTAPI_Folder::get_permission_string($folder), | ||
| 888 | + | ||
| 889 | + 'workflow'=>'n/a', | ||
| 890 | + 'workflow_state'=>'n/a', | ||
| 891 | + | ||
| 892 | + 'mime_type' => 'folder', | ||
| 893 | + 'mime_icon_path' => 'folder', | ||
| 894 | + 'mime_display' => 'Folder', | ||
| 895 | + | ||
| 896 | + 'storage_path' => 'n/a', | ||
| 897 | + ); | ||
| 898 | + | ||
| 899 | + if($wsversion>=3) | ||
| 900 | + { | ||
| 901 | + $array['linked_folder_id'] = $folder->getLinkedFolderId(); | ||
| 902 | + if($folder->isSymbolicLink()) { | ||
| 903 | + $array['item_type'] = "S"; | ||
| 904 | + } | ||
| 905 | + } | ||
| 906 | + | ||
| 907 | + $array['items']=$items; | ||
| 908 | + if($wsversion<3 || (strpos($what,'F') !== false && !$folder->isSymbolicLink()) || | ||
| 909 | + ($folder->isSymbolicLink() && strpos($what,'S') !== false)) { | ||
| 910 | + $contents[] = $array; | ||
| 911 | + } | ||
| 912 | + } | ||
| 913 | + else | ||
| 914 | + { | ||
| 915 | + $contents[] = array( | ||
| 916 | + 'id' => (int) $folder->getId(), | ||
| 917 | + 'item_type'=>'F', | ||
| 918 | + 'title'=>$folder->getName(), | ||
| 919 | + 'creator'=>is_null($creator)?'n/a':$creator->getName(), | ||
| 920 | + 'checkedoutby'=>'n/a', | ||
| 921 | + 'modifiedby'=>'n/a', | ||
| 922 | + 'filename'=>$folder->getName(), | ||
| 923 | + 'size'=>'n/a', | ||
| 924 | + 'major_version'=>'n/a', | ||
| 925 | + 'minor_version'=>'n/a', | ||
| 926 | + 'storage_path'=>'n/a', | ||
| 927 | + 'mime_type'=>'folder', | ||
| 928 | + 'mime_icon_path'=>'folder', | ||
| 929 | + 'mime_display'=>'Folder', | ||
| 930 | + 'items'=>$items, | ||
| 931 | + 'workflow'=>'n/a', | ||
| 932 | + 'workflow_state'=>'n/a' | ||
| 933 | + ); | ||
| 934 | + } | ||
| 935 | + | ||
| 936 | + } | ||
| 937 | + } | ||
| 938 | + | ||
| 939 | + } | ||
| 940 | + | ||
| 941 | + if (strpos($what,'D') !== false) | ||
| 942 | + { | ||
| 943 | + $document_children = Document::getList(array('folder_id = ? AND status_id = 1', $this->folderid)); | ||
| 944 | + | ||
| 945 | + // I hate that KT doesn't cache things nicely... | ||
| 946 | + $mime_cache = array(); | ||
| 947 | + | ||
| 948 | + foreach ($document_children as $document) | ||
| 949 | + { | ||
| 950 | + if (KTPermissionUtil::userHasPermissionOnItem($user, $read_permission, $document)) | ||
| 951 | + { | ||
| 952 | + $created_by=$this->_resolve_user($document->getCreatorID()); | ||
| 953 | + $created_date = $document->getCreatedDateTime(); | ||
| 954 | + if (empty($created_date)) $created_date = 'n/a'; | ||
| 955 | + | ||
| 956 | + $checked_out_by=$this->_resolve_user($document->getCheckedOutUserID()); | ||
| 957 | + $checked_out_date = $document->getCheckedOutDate(); | ||
| 958 | + if (empty($checked_out_date)) $checked_out_date = 'n/a'; | ||
| 959 | + | ||
| 960 | + $modified_by=$this->_resolve_user($document->getCreatorID()); | ||
| 961 | + $modified_date = $document->getLastModifiedDate(); | ||
| 962 | + if (empty($modified_date)) $modified_date = 'n/a'; | ||
| 963 | + | ||
| 964 | + $owned_by =$this->_resolve_user($document->getOwnerID()); | ||
| 965 | + | ||
| 966 | + $mimetypeid=$document->getMimeTypeID(); | ||
| 967 | + if (!array_key_exists($mimetypeid, $mime_cache)) | ||
| 968 | + { | ||
| 969 | + | ||
| 970 | + $type=KTMime::getMimeTypeName($mimetypeid); | ||
| 971 | + $icon=KTMime::getIconPath($mimetypeid); | ||
| 972 | + $display=KTMime::getFriendlyNameForString($type); | ||
| 973 | + $mime_cache[$mimetypeid] = array( | ||
| 974 | + 'type'=>$type, | ||
| 975 | + 'icon'=>$icon, | ||
| 976 | + 'display'=>$display | ||
| 977 | + | ||
| 978 | + ); | ||
| 979 | + } | ||
| 980 | + $mimeinfo=$mime_cache[$mimetypeid]; | ||
| 981 | + | ||
| 982 | + $workflow='n/a'; | ||
| 983 | + $state='n/a'; | ||
| 984 | + | ||
| 985 | + $wf = KTWorkflowUtil::getWorkflowForDocument($document); | ||
| 986 | + | ||
| 987 | + if (!is_null($wf) && !PEAR::isError($wf)) | ||
| 988 | + { | ||
| 989 | + $workflow=$wf->getHumanName(); | ||
| 990 | + | ||
| 991 | + $ws=KTWorkflowUtil::getWorkflowStateForDocument($document); | ||
| 992 | + if (!is_null($ws) && !PEAR::isError($ws)) | ||
| 993 | + { | ||
| 994 | + $state=$ws->getHumanName(); | ||
| 995 | + } | ||
| 996 | + } | ||
| 997 | + | ||
| 998 | + if ($wsversion >= 2) | ||
| 999 | + { | ||
| 1000 | + $docTypeId = $document->getDocumentTypeID(); | ||
| 1001 | + $documentType = DocumentType::get($docTypeId); | ||
| 1002 | + | ||
| 1003 | + $oemDocumentNo = $document->getOemNo(); | ||
| 1004 | + if (empty($oemDocumentNo)) $oemDocumentNo = 'n/a'; | ||
| 1005 | + | ||
| 1006 | + | ||
| 1007 | + $array = array( | ||
| 1008 | + 'id' => (int) $document->getId(), | ||
| 1009 | + 'item_type' => 'D', | ||
| 1010 | + | ||
| 1011 | + 'custom_document_no'=>'n/a', | ||
| 1012 | + 'oem_document_no'=>$oemDocumentNo, | ||
| 1013 | + | ||
| 1014 | + 'title' => $document->getName(), | ||
| 1015 | + 'document_type'=>$documentType->getName(), | ||
| 1016 | + 'filename' => $document->getFileName(), | ||
| 1017 | + 'filesize' => $document->getFileSize(), | ||
| 1018 | + | ||
| 1019 | + 'created_by' => is_null($created_by)?'n/a':$created_by->getName(), | ||
| 1020 | + 'created_date' => $created_date, | ||
| 1021 | + | ||
| 1022 | + 'checked_out_by' => is_null($checked_out_by)?'n/a':$checked_out_by->getName(), | ||
| 1023 | + 'checked_out_date' => $checked_out_date, | ||
| 1024 | + | ||
| 1025 | + 'modified_by' => is_null($modified_by)?'n/a':$modified_by->getName(), | ||
| 1026 | + 'modified_date' => $modified_date, | ||
| 1027 | + | ||
| 1028 | + 'owned_by' => is_null($owned_by)?'n/a':$owned_by->getName(), | ||
| 1029 | + | ||
| 1030 | + 'version' => $document->getMajorVersionNumber() . '.' . $document->getMinorVersionNumber(), | ||
| 1031 | + 'content_id' => $document->getContentVersionId(), | ||
| 1032 | + | ||
| 1033 | + 'is_immutable'=> $document->getImmutable()?'true':'false', | ||
| 1034 | + 'permissions' => KTAPI_Document::get_permission_string($document), | ||
| 1035 | + | ||
| 1036 | + 'workflow'=> $workflow, | ||
| 1037 | + 'workflow_state'=> $state, | ||
| 1038 | + | ||
| 1039 | + 'mime_type' => $mime_cache[$mimetypeid]['type'], | ||
| 1040 | + 'mime_icon_path' => $mime_cache[$mimetypeid]['icon'], | ||
| 1041 | + 'mime_display' => $mime_cache[$mimetypeid]['display'], | ||
| 1042 | + | ||
| 1043 | + 'storage_path' => $document->getStoragePath(), | ||
| 1044 | + ); | ||
| 1045 | + if($wsversion>=3){ | ||
| 1046 | + $document->switchToRealCore(); | ||
| 1047 | + $array['linked_document_id'] = $document->getLinkedDocumentId(); | ||
| 1048 | + $document->switchToLinkedCore(); | ||
| 1049 | + if($document->isSymbolicLink()){ | ||
| 1050 | + $array['item_type'] = "S"; | ||
| 1051 | + } | ||
| 1052 | + } | ||
| 1053 | + | ||
| 1054 | + $array['items']=array(); | ||
| 1055 | + | ||
| 1056 | + | ||
| 1057 | + if($wsversion<3 || (strpos($what,'D') !== false && !$document->isSymbolicLink()) || ($document->isSymbolicLink() && strpos($what,'S') !== false)){ | ||
| 1058 | + $contents[] = $array; | ||
| 1059 | + } | ||
| 1060 | + } | ||
| 1061 | + else | ||
| 1062 | + { | ||
| 1063 | + $contents[] = array( | ||
| 1064 | + 'id' => (int) $document->getId(), | ||
| 1065 | + 'item_type'=>'D', | ||
| 1066 | + 'title'=>$document->getName(), | ||
| 1067 | + 'creator'=>is_null($created_by)?'n/a':$created_by->getName(), | ||
| 1068 | + 'checkedoutby'=>is_null($checked_out_by)?'n/a':$checked_out_by->getName(), | ||
| 1069 | + 'modifiedby'=>is_null($modified_by)?'n/a':$modified_by->getName(), | ||
| 1070 | + 'filename'=>$document->getFileName(), | ||
| 1071 | + 'size'=>$document->getFileSize(), | ||
| 1072 | + 'major_version'=>$document->getMajorVersionNumber(), | ||
| 1073 | + 'minor_version'=>$document->getMinorVersionNumber(), | ||
| 1074 | + 'storage_path'=>$document->getStoragePath(), | ||
| 1075 | + 'mime_type'=>$mime_cache[$mimetypeid]['type'], | ||
| 1076 | + 'mime_icon_path'=>$mime_cache[$mimetypeid]['icon'], | ||
| 1077 | + 'mime_display'=>$mime_cache[$mimetypeid]['display'], | ||
| 1078 | + 'items'=>array(), | ||
| 1079 | + 'workflow'=>$workflow, | ||
| 1080 | + 'workflow_state'=>$state | ||
| 1081 | + ); | ||
| 1082 | + } | ||
| 1083 | + } | ||
| 1084 | + } | ||
| 1085 | + } | ||
| 1086 | + | ||
| 1087 | + return $contents; | ||
| 1088 | + } | ||
| 801 | 1089 | ||
| 802 | /** | 1090 | /** |
| 803 | * This adds a shortcut to an existing document to the current folder | 1091 | * This adds a shortcut to an existing document to the current folder |
lib/api/ktcmis/ktcmis.inc.php
| @@ -633,6 +633,15 @@ class KTObjectService extends KTCMISBase { | @@ -633,6 +633,15 @@ class KTObjectService extends KTCMISBase { | ||
| 633 | ); | 633 | ); |
| 634 | } | 634 | } |
| 635 | 635 | ||
| 636 | + // check whether there is a list of items which did not delete | ||
| 637 | + if (count($result) > 0) | ||
| 638 | + { | ||
| 639 | + return array( | ||
| 640 | + "status_code" => 1, | ||
| 641 | + "message" => $result | ||
| 642 | + ); | ||
| 643 | + } | ||
| 644 | + | ||
| 636 | return array( | 645 | return array( |
| 637 | 'status_code' => 0, | 646 | 'status_code' => 0, |
| 638 | 'results' => $objectId | 647 | 'results' => $objectId |
lib/api/ktcmis/services/CMISObjectService.inc.php
| @@ -535,8 +535,25 @@ class CMISObjectService { | @@ -535,8 +535,25 @@ class CMISObjectService { | ||
| 535 | // TODO list of objects which failed in $failedToDelete array; | 535 | // TODO list of objects which failed in $failedToDelete array; |
| 536 | // since we do not delete the folder or any contents if anything cannot be deleted, this will contain the entire tree listing | 536 | // since we do not delete the folder or any contents if anything cannot be deleted, this will contain the entire tree listing |
| 537 | // NOTE once we do this we will need to deal with it externally as well, since we can no longer just catch an exception. | 537 | // NOTE once we do this we will need to deal with it externally as well, since we can no longer just catch an exception. |
| 538 | - if ($result['status_code'] == 1) { | ||
| 539 | - throw new RuntimeException('There was an error deleting the object: ' . $result['message']); | 538 | + if ($result['status_code'] == 1) |
| 539 | + { | ||
| 540 | + // TODO consider sending back full properties on each object? | ||
| 541 | + // Not sure yet what this output may be used for by a client, and the current specification (0.61c) says: | ||
| 542 | + // "A list of identifiers of objects in the folder tree that were not deleted", so let's leave it returning just ids for now. | ||
| 543 | + $failedToDelete[] = CMISUtil::encodeObjectId('Folder', $objectId); | ||
| 544 | + $folderContents = $object->get_full_listing(); | ||
| 545 | + foreach($folderContents as $folderObject) | ||
| 546 | + { | ||
| 547 | + if ($folderObject['item_type'] == 'F') $type = 'Folder'; | ||
| 548 | + else if ($folderObject['item_type'] == 'D') $type = 'Document'; | ||
| 549 | + // TODO deal with non-folder and non-document content | ||
| 550 | + else continue; | ||
| 551 | + | ||
| 552 | + // TODO find out whether this is meant to be a hierarchical list or simply a list. | ||
| 553 | + // for now we are just returning the list in non-hierarchical form | ||
| 554 | + // (seeing as we don't really know how CMIS AtomPub is planning to deal with hierarchies at this time.) | ||
| 555 | + $failedToDelete[] = CMISUtil::encodeObjectId($type, $folderObject['id']); | ||
| 556 | + } | ||
| 540 | } | 557 | } |
| 541 | 558 | ||
| 542 | return $failedToDelete; | 559 | return $failedToDelete; |
webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php
| @@ -177,9 +177,34 @@ class KT_cmis_atom_service_folder extends KT_atom_service { | @@ -177,9 +177,34 @@ class KT_cmis_atom_service_folder extends KT_atom_service { | ||
| 177 | $this->responseFeed = $feed; | 177 | $this->responseFeed = $feed; |
| 178 | return null; | 178 | return null; |
| 179 | } | 179 | } |
| 180 | + // list of failed objects? | ||
| 181 | + if (is_array($result)) | ||
| 182 | + { | ||
| 183 | + $this->setStatus(self::STATUS_SERVER_ERROR); | ||
| 184 | + | ||
| 185 | + $feed = new KT_cmis_atom_responseFeed_GET(CMIS_APP_BASE_URI); | ||
| 186 | + $feed->newField('title', 'Error: Failed to delete all objects in tree: ' . self::STATUS_SERVER_ERROR, $feed); | ||
| 187 | + | ||
| 188 | + foreach($result as $failed) | ||
| 189 | + { | ||
| 190 | + $entry = $feed->newEntry(); | ||
| 191 | + $objectElement = $feed->newElement('cmis:object'); | ||
| 192 | + $propertiesElement = $feed->newElement('cmis:properties'); | ||
| 193 | + $propElement = $feed->newElement('cmis:propertyId'); | ||
| 194 | + $propElement->appendChild($feed->newAttr('cmis:name', 'ObjectId')); | ||
| 195 | + $feed->newField('cmis:value', $failed, $propElement); | ||
| 196 | + $propertiesElement->appendChild($propElement); | ||
| 197 | + $objectElement->appendChild($propertiesElement); | ||
| 198 | + $entry->appendChild($objectElement); | ||
| 199 | + $entry->appendChild($feed->newElement('cmis:terminator')); | ||
| 200 | + } | ||
| 201 | + | ||
| 202 | + $this->responseFeed = $feed; | ||
| 203 | + return null; | ||
| 204 | + } | ||
| 180 | 205 | ||
| 181 | // success | 206 | // success |
| 182 | - $this->setStatus(self::STATUS_NO_CONTENT); | 207 | + $this->setStatus(self::STATUS_NO_CONTENT); |
| 183 | } | 208 | } |
| 184 | 209 | ||
| 185 | /** | 210 | /** |
webservice/classes/atompub/cmis/ObjectService.inc.php
| @@ -119,6 +119,9 @@ class ObjectService extends KTObjectService { | @@ -119,6 +119,9 @@ class ObjectService extends KTObjectService { | ||
| 119 | if ($result['status_code'] == 0) { | 119 | if ($result['status_code'] == 0) { |
| 120 | return $result['results']; | 120 | return $result['results']; |
| 121 | } | 121 | } |
| 122 | + else if (is_array($result['message'])) { | ||
| 123 | + return $result['message']; | ||
| 124 | + } | ||
| 122 | else { | 125 | else { |
| 123 | return new PEAR_Error($result['message']); | 126 | return new PEAR_Error($result['message']); |
| 124 | } | 127 | } |