Commit d5629ce8efa656cd9ffd5577308bca5e6dcaab9b

Authored by Conrad Vermeulen
1 parent b65e77f1

KTS-3583

"CLONE -Two Files with Same Name, Different DocID but both listings in KTExplorer point to one file, and not the other.(SUP-963)"
Fixed.

Committed By: Conrad Vermeulen
Reviewed By: Megan Watson

git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@9499 c91229c3-7414-0410-bfa2-8a42b809f60b
ktapi/KTAPIDocument.inc.php
... ... @@ -6,31 +6,31 @@
6 6 * Document Management Made Simple
7 7 * Copyright (C) 2008 KnowledgeTree Inc.
8 8 * Portions copyright The Jam Warehouse Software (Pty) Limited
9   - *
  9 + *
10 10 * This program is free software; you can redistribute it and/or modify it under
11 11 * the terms of the GNU General Public License version 3 as published by the
12 12 * Free Software Foundation.
13   - *
  13 + *
14 14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 16 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 17 * details.
18   - *
  18 + *
19 19 * You should have received a copy of the GNU General Public License
20 20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21   - *
22   - * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco,
  21 + *
  22 + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco,
23 23 * California 94120-7775, or email info@knowledgetree.com.
24   - *
  24 + *
25 25 * The interactive user interfaces in modified source and object code versions
26 26 * of this program must display Appropriate Legal Notices, as required under
27 27 * Section 5 of the GNU General Public License version 3.
28   - *
  28 + *
29 29 * In accordance with Section 7(b) of the GNU General Public License version 3,
30 30 * these Appropriate Legal Notices must retain the display of the "Powered by
31   - * KnowledgeTree" logo and retain the original copyright notice. If the display of the
  31 + * KnowledgeTree" logo and retain the original copyright notice. If the display of the
32 32 * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
33   - * must display the words "Powered by KnowledgeTree" and retain the original
  33 + * must display the words "Powered by KnowledgeTree" and retain the original
34 34 * copyright notice.
35 35 * Contributor( s): ______________________________________
36 36 *
... ... @@ -113,7 +113,7 @@ class KTAPI_Document extends KTAPI_FolderItem
113 113 {
114 114 return ($this->document->getStatusID() == 3);
115 115 }
116   -
  116 +
