Commit c22b8a428810cbb71dfa274befb34555c9879860

Authored by bshuttle
1 parent 0cf52d8f

Brad Shuttleworth 2006-02-09 add copy action.

Brad Shuttleworth 2006-02-09 fix for KTS-352
    Brad Shuttleworth 2006-02-09 - first pass of copy support.


git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@4890 c91229c3-7414-0410-bfa2-8a42b809f60b
browse.php
... ... @@ -155,6 +155,10 @@ class BrowseDispatcher extends KTStandardDispatcher {
155 155 exit(0);
156 156 }
157 157 $this->oQuery = new ValueBrowseQuery($oField, $oValue);
  158 + $this->resultURL = KTUtil::addQueryString($_SERVER['PHP_SELF'], sprintf("fBrowseMode=lookup_value&fField=%d&fValue=%d", $field, $value));
  159 + $this->aBreadcrumbs[] = array('name' => _('Lookup Values'), 'url' => KTUtil::addQueryString($_SERVER['PHP_SELF'], 'action=selectField'));
  160 + $this->aBreadcrumbs[] = array('name' => $oField->getName(), 'url' => KTUtil::addQueryString($_SERVER['PHP_SELF'], 'action=selectLookup&fField=' . $oField->getId()));
  161 + $this->aBreadcrumbs[] = array('name' => $oField->getName(), 'url' => KTUtil::addQueryString($_SERVER['PHP_SELF'], sprintf("fBrowseMode=lookup_value&fField=%d&fValue=%d", $field, $value)));
