Commit 81077932f738f57878511eea6542b449e87a0709

Authored by Paul Barrett
1 parent 6c79e197

KTS-4186. Prevented checked out documents from being deleted or archived by bulk…

… action.  I also added the folder checking code from the bulk move action to check into subfolders and prevent these actions when any document in the selected folder tree does not permit them.  Additionally I added this folder checking to the copy function since it made sense that it belongs in all 4 of the delete/move/copy/archive actions to check down into the folder structure before proceeding.

Checked out documents can be deleted or archived by bulk action

Fixed

Committed By: Paul Barrett

Reviewed by: Megan Watson
lib/documentmanagement/documentutil.inc.php
@@ -1076,20 +1076,62 @@ $sourceDocument->getName(), @@ -1076,20 +1076,62 @@ $sourceDocument->getName(),
1076 Indexer::index($oDocument); 1076 Indexer::index($oDocument);
1077 } 1077 }
1078 1078
  1079 + function canBeCopied($oDocument, &$sError) {
  1080 + if ($oDocument->getIsCheckedOut()) {
  1081 + $sError = PEAR::raiseError(_kt('Document cannot be copied as it is checked out.'));
  1082 + return false;
  1083 + }
  1084 + if (!KTWorkflowUtil::actionEnabledForDocument($oDocument, 'ktcore.actions.document.copy')) {
  1085 + $sError = PEAR::raiseError(_kt('Document cannot be copied as it is restricted by the workflow.'));
  1086 + return false;
  1087 + }
  1088 + return true;
  1089 + }
1079 1090
1080 - function canBeMoved($oDocument) { 1091 + function canBeMoved($oDocument, &$sError) {
  1092 + if ($oDocument->getImmutable()) {
  1093 + $sError = PEAR::raiseError(_kt('Document cannot be moved as it is immutable.'));
  1094 + return false;
  1095 + }
1081 if ($oDocument->getIsCheckedOut()) { 1096 if ($oDocument->getIsCheckedOut()) {
  1097 + $sError = PEAR::raiseError(_kt('Document cannot be moved as it is checked out.'));
1082 return false; 1098 return false;
1083 } 1099 }
1084 if (!KTWorkflowUtil::actionEnabledForDocument($oDocument, 'ktcore.actions.document.move')) { 1100 if (!KTWorkflowUtil::actionEnabledForDocument($oDocument, 'ktcore.actions.document.move')) {
  1101 + $sError = PEAR::raiseError(_kt('Document cannot be moved as it is restricted by the workflow.'));
1085 return false; 1102 return false;
1086 } 1103 }
1087 - if ($oDocument->getImmutable()) { 1104 + return true;
  1105 + }
  1106 +
  1107 + function canBeDeleted($oDocument, &$sError) {
  1108 + if($oDocument->getImmutable())
  1109 + {
  1110 + $sError = PEAR::raiseError(_kt('Document cannot be deleted as it is immutable.'));
  1111 + return false;
  1112 + }
  1113 + if ($oDocument->getIsCheckedOut()) {
  1114 + $sError = PEAR::raiseError(_kt('Document cannot be deleted as it is checked out.'));
  1115 + return false;
  1116 + }
  1117 + if(!KTWorkflowUtil::actionEnabledForDocument($oDocument, 'ktcore.actions.document.delete')){
  1118 + $sError = PEAR::raiseError(_kt('Document cannot be deleted as it is restricted by the workflow.'));
1088 return false; 1119 return false;
1089 } 1120 }
1090 return true; 1121 return true;
1091 } 1122 }
1092 1123
  1124 + function canBeArchived($oDocument, &$sError) {
  1125 + if ($oDocument->getIsCheckedOut()) {
  1126 + $sError = PEAR::raiseError(_kt('Document cannot be archived as it is checked out.'));
  1127 + return false;
  1128 + }
  1129 + if(!KTWorkflowUtil::actionEnabledForDocument($oDocument, 'ktcore.actions.document.archive')){
  1130 + $sError = PEAR::raiseError(_kt('Document cannot be archived as it is restricted by the workflow.'));
  1131 + return false;
  1132 + }
  1133 + return true;
  1134 + }