117 117 /**
118 118 * Checks if the document is a shortcut
119 119 *
... ... @@ -132,8 +132,8 @@ class KTAPI_Document extends KTAPI_FolderItem
132 132 {
133 133 return $this->document->getSymbolicLinks();
134 134 }
135   -
136   -
  135 +
  136 +
137 137 /**
138 138 * This is the constructor for the KTAPI_Folder.
139 139 *
... ... @@ -379,7 +379,7 @@ class KTAPI_Document extends KTAPI_FolderItem
379 379 //if the document is checked-out by the current user, just return
380 380 //as no need to check-out again BUT we do need to download
381 381 //returning here will allow download, but skip check-out
382   - if ( ($this->document->getIsCheckedOut()) &&
  382 + if ( ($this->document->getIsCheckedOut()) &&
383 383 ($this->document->getCheckedOutUserID() == $_SESSION['userID']) )
384 384 {
385 385 return;
... ... @@ -517,6 +517,8 @@ class KTAPI_Document extends KTAPI_FolderItem
517 517 return $result;
518 518 }
519 519  
  520 + $tgt_folder = $target_folder->get_folder();
  521 +
520 522 $name = $this->document->getName();
521 523 $clash = KTDocumentUtil::nameExists($target_folder, $name);
522 524 if ($clash && !is_null($newname))
... ... @@ -526,7 +528,14 @@ class KTAPI_Document extends KTAPI_FolderItem
526 528 }
527 529 if ($clash)
528 530 {
529   - return new PEAR_Error('A document with this title already exists in your chosen folder. Please choose a different folder, or specify a new title for the copied document.');
  531 + if (is_null($newname))
  532 + {
  533 + $name = KTDocumentUtil::getUniqueDocumentName($tgt_folder, $name);
  534 + }
  535 + else
  536 + {
  537 + return new PEAR_Error('A document with this title already exists in your chosen folder. Please choose a different folder, or specify a new title for the copied document.');
  538 + }
530 539 }
531 540  
532 541 $filename=$this->document->getFilename();
... ... @@ -539,7 +548,14 @@ class KTAPI_Document extends KTAPI_FolderItem
539 548 }
540 549 if ($clash)
541 550 {
542   - return new PEAR_Error('A document with this filename already exists in your chosen folder. Please choose a different folder, or specify a new filename for the copied document.');
  551 + if (is_null($newfilename))
  552 + {
  553 + $filename = KTDocumentUtil::getUniqueFilename($tgt_folder, $newfilename);
  554 + }
  555 + else
  556 + {
  557 + return new PEAR_Error('A document with this filename already exists in your chosen folder. Please choose a different folder, or specify a new filename for the copied document.');
  558 + }
543 559 }
544 560  
545 561 DBUtil::startTransaction();
... ... @@ -1502,7 +1518,7 @@ class KTAPI_Document extends KTAPI_FolderItem
1502 1518  
1503 1519 $detail = array();
1504 1520 $document = $this->document;
1505   -
  1521 +
1506 1522 // get the document id
1507 1523 $detail['document_id'] = (int) $document->getId();
1508 1524  
... ... @@ -1625,7 +1641,7 @@ class KTAPI_Document extends KTAPI_FolderItem
1625 1641 {
1626 1642 $detail['version'] = (float) $detail['version'];
1627 1643 }
1628   -
  1644 +
1629 1645 //might be unset at the bottom in case of old webservice version
1630 1646 //make sure we're using the real document for this one
1631 1647 $this->document->switchToRealCore();
... ... @@ -1684,7 +1700,7 @@ class KTAPI_Document extends KTAPI_FolderItem
1684 1700 if($wsversion < 3){
1685 1701 unset($detail['linked_document_id']);
1686 1702 }
1687   -
  1703 +
1688 1704 return $detail;
1689 1705 }
1690 1706  
... ...
lib/documentmanagement/documentutil.inc.php
... ... @@ -683,20 +683,37 @@ $sourceDocument-&gt;getName(),
683 683 }
684 684 // }}}
685 685  
  686 + function getUniqueFilename($oFolder, $sFilename) {
  687 + // this is just a quick refactoring. We should look at a more optimal way of doing this as there are
  688 + // quite a lot of queries.
  689 + $iFolderId = $oFolder->getId();
  690 + while (KTDocumentUtil::fileExists($oFolder, $sFilename)) {
  691 + $oDoc = Document::getByFilenameAndFolder($sFilename, $iFolderId);
  692 + $sFilename = KTDocumentUtil::generateNewDocumentFilename($oDoc->getFileName());
  693 + }
  694 + return $sFilename;
  695 + }
  696 +
  697 + function getUniqueDocumentName($oFolder, $sFilename)
  698 + {
  699 + // this is just a quick refactoring. We should look at a more optimal way of doing this as there are
  700 + // quite a lot of queries.
  701 + $iFolderId = $oFolder->getId();
  702 + while(KTDocumentUtil::nameExists($oFolder, $sFilename)) {
  703 + $oDoc = Document::getByNameAndFolder($sFilename, $iFolderId);
  704 + $sFilename = KTDocumentUtil::generateNewDocumentName($oDoc->getName());
  705 + }
  706 + return $sFilename;
  707 + }
686 708  
687 709 // {{{ _in_add
688 710 function &_in_add($oFolder, $sFilename, $oUser, $aOptions) {
689 711 $aOrigOptions = $aOptions;
690   - while(KTDocumentUtil::fileExists($oFolder, $sFilename)) {
691   - $oDoc = Document::getByFilenameAndFolder($sFilename, $oFolder->getId());
692   - $sFilename = KTDocumentUtil::generateNewDocumentFilename($oDoc->getFileName());
693   - }
  712 +
  713 + $sFilename = KTDocumentUtil::getUniqueFilename($oFolder, $sFilename);
694 714 $sName = KTUtil::arrayGet($aOptions, 'description', $sFilename);
695   - while(KTDocumentUtil::nameExists($oFolder, $sName)) {
696   - $oDoc = Document::getByNameAndFolder($sName, $oFolder->getId());
697   - $aOptions['description'] = KTDocumentUtil::generateNewDocumentName($oDoc->getName());
698   - $sName = KTDocumentUtil::generateNewDocumentName($oDoc->getName());
699   - }
  715 + $sName = KTDocumentUtil::getUniqueDocumentName($oFolder, $sName);
  716 + $aOptions['description'] = $sName;
700 717  
701 718 $oUploadChannel =& KTUploadChannel::getSingleton();
702 719 $oUploadChannel->sendMessage(new KTUploadNewFile($sFilename));
... ... @@ -780,43 +797,48 @@ $sourceDocument-&gt;getName(),
780 797 }
781 798 // }}}
782 799  
783   - function generateNewDocumentFilename($sDocFilename){
784   - if(preg_match("/\([0-9]+\)(\.[^\.]+){1,}$/", $sDocFilename)){
785   - preg_match("/\([0-9]+\)\./", $sDocFilename, $matches);
786   - $new_one = substr($matches[0], 1);
787   - $new_two = explode(')', $new_one);
788   - $new = $new_two[0]+1;
789   -
790   - $pattern[0] = '/\([0-9]+\)\./';
791   - $replacement[0] = ' ('.$new.').';
792   - $sFilename = preg_replace($pattern, $replacement, $sDocFilename);
793   - }else{
794   - $matches = explode('.', $sDocFilename);
795   - $prefix = $matches[0].' (2)';
796   - for($i = 1; $i < count($matches); $i++ ){
797   - $suffix .= '.'.$matches[$i];
798   - }
799   - $sFilename = $prefix.$suffix;
800   - }
801   -
802   - return $sFilename;
  800 + function incrementNameCollissionNumbering($sDocFilename, $skipExtension = false){
  801 +
  802 + $iDot = strpos($sDocFilename, '.');
  803 + if ($skipExtension || $iDot === false)
  804 + {
  805 + if(preg_match("/\(([0-9]+)\)$/", $sDocFilename, $matches, PREG_OFFSET_CAPTURE)) {
  806 +
  807 + $iCount = $matches[1][0];
  808 + $iPos = $matches[1][1];
  809 +
  810 + $iNewCount = $iCount + 1;
  811 + $sDocFilename = substr($sDocFilename, 0, $iPos) . $iNewCount . substr($sDocFilename, $iPos + strlen($iCount));
  812 + }
  813 + else {
  814 + $sDocFilename = $sDocFilename . '(1)';
  815 + }
  816 + }
  817 + else
  818 + {
  819 + if(preg_match("/\(([0-9]+)\)(\.[^\.]+)+$/", $sDocFilename, $matches, PREG_OFFSET_CAPTURE)) {
  820 +
  821 + $iCount = $matches[1][0];
  822 + $iPos = $matches[1][1];
  823 +
  824 + $iNewCount = $iCount + 1;
  825 + $sDocFilename = substr($sDocFilename, 0, $iPos) . $iNewCount . substr($sDocFilename, $iPos + strlen($iCount));
  826 + }
  827 + else {
  828 + $sDocFilename = substr($sDocFilename, 0, $iDot) . '(1)' . substr($sDocFilename, $iDot);
  829 + }
  830 + }
  831 + return $sDocFilename;
  832 + }
  833 +
  834 +
  835 + function generateNewDocumentFilename($sDocFilename) {
  836 + return self::incrementNameCollissionNumbering($sDocFilename, false);
803 837 }
804 838  
805 839 function generateNewDocumentName($sDocName){
806   - if(preg_match("/\([0-9]+\)$/", $sDocName)){
807   - preg_match("/\([0-9]+\)$/", $sDocName, $matches);
808   - $new_one = substr($matches[0], 1);
809   - $new_two = explode(')', $new_one);
810   - $new = $new_two[0]+1;
811   -
812   - $pattern[0] = '/\([0-9]+\)$/';
813   - $replacement[0] = '('.$new.')';
814   - $sName = preg_replace($pattern, $replacement, $sDocName);
815   - }else{
816   - $sName = $sDocName.' (2)';
817   - }
818   -
819   - return $sName;
  840 + return self::incrementNameCollissionNumbering($sDocName, true);
  841 +
820 842 }
821 843  
822 844 // {{{ fileExists
... ... @@ -1039,61 +1061,89 @@ $sourceDocument-&gt;getName(),
1039 1061 //print '--------------------------------- BEFORE';
1040 1062 //print_r($oDocument);
1041 1063  
1042   - // grab the "source "data
1043   - $sTable = KTUtil::getTableName('documents');
1044   - $sQuery = 'SELECT * FROM ' . $sTable . ' WHERE id = ?';
  1064 + // TODO: this is not optimal. we have get() functions that will do SELECT when we already have the data in arrays
  1065 +
  1066 + // get the core record to be copied
  1067 + $sDocumentTable = KTUtil::getTableName('documents');
  1068 + $sQuery = 'SELECT * FROM ' . $sDocumentTable . ' WHERE id = ?';
1045 1069 $aParams = array($oDocument->getId());
1046 1070 $aCoreRow = DBUtil::getOneResult(array($sQuery, $aParams));
  1071 + // we unset the id as a new one will be created on insert
1047 1072 unset($aCoreRow['id']);
1048 1073  
1049   - $aCoreRow['modified'] = date('Y-m-d H:i:s');
1050   - $aCoreRow['folder_id'] = $oDestinationFolder->getId(); // new location.
1051   - $id = DBUtil::autoInsert($sTable, $aCoreRow);
1052   - if (PEAR::isError($id)) { return $id; }
1053   - // we still have a bogus md_version, but integrity holds, so fix it now.
1054   - $oCore = KTDocumentCore::get($id);
1055   -
1056   - // Get the metadata version for the source document
1057   - $sTable = KTUtil::getTableName('document_metadata_version');
1058   - $sQuery = 'SELECT * FROM ' . $sTable . ' WHERE id = ?';
1059   - $aParams = array($oDocument->getMetadataVersionId());
  1074 + // get a copy of the latest metadata version for the copied document
  1075 + $iOldMetadataId = $aCoreRow['metadata_version_id'];
  1076 + $sMetadataTable = KTUtil::getTableName('document_metadata_version');
  1077 + $sQuery = 'SELECT * FROM ' . $sMetadataTable . ' WHERE id = ?';
  1078 + $aParams = array($iOldMetadataId);
1060 1079 $aMDRow = DBUtil::getOneResult(array($sQuery, $aParams));
  1080 + // we unset the id as a new one will be created on insert
1061 1081 unset($aMDRow['id']);
1062 1082  
1063   - // Copy the source metadata into the destination document
1064   - $aMDRow['document_id'] = $oCore->getId();
1065   - if(!empty($sDestinationDocName)){
  1083 + // set the name for the document, possibly using name collission
  1084 + if (empty($sDestinationDocName)){
  1085 + $aMDRow['name'] = KTDocumentUtil::getUniqueDocumentName($oDestinationFolder, $aMDRow['name']);
  1086 + }
  1087 + else {
1066 1088 $aMDRow['name'] = $sDestinationDocName;
1067   - $aMDRow['description'] = $sDestinationDocName;
1068 1089 }
1069   - $id = DBUtil::autoInsert($sTable, $aMDRow);
1070   - if (PEAR::isError($id)) { return $id; }
1071   - $oCore->setMetadataVersionId($id);
1072   - $oMDV = KTDocumentMetadataVersion::get($id);
1073 1090  
1074   - // Get the content version for the source document
1075   - $sTable = KTUtil::getTableName('document_content_version');
1076   - $sQuery = 'SELECT * FROM ' . $sTable . ' WHERE id = ?';
1077   - $aParams = array($oDocument->_oDocumentContentVersion->getId());
  1091 + // get a copy of the latest content version for the copied document
  1092 + $iOldContentId = $aMDRow['content_version_id'];
  1093 + $sContentTable = KTUtil::getTableName('document_content_version');
  1094 + $sQuery = 'SELECT * FROM ' . $sContentTable . ' WHERE id = ?';
  1095 + $aParams = array($iOldContentId);
1078 1096 $aContentRow = DBUtil::getOneResult(array($sQuery, $aParams));
  1097 + // we unset the id as a new one will be created on insert
1079 1098 unset($aContentRow['id']);
1080 1099  
1081   - // Copy the source content into the destination document
1082   - $aContentRow['document_id'] = $oCore->getId();
1083   - if(!empty($sDestinationDocName)){
  1100 + // set the filename for the document, possibly using name collission
  1101 + if(empty($sDestinationDocName)) {
  1102 + $aContentRow['filename'] = KTDocumentUtil::getUniqueFilename($oDestinationFolder, $aContentRow['filename']);
  1103 + }
  1104 + else {
1084 1105 $aContentRow['filename'] = $sDestinationDocName;
1085 1106 }
1086   - $id = DBUtil::autoInsert($sTable, $aContentRow);
  1107 +
  1108 + // create the new document record
  1109 + $aCoreRow['modified'] = date('Y-m-d H:i:s');
  1110 + $aCoreRow['folder_id'] = $oDestinationFolder->getId(); // new location.
  1111 + $id = DBUtil::autoInsert($sDocumentTable, $aCoreRow);
  1112 + if (PEAR::isError($id)) { return $id; }
  1113 + $iNewDocumentId = $id;
  1114 +
  1115 + // create the new metadata record
  1116 + $aMDRow['document_id'] = $iNewDocumentId;
  1117 + $aMDRow['description'] = $aMDRow['name'];
  1118 + $id = DBUtil::autoInsert($sMetadataTable, $aMDRow);
  1119 + if (PEAR::isError($id)) { return $id; }
  1120 + $iNewMetadataId = $id;
  1121 +
  1122 + // the document metadata version is still pointing to the original
  1123 + $aCoreUpdate = array();
  1124 + $aCoreUpdate['metadata_version_id'] = $iNewMetadataId;
  1125 + $aCoreUpdate['metadata_version'] = 0;
  1126 +
  1127 + // create the new content version
  1128 + $aContentRow['document_id'] = $iNewDocumentId;
  1129 + $id = DBUtil::autoInsert($sContentTable, $aContentRow);
1087 1130 if (PEAR::isError($id)) { return $id; }
1088   - $oMDV->setContentVersionId($id);
  1131 + $iNewContentId = $id;
1089 1132  
1090   - $res = $oCore->update();
  1133 + // the metadata content version is still pointing to the original
  1134 + $aMetadataUpdate = array();
  1135 + $aMetadataUpdate['content_version_id'] = $iNewContentId;
  1136 + $aMetadataUpdate['metadata_version'] = 0;
  1137 +
  1138 + // apply the updates to the document and metadata records
  1139 + $res = DBUtil::autoUpdate($sDocumentTable, $aCoreUpdate, $iNewDocumentId);
1091 1140 if (PEAR::isError($res)) { return $res; }
1092   - $res = $oMDV->update();
  1141 +
  1142 + $res = DBUtil::autoUpdate($sMetadataTable, $aMetadataUpdate, $iNewMetadataId);
1093 1143 if (PEAR::isError($res)) { return $res; }
1094 1144  
1095 1145 // now, we have a semi-sane document object. get it.
1096   - $oNewDocument = Document::get($oCore->getId());
  1146 + $oNewDocument = Document::get($iNewDocumentId);
1097 1147  
1098 1148 //print '--------------------------------- AFTER';
1099 1149 //print_r($oDocument);
... ... @@ -1101,7 +1151,7 @@ $sourceDocument-&gt;getName(),
1101 1151 //print_r($oNewDocument);
1102 1152  
1103 1153 // copy the metadata from old to new.
1104   - $res = KTDocumentUtil::copyMetadata($oNewDocument, $oDocument->getMetadataVersionId());
  1154 + $res = KTDocumentUtil::copyMetadata($oNewDocument, $iOldMetadataId);
1105 1155 if (PEAR::isError($res)) { return $res; }
1106 1156  
1107 1157 // Ensure the copied document is not checked out
... ... @@ -1112,7 +1162,6 @@ $sourceDocument-&gt;getName(),
1112 1162 $oStorage =& KTStorageManagerUtil::getSingleton();
1113 1163 $res = $oStorage->copy($oDocument, $oNewDocument);
1114 1164  
1115   -
1116 1165 $oOriginalFolder = Folder::get($oDocument->getFolderId());
1117 1166 $iOriginalFolderPermissionObjectId = $oOriginalFolder->getPermissionObjectId();
1118 1167 $iDocumentPermissionObjectId = $oDocument->getPermissionObjectId();
... ... @@ -1227,12 +1276,17 @@ $sourceDocument-&gt;getName(),
1227 1276  
1228 1277 //put the document in the new folder
1229 1278 $oDocument->setFolderID($oFolder->getId());
  1279 + $sName = $oDocument->getName();
  1280 + $sFilename = $oDocument->getFileName();
  1281 +
  1282 + $oDocument->setFileName(KTDocumentUtil::getUniqueFilename($oToFolder, $sFilename));
  1283 + $oDocument->setName(KTDocumentUtil::getUniqueDocumentName($oToFolder, $sName));
  1284 +
1230 1285 $res = $oDocument->update();
1231 1286 if (PEAR::isError($res)) {
1232 1287 return $res;
1233 1288 }
1234 1289  
1235   -
1236 1290 //move the document on the file system(not if it's a symlink)
1237 1291 if(!$oDocument->isSymbolicLink()){
1238 1292 $oStorage =& KTStorageManagerUtil::getSingleton();
... ...