158 162 } else if ($this->browse_mode == 'document_type') {
159 163 $this->editable = false;
160 164 // FIXME implement document_type browsing.
... ... @@ -168,7 +172,8 @@ class BrowseDispatcher extends KTStandardDispatcher {
168 172 $this->oQuery = new TypeBrowseQuery($oDocType);
169 173  
170 174 // FIXME probably want to redirect to self + action=selectType
171   - $this->aBreadcrumbs[] = array('name' => _('Document Types'));
  175 + $this->aBreadcrumbs[] = array('name' => _('Document Types'), 'url' => KTUtil::addQueryString($_SERVER['PHP_SELF'], 'action=selectType'));
  176 + $this->aBreadcrumbs[] = array('name' => $oDocType->getName(), 'url' => KTUtil::addQueryString($_SERVER['PHP_SELF'], 'fBrowseMode=document_type&fType=' . $oDocType->getId()));
172 177  
173 178 $this->resultURL = KTUtil::addQueryString($_SERVER['PHP_SELF'], sprintf("fType=%s&fBrowseMode=document_type", $doctype));;
174 179 } else {
... ...
lib/documentmanagement/documentmetadataversion.inc.php
... ... @@ -59,6 +59,7 @@ class KTDocumentMetadataVersion extends KTEntity {
59 59 function getMetadataVersion() { return $this->iMetadataVersion; }
60 60 function setMetadataVersion($iNewValue) { $this->iMetadataVersion = $iNewValue; }
61 61 function getContentVersionId() { return $this->iContentVersionId; }
  62 + function setContentVersionId($iNewValue) { $this->iContentVersionId = $iNewValue; }
62 63 function setContentVersion($iNewValue) { $this->iContentVersion = $iNewValue; }
63 64 function getDocumentTypeId() { return $this->iDocumentTypeId; }
64 65 function setDocumentTypeId($iNewValue) { $this->iDocumentTypeId = $iNewValue; }
... ...
lib/documentmanagement/documentutil.inc.php
... ... @@ -627,6 +627,7 @@ class KTDocumentUtil {
627 627 KTDocumentUtil::updateSearchableText($oDocument);
628 628 }
629 629  
  630 +
630 631 function canBeMoved($oDocument) {
631 632 if ($oDocument->getIsCheckedOut()) {
632 633 return false;
... ... @@ -636,6 +637,78 @@ class KTDocumentUtil {
636 637 }
637 638 return true;
638 639 }
  640 +
  641 +
  642 + function copy($oDocument, $oDestinationFolder) {
  643 + // 1. generate a new triad of content, metadata and core objects.
  644 + // 2. update the storage path.
  645 +
  646 +
  647 + // grab the "source "data
  648 + $sTable = KTUtil::getTableName('documents');
  649 + $sQuery = 'SELECT * FROM ' . $sTable . ' WHERE id = ?';
  650 + $aParams = array($oDocument->getId());
  651 + $aCoreRow = DBUtil::getOneResult(array($sQuery, $aParams));
  652 + unset($aCoreRow['id']);
  653 +
  654 + $aCoreRow['folder_id'] = $oDestinationFolder->getId(); // new location.
  655 + $id = DBUtil::autoInsert($sTable, $aCoreRow);
  656 + if (PEAR::isError($id)) { return $id; }
  657 + // we still have a bogus md_version, but integrity holds, so fix it now.
  658 + $oCore = KTDocumentCore::get($id);
  659 +
  660 + $sTable = KTUtil::getTableName('document_metadata_version');
  661 + $sQuery = 'SELECT * FROM ' . $sTable . ' WHERE id = ?';
  662 + $aParams = array($oDocument->getMetadataVersionId());
  663 + $aMDRow = DBUtil::getOneResult(array($sQuery, $aParams));
  664 + unset($aMDRow['id']);
  665 + $aMDRow['document_id'] = $oCore->getId();
  666 + $id = DBUtil::autoInsert($sTable, $aMDRow);
  667 + if (PEAR::isError($id)) { return $id; }
  668 + $oCore->setMetadataVersionId($id);
  669 + $oMDV = KTDocumentMetadataVersion::get($id);
  670 +
  671 + $sTable = KTUtil::getTableName('document_content_version');
  672 + $sQuery = 'SELECT * FROM ' . $sTable . ' WHERE id = ?';
  673 + $aParams = array($oDocument->_oDocumentContentVersion->getId());
  674 + $aContentRow = DBUtil::getOneResult(array($sQuery, $aParams));
  675 + unset($aContentRow['id']);
  676 + $aContentRow['document_id'] = $oCore->getId();
  677 + $id = DBUtil::autoInsert($sTable, $aContentRow);
  678 + if (PEAR::isError($id)) { return $id; }
  679 + $oMDV->setContentVersionId($id);
  680 +
  681 + $res = $oCore->update();
  682 + if (PEAR::isError($res)) { return $res; }
  683 + $res = $oMDV->update();
  684 + if (PEAR::isError($res)) { return $res; }
  685 +
  686 + // now, we have a semi-sane document object. get it.
  687 + $oNewDocument = Document::get($oCore->getId());
  688 +
  689 + // copy the metadata from old to new.
  690 + $res = KTDocumentUtil::copyMetadata($oNewDocument, $oDocument->getMetadataVersionId());
  691 + if (PEAR::isError($res)) { return $res; }
  692 +
  693 + // finally, copy the actual file.
  694 + $oStorage =& KTStorageManagerUtil::getSingleton();
  695 + $res = $oStorage->copy($oDocument, $oNewDocument);
  696 +
  697 +
  698 + $oOriginalFolder = Folder::get($oDocument->getFolderId());
  699 + $iOriginalFolderPermissionObjectId = $oOriginalFolder->getPermissionObjectId();
  700 + $iDocumentPermissionObjectId = $oDocument->getPermissionObjectId();
  701 +
  702 + if ($iDocumentPermissionObjectId === $iOriginalFolderPermissionObjectId) {
  703 + $oNewDocument->setPermissionObjectId($oDestinationFolder->getPermissionObjectId());
  704 + }
  705 +
  706 + $res = $oNewDocument->update();
  707 + if (PEAR::isError($res)) { return $res; }
  708 +
  709 + return $oNewDocument;
  710 + }
  711 +
639 712 }
640 713  
641 714 class KTMetadataValidationError extends PEAR_Error {
... ...
lib/storage/ondiskpathstoragemanager.inc.php
... ... @@ -239,7 +239,27 @@ class KTOnDiskPathStorageManager extends KTStorageManager {
239 239 return KTUtil::moveDirectory($sSrc, $sDst);
240 240 }
241 241  
242   -
  242 + /**
  243 + * Perform any storage changes necessary to account for a copied
  244 + * document object.
  245 + */
  246 + function copy($oSrcDocument, &$oNewDocument) {
  247 + // we get the Folder object
  248 + $oVersion = $oNewDocument->_oDocumentContentVersion;
  249 + $oConfig =& KTConfig::getSingleton();
  250 + $sDocumentRoot = $oConfig->get('urls/documentRoot');
  251 +
  252 + $sOldPath = sprintf("%s/%s-%s", KTDocumentCore::_generateFolderPath($oSrcDocument->getFolderID()), $oSrcDocument->_oDocumentContentVersion->getId(), $oSrcDocument->_oDocumentContentVersion->getFileName());
  253 + $sNewPath = sprintf("%s/%s-%s", KTDocumentCore::_generateFolderPath($oNewDocument->getFolderID()), $oNewDocument->_oDocumentContentVersion->getId(), $oNewDocument->_oDocumentContentVersion->getFileName());
  254 + $sFullOldPath = sprintf("%s/%s", $sDocumentRoot, $sOldPath);
  255 + $sFullNewPath = sprintf("%s/%s", $sDocumentRoot, $sNewPath);
  256 +
  257 + $res = KTUtil::copyFile($sFullOldPath, $sFullNewPath);
  258 + if (PEAR::isError($res)) { return $res; }
  259 + $oVersion->setStoragePath($sNewPath);
  260 + $oVersion->update();
  261 + }
  262 +
243 263 /**
244 264 * Deletes a document- moves it to the Deleted/ folder
245 265 *
... ...
lib/storage/storagemanager.inc.php
... ... @@ -67,6 +67,14 @@ class KTStorageManager {
67 67 }
68 68  
69 69 /**
  70 + * Perform any storage changes necessary to account for a copied
  71 + * document object.
  72 + */
  73 + function copy ($oSrcDocument, &$oNewDocument) {
  74 + return PEAR::raiseError("Not implemented");
  75 + }
  76 +
  77 + /**
70 78 * Performs any storage changes necessary to account for the
71 79 * document being marked as deleted.
72 80 */
... ...
lib/util/ktutil.inc
... ... @@ -318,6 +318,33 @@ class KTUtil {
318 318 }
319 319 }
320 320 // }}}
  321 +
  322 + // {{{ copyFile
  323 + function copyFile ($sSrc, $sDst) {
  324 + // Only 4.3.3 and above allow us to use rename across partitions
  325 + // on Unix-like systems.
  326 + if (OS_UNIX) {
  327 + $aSrcStat = stat($sSrc);
  328 + if ($aSrcStat === false) {
  329 + return PEAR::raiseError("Couldn't stat source file: $sSrc");
  330 + }
  331 + $aDstStat = stat(dirname($sDst));
  332 + if ($aDstStat === false) {
  333 + return PEAR::raiseError("Couldn't stat destination location: $sDst");
  334 + }
  335 +
  336 + $res = @copy($sSrc, $sDst);
  337 + if ($res === false) {
  338 + return PEAR::raiseError("Could not copy to destination: $sDst");
  339 + }
  340 + } else {
  341 + $res = @copy($sSrc, $sDst);
  342 + if ($res === false) {
  343 + return PEAR::raiseError("Could not copy to destination: $sDst");
  344 + }
  345 + }
  346 + }
  347 + // }}}
321 348  
322 349 // {{{ getTableName
323 350 /**
... ...
plugins/ktcore/KTCorePlugin.php
... ... @@ -16,6 +16,7 @@ class KTCorePlugin extends KTPlugin {
16 16 $this->registerAction('documentaction', 'KTDocumentEditAction', 'ktcore.actions.document.edit', 'KTDocumentActions.php');
17 17 $this->registerAction('documentaction', 'KTDocumentDeleteAction', 'ktcore.actions.document.delete', 'KTDocumentActions.php');
18 18 $this->registerAction('documentaction', 'KTDocumentMoveAction', 'ktcore.actions.document.move', 'KTDocumentActions.php');
  19 + $this->registerAction('documentaction', 'KTDocumentCopyAction', 'ktcore.actions.document.copy', 'KTDocumentActions.php');
19 20 $this->registerAction('documentaction', 'KTDocumentTransactionHistoryAction', 'ktcore.actions.document.transactionhistory', 'KTDocumentActions.php');
20 21 $this->registerAction('documentaction', 'KTDocumentVersionHistoryAction', 'ktcore.actions.document.versionhistory', 'KTDocumentActions.php');
21 22 $this->registerAction('documentaction', 'KTDocumentArchiveAction', 'ktcore.actions.document.archive', 'KTDocumentActions.php');
... ...
plugins/ktcore/KTDocumentActions.php
... ... @@ -553,6 +553,172 @@ class KTDocumentMoveAction extends KTDocumentAction {
553 553 }
554 554 // }}}
555 555  
  556 +
  557 +class KTDocumentCopyColumn extends TitleColumn {
  558 + function KTDocumentCopyColumn($sLabel, $sName, $oDocument) {
  559 + $this->oDocument = $oDocument;
  560 + parent::TitleColumn($sLabel, $sName);
  561 + }
  562 + function buildFolderLink($aDataRow) {
  563 + return KTUtil::addQueryString($_SERVER['PHP_SELF'], sprintf('fDocumentId=%d&fFolderId=%d', $this->oDocument->getId(), $aDataRow["folder"]->getId()));
  564 + }
  565 +}
  566 +
  567 +// {{{ KTDocumentMoveAction
  568 +class KTDocumentCopyAction extends KTDocumentAction {
  569 + var $sDisplayName = 'Copy';
  570 + var $sName = 'ktcore.actions.document.copy';
  571 +
  572 + var $_sShowPermission = "ktcore.permissions.read";
  573 +
  574 + function getInfo() {
  575 + if ($this->oDocument->getIsCheckedOut()) {
  576 + return null;
  577 + }
  578 + return parent::getInfo();
  579 + }
  580 +
  581 + function check() {
  582 + $res = parent::check();
  583 + if ($res !== true) {
  584 + return $res;
  585 + }
  586 + if ($this->oDocument->getIsCheckedOut()) {
  587 + $_SESSION["KTErrorMessage"][]= _("This document can't be copied because it is checked out");
  588 + controllerRedirect('viewDocument', 'fDocumentId=' . $this->oDocument->getId());
  589 + exit(0);
  590 + }
  591 + $iFolderId = KTUtil::arrayGet($_REQUEST, 'fFolderId', $this->oDocument->getFolderId());
  592 + $this->oFolder = $this->oValidator->validateFolder($iFolderId);
  593 + $this->oDocumentFolder = $this->oValidator->validateFolder($this->oDocument->getFolderId());
  594 + return true;
  595 + }
  596 +
  597 + function do_main() {
  598 + $this->oPage->setBreadcrumbDetails(_("Copy"));
  599 + $oTemplate =& $this->oValidator->validateTemplate('ktcore/action/copy');
  600 + $move_fields = array();
  601 + $aNames = $this->oDocumentFolder->getPathArray();
  602 + $aNames[] = $this->oDocument->getName();
  603 + $sDocumentName = join(" » ", $aNames);
  604 + $move_fields[] = new KTStaticTextWidget(_('Document to copy'), '', 'fDocumentId', $sDocumentName, $this->oPage, false);
  605 +
  606 + $collection = new DocumentCollection();
  607 + $collection->addColumn(new KTDocumentMoveColumn("Test 1 (title)","title", $this->oDocument));
  608 + $qObj = new FolderBrowseQuery($this->oFolder->getId());
  609 + $collection->setQueryObject($qObj);
  610 +
  611 + $batchPage = (int) KTUtil::arrayGet($_REQUEST, "page", 0);
  612 + $batchSize = 20;
  613 +
  614 + $resultURL = KTUtil::addQueryString($_SERVER['PHP_SELF'], sprintf("fDocumentId=%d&fFolderId=%d", $this->oDocument->getId(), $this->oFolder->getId()));
  615 + $collection->setBatching($resultURL, $batchPage, $batchSize);
  616 +
  617 + // ordering. (direction and column)
  618 + $displayOrder = KTUtil::arrayGet($_REQUEST, 'sort_order', "asc");
  619 + if ($displayOrder !== "asc") { $displayOrder = "desc"; }
  620 + $displayControl = KTUtil::arrayGet($_REQUEST, 'sort_on', "title");
  621 +
  622 + $collection->setSorting($displayControl, $displayOrder);
  623 +
  624 + $collection->getResults();
  625 +
  626 + $aBreadcrumbs = array();
  627 + $folder_path_names = $this->oFolder->getPathArray();
  628 + $folder_path_ids = explode(',', $this->oFolder->getParentFolderIds());
  629 + $folder_path_ids[] = $this->oFolder->getId();
  630 + if ($folder_path_ids[0] == 0) {
  631 + array_shift($folder_path_ids);
  632 + array_shift($folder_path_names);
  633 + }
  634 +
  635 + foreach (range(0, count($folder_path_ids) - 1) as $index) {
  636 + $id = $folder_path_ids[$index];
  637 + $url = KTUtil::addQueryString($_SERVER['PHP_SELF'], sprintf("fDocumentId=%d&fFolderId=%d", $this->oDocument->getId(), $id));
  638 + $aBreadcrumbs[] = array("url" => $url, "name" => $folder_path_names[$index]);
  639 + }
  640 +
  641 + $oTemplate->setData(array(
  642 + 'context' => &$this,
  643 + 'move_fields' => $move_fields,
  644 + 'collection' => $collection,
  645 + 'collection_breadcrumbs' => $aBreadcrumbs,
  646 + ));
  647 + return $oTemplate->render();
  648 + }
  649 +
  650 + function do_copy() {
  651 + $this->oPage->setBreadcrumbDetails(_("Copy"));
  652 + $oTemplate =& $this->oValidator->validateTemplate('ktcore/action/copy_final');
  653 + $sFolderPath = join(" » ", $this->oFolder->getPathArray());
  654 + $aNames = $this->oDocumentFolder->getPathArray();
  655 + $aNames[] = $this->oDocument->getName();
  656 + $sDocumentName = join(" » ", $aNames);
  657 + $move_fields = array();
  658 + $move_fields[] = new KTStaticTextWidget(_('Document to move'), '', 'fDocumentId', $sDocumentName, $this->oPage, false);
  659 + $move_fields[] = new KTStaticTextWidget(_('Target folder'), '', 'fFolderId', $sFolderPath, $this->oPage, false);
  660 + $move_fields[] = new KTStringWidget(_('Reason'), _('The reason for this document to be moved.'), 'reason', "", $this->oPage, true);
  661 +
  662 + $oTemplate->setData(array(
  663 + 'context' => &$this,
  664 + 'move_fields' => $move_fields,
  665 + ));
  666 + return $oTemplate->render();
  667 + }
  668 +
  669 + function do_copy_final() {
  670 + $sReason = KTUtil::arrayGet($_REQUEST, 'reason');
  671 + $aOptions = array(
  672 + 'message' => _("No reason given"),
  673 + 'redirect_to' => array('move', sprintf('fDocumentId=%d&fFolderId=%d', $this->oDocument->getId(), $this->oFolder->getId())),
  674 + );
  675 + $this->oValidator->notEmpty($sReason, $aOptions);
  676 +
  677 + if (!Permission::userHasFolderWritePermission($this->oFolder)) {
  678 + $this->errorRedirectTo("main", _("You do not have permission to move a document to this location"), sprintf("fDocumentId=%d&fFolderId=%d", $this->oDocument->getId(), $this->oFolder->getId()));
  679 + exit(0);
  680 + }
  681 +
  682 + // FIXME agree on document-duplication rules re: naming, etc.
  683 +
  684 + $this->startTransaction();
  685 +
  686 + $oNewDoc = KTDocumentUtil::copy($this->oDocument, $this->oFolder);
  687 + if (PEAR::isError($oNewDoc)) {
  688 + $this->errorRedirectTo("main", _("Failed to move document: ") . $oNewDoc->getMessage(), sprintf("fDocumentId=%d&fFolderId=%d", $this->oDocument->getId(), $this->oFolder->getId()));
  689 + exit(0);
  690 + }
  691 +
  692 + $this->commitTransaction();
  693 +
  694 +
  695 + // FIXME do we need to refactor all trigger usage into the util function?
  696 + $oKTTriggerRegistry = KTTriggerRegistry::getSingleton();
  697 + $aTriggers = $oKTTriggerRegistry->getTriggers('copyDocument', 'postValidate');
  698 + foreach ($aTriggers as $aTrigger) {
  699 + $sTrigger = $aTrigger[0];
  700 + $oTrigger = new $sTrigger;
  701 + $aInfo = array(
  702 + "document" => $oNewDocument,
  703 + "old_folder" => $this->oDocumentFolder,
  704 + "new_folder" => $this->oFolder,
  705 + );
  706 + $oTrigger->setInfo($aInfo);
  707 + $ret = $oTrigger->postValidate();
  708 + }
  709 +
  710 + $aOptions = array('user' => $oUser);
  711 + $oDocumentTransaction = & new DocumentTransaction($oNewDoc, "Document copied from old version.", 'ktcore.transactions.create', $aOptions);
  712 + $res = $oDocumentTransaction->create();
  713 +
  714 + $_SESSION['KTInfoMessage'][] = _('Document copied.');
  715 +
  716 + controllerRedirect('viewDocument', 'fDocumentId=' . $oNewDoc->getId());
  717 + exit(0);
  718 + }
  719 +}
  720 +// }}}
  721 +
556 722 // {{{ KTDocumentHistoryAction
557 723 class KTDocumentTransactionHistoryAction extends KTDocumentAction {
558 724 var $sDisplayName = 'Transaction History';
... ...