1093 1135
1094 function copy($oDocument, $oDestinationFolder, $sReason = null, $sDestinationDocName = null) { 1136 function copy($oDocument, $oDestinationFolder, $sReason = null, $sDestinationDocName = null) {
1095 // 1. generate a new triad of content, metadata and core objects. 1137 // 1. generate a new triad of content, metadata and core objects.
plugins/ktcore/KTBulkActions.php
@@ -53,14 +53,64 @@ class KTBulkDeleteAction extends KTBulkAction { @@ -53,14 +53,64 @@ class KTBulkDeleteAction extends KTBulkAction {
53 53
54 function check_entity($oEntity) { 54 function check_entity($oEntity) {
55 if(is_a($oEntity, 'Document')) { 55 if(is_a($oEntity, 'Document')) {
56 - if($oEntity->getImmutable())  
57 - {  
58 - return PEAR::raiseError(_kt('Document cannot be deleted as it is immutable')); 56 + if(!KTDocumentUtil::canBeDeleted($oEntity, $sError)) {
  57 + if (PEAR::isError($sError))
  58 + {
  59 + return $sError;
  60 + }
  61 + return PEAR::raiseError(_kt('Document cannot be deleted'));
59 } 62 }
60 - if(!KTWorkflowUtil::actionEnabledForDocument($oEntity, 'ktcore.actions.document.delete')){  
61 - return PEAR::raiseError(_kt('Document cannot be deleted as it is restricted by the workflow.')); 63 + }
  64 +
  65 + if(is_a($oEntity, 'Folder')) {
  66 + $aDocuments = array();
  67 + $aChildFolders = array();
  68 +
  69 + $oFolder = $oEntity;
  70 +
  71 + // Get folder id
  72 + $sFolderId = $oFolder->getID();
  73 +
  74 + // Get documents in folder
  75 + $sDocuments = $oFolder->getDocumentIDs($sFolderId);
  76 + $aDocuments = (!empty($sDocuments)) ? explode(',', $sDocuments) : array();
  77 +
  78 + // Loop through documents and send to this function for checking
  79 + if(!empty($aDocuments)){
  80 + foreach($aDocuments as $sDocID){
  81 + $oDocument = Document::get($sDocID);
  82 + $res = $this->check_entity($oDocument);
  83 + if (PEAR::isError($res))
  84 + {
  85 + // NOTE: we may want to append the document reason to this
  86 + // in order for the user to have some idea WHY the folder cannot be deleted
  87 + return PEAR::raiseError(_kt('Folder cannot be deleted'));
  88 + }
  89 + }
  90 + }
  91 +
  92 + // If all documents at the current level may be deleted, we can continue
  93 + // Get any existing subfolders
  94 + $sWhereClause = "parent_folder_ids = '{$sFolderId}' OR
  95 + parent_folder_ids LIKE '{$sFolderId},%' OR
  96 + parent_folder_ids LIKE '%,{$sFolderId},%' OR
  97 + parent_folder_ids LIKE '%,{$sFolderId}'";
  98 + $aChildFolders = $this->oFolder->getList($sWhereClause);
  99 +
  100 + // Loop through subfolders and check each in the same way as the parent
  101 + if(!empty($aChildFolders)){
  102 + foreach($aChildFolders as $oChild){
  103 + $res = $this->check_entity($oChild);
  104 + if (PEAR::isError($res))
  105 + {
  106 + // NOTE: we may want to append the document reason to this
  107 + // in order for the user to have some idea WHY the folder cannot be deleted
  108 + return PEAR::raiseError(_kt('Folder cannot be deleted'));
  109 + }
  110 + }
62 } 111 }
63 } 112 }
  113 +
64 return parent::check_entity($oEntity); 114 return parent::check_entity($oEntity);
65 } 115 }
66 116
@@ -348,7 +398,11 @@ class KTBulkMoveAction extends KTBulkAction { @@ -348,7 +398,11 @@ class KTBulkMoveAction extends KTBulkAction {
348 function check_entity($oEntity) { 398 function check_entity($oEntity) {
349 399
350 if(is_a($oEntity, 'Document')) { 400 if(is_a($oEntity, 'Document')) {
351 - if(!KTDocumentUtil::canBeMoved($oEntity)) { 401 + if(!KTDocumentUtil::canBeMoved($oEntity, $sError)) {
  402 + if (PEAR::isError($sError))
  403 + {
  404 + return $sError;
  405 + }
352 return PEAR::raiseError(_kt('Document cannot be moved')); 406 return PEAR::raiseError(_kt('Document cannot be moved'));
353 } 407 }
354 } 408 }
@@ -373,6 +427,8 @@ class KTBulkMoveAction extends KTBulkAction { @@ -373,6 +427,8 @@ class KTBulkMoveAction extends KTBulkAction {
373 $res = $this->check_entity($oDocument); 427 $res = $this->check_entity($oDocument);
374 if (PEAR::isError($res)) 428 if (PEAR::isError($res))
375 { 429 {
  430 + // NOTE: we may want to append the document reason to this
  431 + // in order for the user to have some idea WHY the folder cannot be moved
376 return PEAR::raiseError(_kt('Folder cannot be moved')); 432 return PEAR::raiseError(_kt('Folder cannot be moved'));
377 } 433 }
378 } 434 }
@@ -392,6 +448,8 @@ class KTBulkMoveAction extends KTBulkAction { @@ -392,6 +448,8 @@ class KTBulkMoveAction extends KTBulkAction {
392 $res = $this->check_entity($oChild); 448 $res = $this->check_entity($oChild);
393 if (PEAR::isError($res)) 449 if (PEAR::isError($res))
394 { 450 {
  451 + // NOTE: we may want to append the document reason to this
  452 + // in order for the user to have some idea WHY the folder cannot be moved
395 return PEAR::raiseError(_kt('Folder cannot be moved')); 453 return PEAR::raiseError(_kt('Folder cannot be moved'));
396 } 454 }
397 } 455 }
@@ -579,10 +637,64 @@ class KTBulkCopyAction extends KTBulkAction { @@ -579,10 +637,64 @@ class KTBulkCopyAction extends KTBulkAction {
579 637
580 function check_entity($oEntity) { 638 function check_entity($oEntity) {
581 if(is_a($oEntity, 'Document')) { 639 if(is_a($oEntity, 'Document')) {
582 - if(!KTDocumentUtil::canBeMoved($oEntity)) { 640 + if(!KTDocumentUtil::canBeCopied($oEntity, $sError)) {
  641 + if (PEAR::isError($sError))
  642 + {
  643 + return $sError;
  644 + }
583 return PEAR::raiseError(_kt('Document cannot be copied')); 645 return PEAR::raiseError(_kt('Document cannot be copied'));
584 } 646 }
585 } 647 }
  648 +
  649 + if(is_a($oEntity, 'Folder')) {
  650 + $aDocuments = array();
  651 + $aChildFolders = array();
  652 +
  653 + $oFolder = $oEntity;
  654 +
  655 + // Get folder id
  656 + $sFolderId = $oFolder->getID();
  657 +
  658 + // Get documents in folder
  659 + $sDocuments = $oFolder->getDocumentIDs($sFolderId);
  660 + $aDocuments = (!empty($sDocuments)) ? explode(',', $sDocuments) : array();
  661 +
  662 + // Loop through documents and send to this function for checking
  663 + if(!empty($aDocuments)){
  664 + foreach($aDocuments as $sDocID){
  665 + $oDocument = Document::get($sDocID);
  666 + $res = $this->check_entity($oDocument);
  667 + if (PEAR::isError($res))
  668 + {
  669 + // NOTE: we may want to append the document reason to this
  670 + // in order for the user to have some idea WHY the folder cannot be copied
  671 + return PEAR::raiseError(_kt('Folder cannot be copied'));
  672 + }
  673 + }
  674 + }
  675 +
  676 + // If all documents at the current level may be copied, we can continue
  677 + // Get any existing subfolders
  678 + $sWhereClause = "parent_folder_ids = '{$sFolderId}' OR
  679 + parent_folder_ids LIKE '{$sFolderId},%' OR
  680 + parent_folder_ids LIKE '%,{$sFolderId},%' OR
  681 + parent_folder_ids LIKE '%,{$sFolderId}'";
  682 + $aChildFolders = $this->oFolder->getList($sWhereClause);
  683 +
  684 + // Loop through subfolders and check each in the same way as the parent
  685 + if(!empty($aChildFolders)){
  686 + foreach($aChildFolders as $oChild){
  687 + $res = $this->check_entity($oChild);
  688 + if (PEAR::isError($res))
  689 + {
  690 + // NOTE: we may want to append the document reason to this
  691 + // in order for the user to have some idea WHY the folder cannot be copied
  692 + return PEAR::raiseError(_kt('Folder cannot be copied'));
  693 + }
  694 + }
  695 + }
  696 + }
  697 +
586 return parent::check_entity($oEntity); 698 return parent::check_entity($oEntity);
587 } 699 }
588 700
@@ -709,17 +821,76 @@ class KTBulkArchiveAction extends KTBulkAction { @@ -709,17 +821,76 @@ class KTBulkArchiveAction extends KTBulkAction {
709 } 821 }
710 822
711 function check_entity($oEntity) { 823 function check_entity($oEntity) {
  824 + // NOTE: these checks don't have an equivalent in the delete and move functions.
  825 + // possibly they are no longer needed but I am leaving them here
  826 + // to avoid any potential problems I may not be aware of
712 if((!is_a($oEntity, 'Document')) && (!is_a($oEntity, 'Folder'))) { 827 if((!is_a($oEntity, 'Document')) && (!is_a($oEntity, 'Folder'))) {
713 - return PEAR::raiseError(_kt('Document cannot be archived')); 828 + return PEAR::raiseError(_kt('Document cannot be archived'));
714 } 829 }
  830 +
715 if($oEntity->isSymbolicLink()){ 831 if($oEntity->isSymbolicLink()){
716 return PEAR::raiseError(_kt("It is not possible to archive a shortcut. Please archive the target document or folder instead.")); 832 return PEAR::raiseError(_kt("It is not possible to archive a shortcut. Please archive the target document or folder instead."));
717 } 833 }
718 - if(is_a($oEntity, 'Document')){  
719 - if(!KTWorkflowUtil::actionEnabledForDocument($oEntity, 'ktcore.actions.document.archive')){  
720 - return PEAR::raiseError(_kt('Document cannot be archived as it is restricted by the workflow.')); 834 +
  835 + if(is_a($oEntity, 'Document')) {
  836 + if(!KTDocumentUtil::canBeArchived($oEntity, $sError)) {
  837 + if (PEAR::isError($sError))
  838 + {
  839 + return $sError;
  840 + }
  841 + return PEAR::raiseError(_kt('Document cannot be archived'));
  842 + }
  843 + }
  844 +
  845 + if(is_a($oEntity, 'Folder')) {
  846 + $aDocuments = array();
  847 + $aChildFolders = array();
  848 +
  849 + $oFolder = $oEntity;
  850 +
  851 + // Get folder id
  852 + $sFolderId = $oFolder->getID();
  853 +
  854 + // Get documents in folder
  855 + $sDocuments = $oFolder->getDocumentIDs($sFolderId);
  856 + $aDocuments = (!empty($sDocuments)) ? explode(',', $sDocuments) : array();
  857 +
  858 + // Loop through documents and send to this function for checking
  859 + if(!empty($aDocuments)){
  860 + foreach($aDocuments as $sDocID){
  861 + $oDocument = Document::get($sDocID);
  862 + $res = $this->check_entity($oDocument);
  863 + if (PEAR::isError($res))
  864 + {
  865 + // NOTE: we may want to append the document reason to this
  866 + // in order for the user to have some idea WHY the folder cannot be archived
  867 + return PEAR::raiseError(_kt('Folder cannot be archived'));
  868 + }
  869 + }
  870 + }
  871 +
  872 + // If all documents at the current level may be archived, we can continue
  873 + // Get any existing subfolders
  874 + $sWhereClause = "parent_folder_ids = '{$sFolderId}' OR
  875 + parent_folder_ids LIKE '{$sFolderId},%' OR
  876 + parent_folder_ids LIKE '%,{$sFolderId},%' OR
  877 + parent_folder_ids LIKE '%,{$sFolderId}'";
  878 + $aChildFolders = $this->oFolder->getList($sWhereClause);
  879 +
  880 + // Loop through subfolders and check each in the same way as the parent
  881 + if(!empty($aChildFolders)){
  882 + foreach($aChildFolders as $oChild){
  883 + $res = $this->check_entity($oChild);
  884 + if (PEAR::isError($res))
  885 + {
  886 + // NOTE: we may want to append the document reason to this
  887 + // in order for the user to have some idea WHY the folder cannot be archived
  888 + return PEAR::raiseError(_kt('Folder cannot be archived'));
  889 + }
  890 + }
721 } 891 }
722 } 892 }
  893 +
723 return parent::check_entity($oEntity); 894 return parent::check_entity($oEntity);
724 } 895 }
725 896
@@ -871,6 +1042,7 @@ class KTBulkArchiveAction extends KTBulkAction { @@ -871,6 +1042,7 @@ class KTBulkArchiveAction extends KTBulkAction {
871 } 1042 }
872 } 1043 }
873 1044
  1045 +// NOTE: None of the new code for folder recursion is implemented for this action.
874 class KTBrowseBulkExportAction extends KTBulkAction { 1046 class KTBrowseBulkExportAction extends KTBulkAction {
875 var $sName = 'ktcore.actions.bulk.export'; 1047 var $sName = 'ktcore.actions.bulk.export';
876 var $_sPermission = 'ktcore.permissions.read'; 1048 var $_sPermission = 'ktcore.permissions.read';
@@ -881,8 +1053,6 @@ class KTBrowseBulkExportAction extends KTBulkAction { @@ -881,8 +1053,6 @@ class KTBrowseBulkExportAction extends KTBulkAction {
881 return _kt('Download All'); 1053 return _kt('Download All');
882 } 1054 }
883 1055
884 -  
885 -  
886 function check_entity($oEntity) { 1056 function check_entity($oEntity) {
887 if((!is_a($oEntity, 'Document')) && (!is_a($oEntity, 'Folder'))) { 1057 if((!is_a($oEntity, 'Document')) && (!is_a($oEntity, 'Folder'))) {
888 return PEAR::raiseError(_kt('Document cannot be exported')); 1058 return PEAR::raiseError(_kt('Document cannot be exported'));
@@ -1081,6 +1251,7 @@ class KTBrowseBulkExportAction extends KTBulkAction { @@ -1081,6 +1251,7 @@ class KTBrowseBulkExportAction extends KTBulkAction {
1081 } 1251 }
1082 } 1252 }
1083 1253
  1254 +// NOTE: None of the new code for folder recursion is implemented for this action.
1084 class KTBrowseBulkCheckoutAction extends KTBulkAction { 1255 class KTBrowseBulkCheckoutAction extends KTBulkAction {
1085 var $sName = 'ktcore.actions.bulk.checkout'; 1256 var $sName = 'ktcore.actions.bulk.checkout';
1086 var $_sPermission = 'ktcore.permissions.write'; 1257 var $_sPermission = 'ktcore.permissions.write';