Commit 81077932f738f57878511eea6542b449e87a0709
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
Showing
2 changed files
with
228 additions
and
15 deletions
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'; |