. * * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, * California 94120-7775, or email info@knowledgetree.com. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU General Public License version 3. * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "Powered by * KnowledgeTree" logo and retain the original copyright notice. If the display of the * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. * Contributor( s): ______________________________________ */ // LEGACY PATHS require_once(KT_LIB_DIR . '/documentmanagement/DocumentFieldLink.inc'); require_once(KT_LIB_DIR . '/documentmanagement/DocumentTransaction.inc'); require_once(KT_LIB_DIR . '/documentmanagement/Document.inc'); require_once(KT_LIB_DIR . '/storage/storagemanager.inc.php'); // NEW PATHS require_once(KT_LIB_DIR . '/storage/storagemanager.inc.php'); require_once(KT_LIB_DIR . '/filelike/filelikeutil.inc.php'); require_once(KT_LIB_DIR . '/metadata/metadatautil.inc.php'); require_once(KT_LIB_DIR . '/metadata/fieldset.inc.php'); require_once(KT_LIB_DIR . '/subscriptions/subscriptions.inc.php'); require_once(KT_LIB_DIR . '/triggers/triggerregistry.inc.php'); require_once(KT_LIB_DIR . '/foldermanagement/Folder.inc'); require_once(KT_LIB_DIR . '/alert/EmailTemplate.inc.php'); require_once(KT_LIB_DIR . '/browse/browseutil.inc.php'); // WORKFLOW require_once(KT_LIB_DIR . '/workflow/workflowutil.inc.php'); class KTDocumentUtil { function checkin($oDocument, $sFilename, $sCheckInComment, $oUser, $aOptions = false) { $oStorage =& KTStorageManagerUtil::getSingleton(); $iFileSize = filesize($sFilename); $iPreviousMetadataVersion = $oDocument->getMetadataVersionId(); $bSuccess = $oDocument->startNewContentVersion($oUser); if (PEAR::isError($bSuccess)) { return $bSuccess; } KTDocumentUtil::copyMetadata($oDocument, $iPreviousMetadataVersion); $aOptions['temp_file'] = $sFilename; $res = KTDocumentUtil::storeContents($oDocument, '', $aOptions); if (PEAR::isError($res)) { return $res; } $oDocument->setLastModifiedDate(getCurrentDateTime()); $oDocument->setModifiedUserId($oUser->getId()); $oDocument->setIsCheckedOut(false); $oDocument->setCheckedOutUserID(-1); if ($aOptions['major_update']) { $oDocument->setMajorVersionNumber($oDocument->getMajorVersionNumber()+1); $oDocument->setMinorVersionNumber('0'); } else { $oDocument->setMinorVersionNumber($oDocument->getMinorVersionNumber()+1); } $oDocument->setFileSize($iFileSize); if(is_array($aOptions)) { $sFilename = KTUtil::arrayGet($aOptions, 'newfilename', ''); if(!empty($sFilename)) { global $default; $oDocument->setFileName($sFilename); $default->log->info('renamed document ' . $oDocument->getId() . ' to ' . $sFilename); // detection of mime types needs to be refactored. this stuff is damn messy! // If the filename has changed then update the mime type $iMimeTypeId = KTMime::getMimeTypeID('', $sFilename); $oDocument->setMimeTypeId($iMimeTypeId); } } $bSuccess = $oDocument->update(); if ($bSuccess !== true) { if (PEAR::isError($bSuccess)) { return $bSuccess; } return PEAR::raiseError(_kt('An error occurred while storing this document in the database')); } // create the document transaction record $oDocumentTransaction = new DocumentTransaction($oDocument, $sCheckInComment, 'ktcore.transactions.check_in'); $oDocumentTransaction->create(); $oKTTriggerRegistry = KTTriggerRegistry::getSingleton(); $aTriggers = $oKTTriggerRegistry->getTriggers('content', 'scan'); foreach ($aTriggers as $aTrigger) { $sTrigger = $aTrigger[0]; $oTrigger = new $sTrigger; $oTrigger->setDocument($oDocument); $ret = $oTrigger->scan(); if (PEAR::isError($ret)) { $oDocument->delete(); return $ret; } } Indexer::index($oDocument); // fire subscription alerts for the checked in document $oSubscriptionEvent = new SubscriptionEvent(); $oFolder = Folder::get($oDocument->getFolderID()); $oSubscriptionEvent->CheckinDocument($oDocument, $oFolder); return true; } function checkout($oDocument, $sCheckoutComment, $oUser) { //automatically check out the linked document if this is a shortcut if($oDocument->isSymbolicLink()){ $oDocument->switchToLinkedCore(); } if ($oDocument->getIsCheckedOut()) { return PEAR::raiseError(_kt('Already checked out.')); } // FIXME at the moment errors this _does not_ rollback. $oDocument->setIsCheckedOut(true); $oDocument->setCheckedOutUserID($oUser->getId()); if (!$oDocument->update()) { return PEAR::raiseError(_kt('There was a problem checking out the document.')); } $oKTTriggerRegistry = KTTriggerRegistry::getSingleton(); $aTriggers = $oKTTriggerRegistry->getTriggers('checkout', 'postValidate'); foreach ($aTriggers as $aTrigger) { $sTrigger = $aTrigger[0]; $oTrigger = new $sTrigger; $aInfo = array( 'document' => $oDocument, ); $oTrigger->setInfo($aInfo); $ret = $oTrigger->postValidate(); if (PEAR::isError($ret)) { return $ret; } } $oDocumentTransaction = new DocumentTransaction($oDocument, $sCheckoutComment, 'ktcore.transactions.check_out'); $oDocumentTransaction->create(); // fire subscription alerts for the downloaded document $oSubscriptionEvent = new SubscriptionEvent(); $oFolder = Folder::get($oDocument->getFolderID()); $oSubscriptionEvent->CheckOutDocument($oDocument, $oFolder); return true; } function archive($oDocument, $sReason) { $this->startTransaction(); $oDocument->setStatusID(ARCHIVED); $res = $oDocument->update(); if (PEAR::isError($res) || ($res === false)) { return PEAR::raiseError(_kt('There was a database error while trying to archive this file')); } //delete all shortcuts linking to this document $aSymlinks = $oDocument->getSymbolicLinks(); foreach($aSymlinks as $aSymlink){ $oShortcutDocument = Document::get($aSymlink['id']); $oOwnerUser = User::get($oShortcutDocument->getOwnerID()); KTDocumentUtil::deleteSymbolicLink($aSymlink['id']); //send an email to the owner of the shortcut if($oOwnerUser->getEmail()!=null && $oOwnerUser->getEmailNotification() == true){ $emailTemplate = new EmailTemplate("kt3/notifications/notification.SymbolicLinkArchived",array('user_name'=>$this->oUser->getName(), 'url'=>KTUtil::ktLink(KTBrowseUtil::getUrlForDocument($oShortcutDocument)), 'title' =>$oShortcutDocument->getName())); $email = new EmailAlert($oOwnerUser->getEmail(),_kt("KnowledgeTree Notification"),$emailTemplate->getBody()); $email->send(); } } $oDocumentTransaction = & new DocumentTransaction($oDocument, sprintf(_kt('Document archived: %s'), $sReason), 'ktcore.transactions.update'); $oDocumentTransaction->create(); $this->commitTransaction(); $oKTTriggerRegistry = KTTriggerRegistry::getSingleton(); $aTriggers = $oKTTriggerRegistry->getTriggers('archive', 'postValidate'); foreach ($aTriggers as $aTrigger) { $sTrigger = $aTrigger[0]; $oTrigger = new $sTrigger; $aInfo = array( 'document' => $oDocument, ); $oTrigger->setInfo($aInfo); $ret = $oTrigger->postValidate(); if (PEAR::isError($ret)) { $oDocument->delete(); return $ret; } } // fire subscription alerts for the archived document $oSubscriptionEvent = new SubscriptionEvent(); $oFolder = Folder::get($oDocument->getFolderID()); $oSubscriptionEvent->ArchivedDocument($oDocument, $oFolder); return true; } function &_add($oFolder, $sFilename, $oUser, $aOptions) { global $default; //$oContents = KTUtil::arrayGet($aOptions, 'contents'); $aMetadata = KTUtil::arrayGet($aOptions, 'metadata', null, false); $oDocumentType = KTUtil::arrayGet($aOptions, 'documenttype'); $sDescription = KTUtil::arrayGet($aOptions, 'description', ''); if(empty($sDescription)){ // If no document name is provided use the filename minus the extension $aFile = pathinfo($sFilename); $sDescription = (isset($aFile['filename']) && !empty($aFile['filename'])) ? $aFile['filename'] : $sFilename; } $oUploadChannel =& KTUploadChannel::getSingleton(); if ($oDocumentType) { $iDocumentTypeId = KTUtil::getId($oDocumentType); } else { $iDocumentTypeId = 1; } $oUploadChannel->sendMessage(new KTUploadGenericMessage(_kt('Creating database entry'))); $oDocument =& Document::createFromArray(array( 'name' => $sDescription, 'description' => $sDescription, 'filename' => $sFilename, 'folderid' => $oFolder->getID(), 'creatorid' => $oUser->getID(), 'documenttypeid' => $iDocumentTypeId, )); $oUploadChannel->sendMessage(new KTUploadGenericMessage(_kt('Storing contents'))); $res = KTDocumentUtil::storeContents($oDocument, '', $aOptions); if (PEAR::isError($res)) { if (!PEAR::isError($oDocument)) { $oDocument->delete(); } return $res; } if (is_null($aMetadata)) { $res = KTDocumentUtil::setIncomplete($oDocument, 'metadata'); if (PEAR::isError($res)) { $oDocument->delete(); return $res; } } else { $oUploadChannel->sendMessage(new KTUploadGenericMessage(_kt('Saving metadata'))); $res = KTDocumentUtil::saveMetadata($oDocument, $aMetadata, $aOptions); if (PEAR::isError($res)) { $oDocument->delete(); return $res; } } // setIncomplete and storeContents may change the document's status or // storage_path, so now is the time to update $oDocument->update(); return $oDocument; } /** * Create a symbolic link in the target folder * * @param Document $sourceDocument the document to create a link to * @param Folder $targetFolder the folder to place the link in * @param User $user current user */ static function createSymbolicLink($sourceDocument, $targetFolder, $user = null) // added/ { //validate input if (is_numeric($sourceDocument)) { $sourceDocument = Document::get($sourceDocument); } if (!$sourceDocument instanceof Document) { return PEAR::raiseError(_kt('Source document not specified')); } if (is_numeric($targetFolder)) { $targetFolder = Folder::get($targetFolder); } if (!$targetFolder instanceof Folder) { return PEAR::raiseError(_kt('Target folder not specified')); } if (is_null($user)) { $user = $_SESSION['userID']; } if (is_numeric($user)) { $user = User::get($user); } //check for permissions $oPermission =& KTPermission::getByName("ktcore.permissions.write"); $oReadPermission =& KTPermission::getByName("ktcore.permissions.read"); if (KTBrowseUtil::inAdminMode($user, $targetFolder)) { if(!KTPermissionUtil::userHasPermissionOnItem($user, $oPermission, $targetFolder)){ return PEAR::raiseError(_kt('You\'re not authorized to create shortcuts')); } } if (!KTBrowseUtil::inAdminMode($user, $sourceDocument->getParentID())) { if(!KTPermissionUtil::userHasPermissionOnItem($user, $oReadPermission, $sourceDocument)){ return PEAR::raiseError(_kt('You\'re not authorized to create a shortcut to this document')); } } //check if the shortcut doesn't already exists in the target folder $aSymlinks = $sourceDocument->getSymbolicLinks(); foreach($aSymlinks as $iSymlink){ $oSymlink = Document::get($iSymlink['id']); $oSymlink->switchToRealCore(); if($oSymlink->getFolderID() == $targetFolder->getID()){ return PEAR::raiseError(_kt('There already is a shortcut to this document in the target folder.')); } } //create the actual shortcut $oCore = KTDocumentCore::createFromArray(array( 'iCreatorId'=>$user->getId(), 'iFolderId'=>$targetFolder->getId(), 'iLinkedDocumentId'=>$sourceDocument->getId(), 'sFullPath'=> $targetFolder->getFullPath() . '/' . $sourceDocument->getName(), 'iPermissionObjectId'=>$targetFolder->getPermissionObjectID(), 'iPermissionLookupId'=>$targetFolder->getPermissionLookupID(), 'iStatusId'=>1, 'iMetadataVersionId'=>$sourceDocument->getMetadataVersionId(), )); $document = Document::get($oCore->getId()); return $document; } /** * Deletes a document symbolic link * * @param Document $document the symbolic link document * @param User $user the user deleting the link * @return unknown */ static function deleteSymbolicLink($document, $user = null) // added/ { //validate input if (is_numeric($document)) { $document = Document::get($document); } if (!$document instanceof Document) { return PEAR::raiseError(_kt('Document not specified')); } if (!$document->isSymbolicLink()) { return PEAR::raiseError(_kt('Document must be a symbolic link entity')); } if (is_null($user)) { $user = $_SESSION['userID']; } if (is_numeric($user)) { $user = User::get($user); } //check permissions $oPerm = KTPermission::getByName('ktcore.permissions.delete'); if (!KTBrowseUtil::inAdminMode($user, $document->getParentID())) { if(!KTPermissionUtil::userHasPermissionOnItem($user, $oPerm, $document)){ return PEAR::raiseError(_kt('You\'re not authorized to delete this shortcut')); } } // we only need to delete the document entry for the link $sql = "DELETE FROM documents WHERE id=?"; DBUtil::runQuery(array($sql, array($document->getId()))); } // Overwrite the document function overwrite($oDocument, $sFilename, $sTempFileName, $oUser, $aOptions) { //$oDocument, $sFilename, $sCheckInComment, $oUser, $aOptions = false $oStorage =& KTStorageManagerUtil::getSingleton(); $iFileSize = filesize($sTempFileName); // Check that document is not checked out if($oDocument->getIsCheckedOut()) { return PEAR::raiseError(_kt('Document is checkout and cannot be overwritten')); } if (!$oStorage->upload($oDocument, $sTempFileName)) { return PEAR::raiseError(_kt('An error occurred while storing the new file')); } $oDocument->setLastModifiedDate(getCurrentDateTime()); $oDocument->setModifiedUserId($oUser->getId()); $oDocument->setFileSize($iFileSize); $sOriginalFilename = $oDocument->getFileName(); if($sOriginalFilename != $sFilename){ if(strlen($sFilename)) { global $default; $oDocument->setFileName($sFilename); $default->log->info('renamed document ' . $oDocument->getId() . ' to ' . $sFilename); } $oDocument->setMinorVersionNumber($oDocument->getMinorVersionNumber()+1); } $sType = KTMime::getMimeTypeFromFile($sFilename); $iMimeTypeId = KTMime::getMimeTypeID($sType, $oDocument->getFileName()); $oDocument->setMimeTypeId($iMimeTypeId); $bSuccess = $oDocument->update(); if ($bSuccess !== true) { if (PEAR::isError($bSuccess)) { return $bSuccess; } return PEAR::raiseError(_kt('An error occurred while storing this document in the database')); } /* // create the document transaction record $oDocumentTransaction = new DocumentTransaction($oDocument, $sCheckInComment, 'ktcore.transactions.check_in'); $oDocumentTransaction->create(); $oKTTriggerRegistry = KTTriggerRegistry::getSingleton(); $aTriggers = $oKTTriggerRegistry->getTriggers('content', 'scan'); foreach ($aTriggers as $aTrigger) { $sTrigger = $aTrigger[0]; $oTrigger = new $sTrigger; $oTrigger->setDocument($oDocument); $ret = $oTrigger->scan(); if (PEAR::isError($ret)) { $oDocument->delete(); return $ret; } } // NEW SEARCH Indexer::index($oDocument); // fire subscription alerts for the checked in document $oSubscriptionEvent = new SubscriptionEvent(); $oFolder = Folder::get($oDocument->getFolderID()); $oSubscriptionEvent->CheckinDocument($oDocument, $oFolder); */ return true; } // {{{ validateMetadata function validateMetadata(&$oDocument, $aMetadata) { $aFieldsets =& KTFieldset::getGenericFieldsets(); $aFieldsets =& kt_array_merge($aFieldsets, KTFieldset::getForDocumentType($oDocument->getDocumentTypeId())); $aSimpleMetadata = array(); foreach ($aMetadata as $aSingleMetadatum) { list($oField, $sValue) = $aSingleMetadatum; if (is_null($oField)) { continue; } $aSimpleMetadata[$oField->getId()] = $sValue; } $aFailed = array(); foreach ($aFieldsets as $oFieldset) { $aFields =& $oFieldset->getFields(); $aFieldValues = array(); $isRealConditional = ($oFieldset->getIsConditional() && KTMetadataUtil::validateCompleteness($oFieldset)); foreach ($aFields as $oField) { $v = KTUtil::arrayGet($aSimpleMetadata, $oField->getId()); if ($oField->getIsMandatory() && !$isRealConditional) { if (empty($v)) { // XXX: What I'd do for a setdefault... $aFailed['field'][$oField->getId()] = 1; } } if (!empty($v)) { $aFieldValues[$oField->getId()] = $v; } } if ($isRealConditional) { $res = KTMetadataUtil::getNext($oFieldset, $aFieldValues); if ($res) { foreach ($res as $aMDSet) { if ($aMDSet['field']->getIsMandatory()) { $aFailed['fieldset'][$oFieldset->getId()] = 1; } } } } } if (!empty($aFailed)) { return new KTMetadataValidationError($aFailed); } return $aMetadata; } // }}} // {{{ saveMetadata function saveMetadata(&$oDocument, $aMetadata, $aOptions = null) { $table = 'document_fields_link'; $bNoValidate = KTUtil::arrayGet($aOptions, 'novalidate', false); if ($bNoValidate !== true) { $res = KTDocumentUtil::validateMetadata($oDocument, $aMetadata); if (PEAR::isError($res)) { return $res; } $aMetadata = empty($res)?array():$res; } $iMetadataVersionId = $oDocument->getMetadataVersionId(); $res = DBUtil::runQuery(array("DELETE FROM $table WHERE metadata_version_id = ?", array($iMetadataVersionId))); if (PEAR::isError($res)) { return $res; } // XXX: Metadata refactor foreach ($aMetadata as $aInfo) { list($oMetadata, $sValue) = $aInfo; if (is_null($oMetadata)) { continue; } $res = DBUtil::autoInsert($table, array( 'metadata_version_id' => $iMetadataVersionId, 'document_field_id' => $oMetadata->getID(), 'value' => $sValue, )); if (PEAR::isError($res)) { return $res; } } KTDocumentUtil::setComplete($oDocument, 'metadata'); DocumentFieldLink::clearAllCaches(); return true; } // }}} function copyMetadata($oDocument, $iPreviousMetadataVersionId) { $iNewMetadataVersion = $oDocument->getMetadataVersionId(); $sTable = KTUtil::getTableName('document_fields_link'); $aFields = DBUtil::getResultArray(array("SELECT * FROM $sTable WHERE metadata_version_id = ?", array($iPreviousMetadataVersionId))); foreach ($aFields as $aRow) { unset($aRow['id']); $aRow['metadata_version_id'] = $iNewMetadataVersion; DBUtil::autoInsert($sTable, $aRow); } } // {{{ setIncomplete function setIncomplete(&$oDocument, $reason) { $oDocument->setStatusID(STATUS_INCOMPLETE); $table = 'document_incomplete'; $iId = $oDocument->getId(); $aIncomplete = DBUtil::getOneResult(array("SELECT * FROM $table WHERE id = ?", array($iId))); if (PEAR::isError($aIncomplete)) { return $aIncomplete; } if (is_null($aIncomplete)) { $aIncomplete = array('id' => $iId); } $aIncomplete[$reason] = true; $res = DBUtil::autoDelete($table, $iId); if (PEAR::isError($res)) { return $res; } $res = DBUtil::autoInsert($table, $aIncomplete); if (PEAR::isError($res)) { return $res; } return true; } // }}} // {{{ setComplete function setComplete(&$oDocument, $reason) { $table = 'document_incomplete'; $iId = $oDocument->getID(); $aIncomplete = DBUtil::getOneResult(array("SELECT * FROM $table WHERE id = ?", array($iId))); if (PEAR::isError($aIncomplete)) { return $aIncomplete; } if (is_null($aIncomplete)) { $oDocument->setStatusID(LIVE); return true; } $aIncomplete[$reason] = false; $bIncomplete = false; foreach ($aIncomplete as $k => $v) { if ($k === 'id') { continue; } if ($v) { $bIncomplete = true; } } if ($bIncomplete === false) { DBUtil::autoDelete($table, $iId); $oDocument->setStatusID(LIVE); return true; } $res = DBUtil::autoDelete($table, $iId); if (PEAR::isError($res)) { return $res; } $res = DBUtil::autoInsert($table, $aIncomplete); if (PEAR::isError($res)) { return $res; } } // }}} // {{{ add function &add($oFolder, $sFilename, $oUser, $aOptions) { $GLOBALS['_IN_ADD'] = true; $ret = KTDocumentUtil::_in_add($oFolder, $sFilename, $oUser, $aOptions); unset($GLOBALS['_IN_ADD']); return $ret; } // }}} // {{{ _in_add function &_in_add($oFolder, $sFilename, $oUser, $aOptions) { $aOrigOptions = $aOptions; while(KTDocumentUtil::fileExists($oFolder, $sFilename)) { $oDoc = Document::getByFilenameAndFolder($sFilename, $oFolder->getId()); $sFilename = KTDocumentUtil::generateNewDocumentFilename($oDoc->getFileName()); } $sName = KTUtil::arrayGet($aOptions, 'description', $sFilename); while(KTDocumentUtil::nameExists($oFolder, $sName)) { $oDoc = Document::getByNameAndFolder($sName, $oFolder->getId()); $aOptions['description'] = KTDocumentUtil::generateNewDocumentName($oDoc->getName()); $sName = KTDocumentUtil::generateNewDocumentName($oDoc->getName()); } $oUploadChannel =& KTUploadChannel::getSingleton(); $oUploadChannel->sendMessage(new KTUploadNewFile($sFilename)); DBUtil::startTransaction(); $oDocument =& KTDocumentUtil::_add($oFolder, $sFilename, $oUser, $aOptions); $oUploadChannel->sendMessage(new KTUploadGenericMessage(_kt('Document created'))); if (PEAR::isError($oDocument)) { return $oDocument; } $oUploadChannel->sendMessage(new KTUploadGenericMessage(_kt('Scanning file'))); $oKTTriggerRegistry = KTTriggerRegistry::getSingleton(); $aTriggers = $oKTTriggerRegistry->getTriggers('content', 'scan'); $iTrigger = 0; foreach ($aTriggers as $aTrigger) { $sTrigger = $aTrigger[0]; $oTrigger = new $sTrigger; $oTrigger->setDocument($oDocument); // $oUploadChannel->sendMessage(new KTUploadGenericMessage(sprintf(_kt(" (trigger %s)"), $sTrigger))); $ret = $oTrigger->scan(); if (PEAR::isError($ret)) { $oDocument->delete(); return $ret; } } // NEW SEARCH Indexer::index($oDocument); $oUploadChannel->sendMessage(new KTUploadGenericMessage(_kt('Creating transaction'))); $aOptions = array('user' => $oUser); //create the document transaction record $oDocumentTransaction = new DocumentTransaction($oDocument, _kt('Document created'), 'ktcore.transactions.create', $aOptions); $res = $oDocumentTransaction->create(); if (PEAR::isError($res)) { $oDocument->delete(); return $res; } $oUploadChannel->sendMessage(new KTUploadGenericMessage(_kt('Sending subscriptions'))); // fire subscription alerts for the checked in document $oSubscriptionEvent = new SubscriptionEvent(); $oFolder = Folder::get($oDocument->getFolderID()); $oSubscriptionEvent->AddDocument($oDocument, $oFolder); $oKTTriggerRegistry = KTTriggerRegistry::getSingleton(); $aTriggers = $oKTTriggerRegistry->getTriggers('add', 'postValidate'); foreach ($aTriggers as $aTrigger) { $sTrigger = $aTrigger[0]; $oTrigger = new $sTrigger; $aInfo = array( 'document' => $oDocument, 'aOptions' => $aOrigOptions, ); $oTrigger->setInfo($aInfo); $ret = $oTrigger->postValidate(); } DBUtil::commit(); $oUploadChannel->sendMessage(new KTUploadGenericMessage(_kt('Checking permissions...'))); // Check if there are any dynamic conditions / permissions that need to be updated on the document // If there are dynamic conditions then update the permissions on the document // The dynamic condition test fails unless the document exists in the DB therefore update permissions after committing the transaction. include_once(KT_LIB_DIR.'/permissions/permissiondynamiccondition.inc.php'); $iPermissionObjectId = $oFolder->getPermissionObjectID(); $dynamicCondition = KTPermissionDynamicCondition::getByPermissionObjectId($iPermissionObjectId); if(!PEAR::isError($dynamicCondition) && !empty($dynamicCondition)){ $res = KTPermissionUtil::updatePermissionLookup($oDocument); } $oUploadChannel->sendMessage(new KTUploadGenericMessage(_kt('All done...'))); return $oDocument; } // }}} function generateNewDocumentFilename($sDocFilename){ if(preg_match("/\([0-9]+\)(\.[^\.]+){1,}$/", $sDocFilename)){ preg_match("/\([0-9]+\)\./", $sDocFilename, $matches); $new_one = substr($matches[0], 1); $new_two = explode(')', $new_one); $new = $new_two[0]+1; $pattern[0] = '/\([0-9]+\)\./'; $replacement[0] = ' ('.$new.').'; $sFilename = preg_replace($pattern, $replacement, $sDocFilename); }else{ $matches = explode('.', $sDocFilename); $prefix = $matches[0].' (2)'; for($i = 1; $i < count($matches); $i++ ){ $suffix .= '.'.$matches[$i]; } $sFilename = $prefix.$suffix; } return $sFilename; } function generateNewDocumentName($sDocName){ if(preg_match("/\([0-9]+\)$/", $sDocName)){ preg_match("/\([0-9]+\)$/", $sDocName, $matches); $new_one = substr($matches[0], 1); $new_two = explode(')', $new_one); $new = $new_two[0]+1; $pattern[0] = '/\([0-9]+\)$/'; $replacement[0] = '('.$new.')'; $sName = preg_replace($pattern, $replacement, $sDocName); }else{ $sName = $sDocName.' (2)'; } return $sName; } // {{{ fileExists function fileExists($oFolder, $sFilename) { return Document::fileExists($sFilename, $oFolder->getID()); } // }}} // {{{ nameExists function nameExists($oFolder, $sName) { return Document::nameExists($sName, $oFolder->getID()); } // }}} // {{{ storeContents /** * Stores contents (filelike) from source into the document storage */ function storeContents(&$oDocument, $oContents = null, $aOptions = null) { if (is_null($aOptions)) { $aOptions = array(); } if (PEAR::isError($oDocument)) { return PEAR::raiseError(sprintf(_kt("Couldn't store contents: %s"), $oDocument->getMessage())); } $bCanMove = KTUtil::arrayGet($aOptions, 'move'); $oStorage =& KTStorageManagerUtil::getSingleton(); $oKTConfig =& KTConfig::getSingleton(); $sBasedir = $oKTConfig->get('urls/tmpDirectory'); $sFilename = (isset($aOptions['temp_file'])) ? $aOptions['temp_file'] : ''; if(empty($sFilename)){ return PEAR::raiseError(sprintf(_kt("Couldn't store contents: %s"), _kt('The uploaded file does not exist.'))); } $md5hash = md5_file($sFilename); $content = $oDocument->_oDocumentContentVersion; $content->setStorageHash($md5hash); $content->update(); if (empty($aOptions)) $aOptions = array(); $aOptions['md5hash'] = $md5hash; // detection of mime types needs to be refactored. this stuff is damn messy! $sType = KTMime::getMimeTypeFromFile($sFilename); $iMimeTypeId = KTMime::getMimeTypeID($sType, $oDocument->getFileName(), $sFilename); $oDocument->setMimeTypeId($iMimeTypeId); $res = $oStorage->upload($oDocument, $sFilename, $aOptions); if ($res === false) { return PEAR::raiseError(sprintf(_kt("Couldn't store contents: %s"), _kt('No reason given'))); } if (PEAR::isError($res)) { return PEAR::raiseError(sprintf(_kt("Couldn't store contents: %s"), $res->getMessage())); } KTDocumentUtil::setComplete($oDocument, 'contents'); if ($aOptions['cleanup_initial_file'] && file_exists($sFilename)) { @unlink($sFilename); } return true; } // }}} // {{{ delete function delete($oDocument, $sReason, $iDestFolderId = null) { // use the deleteSymbolicLink function is this is a symlink if ($oDocument->isSymbolicLink()) { return KTDocumentUtil::deleteSymbolicLink($oDocument); } $oDocument =& KTUtil::getObject('Document', $oDocument); if (is_null($iDestFolderId)) { $iDestFolderId = $oDocument->getFolderID(); } $oStorageManager =& KTStorageManagerUtil::getSingleton(); global $default; if (count(trim($sReason)) == 0) { return PEAR::raiseError(_kt('Deletion requires a reason')); } if (PEAR::isError($oDocument) || ($oDocument == false)) { return PEAR::raiseError(_kt('Invalid document object.')); } if ($oDocument->getIsCheckedOut() == true) { return PEAR::raiseError(sprintf(_kt('The document is checked out and cannot be deleted: %s'), $oDocument->getName())); } if(!KTWorkflowUtil::actionEnabledForDocument($oDocument, 'ktcore.actions.document.delete')){ return PEAR::raiseError(_kt('Document cannot be deleted as it is restricted by the workflow.')); } // IF we're deleted ... if ($oDocument->getStatusID() == DELETED) { return true; } $oOrigFolder = Folder::get($oDocument->getFolderId()); DBUtil::startTransaction(); // flip the status id $oDocument->setStatusID(DELETED); // $iDestFolderId is DEPRECATED. $oDocument->setFolderID(null); $oDocument->setRestoreFolderId($oOrigFolder->getId()); $oDocument->setRestoreFolderPath(Folder::generateFolderIDs($oOrigFolder->getId())); $res = $oDocument->update(); if (PEAR::isError($res) || ($res == false)) { DBUtil::rollback(); return PEAR::raiseError(_kt('There was a problem deleting the document from the database.')); } // now move the document to the delete folder $res = $oStorageManager->delete($oDocument); if (PEAR::isError($res) || ($res == false)) { //could not delete the document from the file system $default->log->error('Deletion: Filesystem error deleting document ' . $oDocument->getFileName() . ' from folder ' . Folder::getFolderPath($oDocument->getFolderID()) . ' id=' . $oDocument->getFolderID()); // we use a _real_ transaction here ... DBUtil::rollback(); /* //reverse the document deletion $oDocument->setStatusID(LIVE); $oDocument->update(); */ return PEAR::raiseError(_kt('There was a problem deleting the document from storage.')); } //delete all shortcuts linking to this document $aSymlinks = $oDocument->getSymbolicLinks(); foreach($aSymlinks as $aSymlink){ $oShortcutDocument = Document::get($aSymlink['id']); $oOwnerUser = User::get($oShortcutDocument->getOwnerID()); KTDocumentUtil::deleteSymbolicLink($aSymlink['id']); //send an email to the owner of the shortcut if($oOwnerUser->getEmail()!=null && $oOwnerUser->getEmailNotification() == true){ $emailTemplate = new EmailTemplate("kt3/notifications/notification.SymbolicLinkDeleted",array('user_name'=>$this->oUser->getName(), 'url'=>KTUtil::ktLink(KTBrowseUtil::getUrlForDocument($oShortcutDocument)), 'title' =>$oShortcutDocument->getName())); $email = new EmailAlert($oOwnerUser->getEmail(),_kt("KnowledgeTree Notification"),$emailTemplate->getBody()); $email->send(); } } $oDocumentTransaction = new DocumentTransaction($oDocument, _kt('Document deleted: ') . $sReason, 'ktcore.transactions.delete'); $oDocumentTransaction->create(); $oDocument->setFolderID(1); DBUtil::commit(); // we weren't doing notifications on this one $oSubscriptionEvent = new SubscriptionEvent(); $oSubscriptionEvent->RemoveDocument($oDocument, $oOrigFolder); // document is now deleted: triggers are best-effort. $oKTTriggerRegistry = KTTriggerRegistry::getSingleton(); $aTriggers = $oKTTriggerRegistry->getTriggers('delete', 'postValidate'); foreach ($aTriggers as $aTrigger) { $sTrigger = $aTrigger[0]; $oTrigger = new $sTrigger; $aInfo = array( 'document' => $oDocument, ); $oTrigger->setInfo($aInfo); $ret = $oTrigger->postValidate(); if (PEAR::isError($ret)) { $oDocument->delete(); // FIXME nbm: review that on-fail => delete is correct ?! return $ret; } } } // }}} function reindexDocument($oDocument) { Indexer::index($oDocument); } function canBeMoved($oDocument) { if ($oDocument->getIsCheckedOut()) { return false; } if (!KTWorkflowUtil::actionEnabledForDocument($oDocument, 'ktcore.actions.document.move')) { return false; } return true; } function copy($oDocument, $oDestinationFolder, $sReason = null, $sDestinationDocName = null) { // 1. generate a new triad of content, metadata and core objects. // 2. update the storage path. //print '--------------------------------- BEFORE'; //print_r($oDocument); // grab the "source "data $sTable = KTUtil::getTableName('documents'); $sQuery = 'SELECT * FROM ' . $sTable . ' WHERE id = ?'; $aParams = array($oDocument->getId()); $aCoreRow = DBUtil::getOneResult(array($sQuery, $aParams)); unset($aCoreRow['id']); $aCoreRow['modified'] = date('Y-m-d H:i:s'); $aCoreRow['folder_id'] = $oDestinationFolder->getId(); // new location. $id = DBUtil::autoInsert($sTable, $aCoreRow); if (PEAR::isError($id)) { return $id; } // we still have a bogus md_version, but integrity holds, so fix it now. $oCore = KTDocumentCore::get($id); // Get the metadata version for the source document $sTable = KTUtil::getTableName('document_metadata_version'); $sQuery = 'SELECT * FROM ' . $sTable . ' WHERE id = ?'; $aParams = array($oDocument->getMetadataVersionId()); $aMDRow = DBUtil::getOneResult(array($sQuery, $aParams)); unset($aMDRow['id']); // Copy the source metadata into the destination document $aMDRow['document_id'] = $oCore->getId(); if(!empty($sDestinationDocName)){ $aMDRow['name'] = $sDestinationDocName; $aMDRow['description'] = $sDestinationDocName; } $id = DBUtil::autoInsert($sTable, $aMDRow); if (PEAR::isError($id)) { return $id; } $oCore->setMetadataVersionId($id); $oMDV = KTDocumentMetadataVersion::get($id); // Get the content version for the source document $sTable = KTUtil::getTableName('document_content_version'); $sQuery = 'SELECT * FROM ' . $sTable . ' WHERE id = ?'; $aParams = array($oDocument->_oDocumentContentVersion->getId()); $aContentRow = DBUtil::getOneResult(array($sQuery, $aParams)); unset($aContentRow['id']); // Copy the source content into the destination document $aContentRow['document_id'] = $oCore->getId(); if(!empty($sDestinationDocName)){ $aContentRow['filename'] = $sDestinationDocName; } $id = DBUtil::autoInsert($sTable, $aContentRow); if (PEAR::isError($id)) { return $id; } $oMDV->setContentVersionId($id); $res = $oCore->update(); if (PEAR::isError($res)) { return $res; } $res = $oMDV->update(); if (PEAR::isError($res)) { return $res; } // now, we have a semi-sane document object. get it. $oNewDocument = Document::get($oCore->getId()); //print '--------------------------------- AFTER'; //print_r($oDocument); //print '======'; //print_r($oNewDocument); // copy the metadata from old to new. $res = KTDocumentUtil::copyMetadata($oNewDocument, $oDocument->getMetadataVersionId()); if (PEAR::isError($res)) { return $res; } // Ensure the copied document is not checked out $oNewDocument->setIsCheckedOut(false); $oNewDocument->setCheckedOutUserID(-1); // finally, copy the actual file. $oStorage =& KTStorageManagerUtil::getSingleton(); $res = $oStorage->copy($oDocument, $oNewDocument); $oOriginalFolder = Folder::get($oDocument->getFolderId()); $iOriginalFolderPermissionObjectId = $oOriginalFolder->getPermissionObjectId(); $iDocumentPermissionObjectId = $oDocument->getPermissionObjectId(); if ($iDocumentPermissionObjectId === $iOriginalFolderPermissionObjectId) { $oNewDocument->setPermissionObjectId($oDestinationFolder->getPermissionObjectId()); } $res = $oNewDocument->update(); if (PEAR::isError($res)) { return $res; } KTPermissionUtil::updatePermissionLookup($oNewDocument); if (is_null($sReason)) { $sReason = ''; } $oDocumentTransaction = new DocumentTransaction($oDocument, sprintf(_kt("Copied to folder \"%s\". %s"), $oDestinationFolder->getName(), $sReason), 'ktcore.transactions.copy'); $oDocumentTransaction->create(); $oSrcFolder = Folder::get($oDocument->getFolderID()); $oDocumentTransaction = new DocumentTransaction($oNewDocument, sprintf(_kt("Copied from original in folder \"%s\". %s"), $oSrcFolder->getName(), $sReason), 'ktcore.transactions.copy'); $oDocumentTransaction->create(); $oKTTriggerRegistry = KTTriggerRegistry::getSingleton(); $aTriggers = $oKTTriggerRegistry->getTriggers('copyDocument', 'postValidate'); foreach ($aTriggers as $aTrigger) { $sTrigger = $aTrigger[0]; $oTrigger = new $sTrigger; $aInfo = array( 'document' => $oNewDocument, 'old_folder' => $oSrcFolder, 'new_folder' => $oDestinationFolder, ); $oTrigger->setInfo($aInfo); $ret = $oTrigger->postValidate(); if (PEAR::isError($ret)) { return $ret; } } // fire subscription alerts for the copied document $oSubscriptionEvent = new SubscriptionEvent(); $oFolder = Folder::get($oDocument->getFolderID()); $oSubscriptionEvent->MoveDocument($oDocument, $oDestinationFolder, $oSrcFolder, 'CopiedDocument'); return $oNewDocument; } function rename($oDocument, $sNewFilename, $oUser) { $oStorage =& KTStorageManagerUtil::getSingleton(); $iPreviousMetadataVersion = $oDocument->getMetadataVersionId(); $oOldContentVersion = $oDocument->_oDocumentContentVersion; $bSuccess = $oDocument->startNewContentVersion($oUser); if (PEAR::isError($bSuccess)) { return $bSuccess; } KTDocumentUtil::copyMetadata($oDocument, $iPreviousMetadataVersion); $res = $oStorage->renameDocument($oDocument, $oOldContentVersion, $sNewFilename); if (!$res) { return PEAR::raiseError(_kt('An error occurred while storing the new file')); } $oDocument->setLastModifiedDate(getCurrentDateTime()); $oDocument->setModifiedUserId($oUser->getId()); $oDocument->setMinorVersionNumber($oDocument->getMinorVersionNumber()+1); $oDocument->_oDocumentContentVersion->setFilename($sNewFilename); $sType = KTMime::getMimeTypeFromFile($sNewFilename); $iMimeTypeId = KTMime::getMimeTypeID($sType, $sNewFilename); $oDocument->setMimeTypeId($iMimeTypeId); $bSuccess = $oDocument->update(); if ($bSuccess !== true) { if (PEAR::isError($bSuccess)) { return $bSuccess; } return PEAR::raiseError(_kt('An error occurred while storing this document in the database')); } // create the document transaction record $oDocumentTransaction = new DocumentTransaction($oDocument, _kt('Document renamed'), 'ktcore.transactions.update'); $oDocumentTransaction->create(); // fire subscription alerts for the checked in document $oSubscriptionEvent = new SubscriptionEvent(); $oFolder = Folder::get($oDocument->getFolderID()); $oSubscriptionEvent->ModifyDocument($oDocument, $oFolder); return true; } function move($oDocument, $oToFolder, $oUser = null, $sReason = null) { //make sure we move the symlink, and the document it's linking to if($oDocument->isSymbolicLink()){ $oDocument->switchToRealCore(); }else{ $oDocument->switchToLinkedCore(); } $oFolder = $oToFolder; // alias. $oOriginalFolder = Folder::get($oDocument->getFolderId()); $iOriginalFolderPermissionObjectId = $oOriginalFolder->getPermissionObjectId(); $iDocumentPermissionObjectId = $oDocument->getPermissionObjectId(); if ($iDocumentPermissionObjectId === $iOriginalFolderPermissionObjectId) { $oDocument->setPermissionObjectId($oFolder->getPermissionObjectId()); } //put the document in the new folder $oDocument->setFolderID($oFolder->getId()); $res = $oDocument->update(); if (PEAR::isError($res)) { return $res; } //move the document on the file system(not if it's a symlink) if(!$oDocument->isSymbolicLink()){ $oStorage =& KTStorageManagerUtil::getSingleton(); $res = $oStorage->moveDocument($oDocument, $oFolder, $oOriginalFolder); if (PEAR::isError($res) || ($res === false)) { $oDocument->setFolderID($oOriginalFolder->getId()); $res = $oDocument->update(); if (PEAR::isError($res)) { return $res; } return $res; // we failed, bail. } } $sMoveMessage = sprintf(_kt("Moved from %s/%s to %s/%s. %s"), $oOriginalFolder->getFullPath(), $oOriginalFolder->getName(), $oFolder->getFullPath(), $oFolder->getName(), $sReason); // create the document transaction record $oDocumentTransaction = new DocumentTransaction($oDocument, $sMoveMessage, 'ktcore.transactions.move'); $oDocumentTransaction->create(); $oKTTriggerRegistry = KTTriggerRegistry::getSingleton(); $aTriggers = $oKTTriggerRegistry->getTriggers('moveDocument', 'postValidate'); foreach ($aTriggers as $aTrigger) { $sTrigger = $aTrigger[0]; $oTrigger = new $sTrigger; $aInfo = array( 'document' => $oDocument, 'old_folder' => $oOriginalFolder, 'new_folder' => $oFolder, ); $oTrigger->setInfo($aInfo); $ret = $oTrigger->postValidate(); if (PEAR::isError($ret)) { return $ret; } } // fire subscription alerts for the moved document $oSubscriptionEvent = new SubscriptionEvent(); $oSubscriptionEvent->MoveDocument($oDocument, $oFolder, $oOriginalFolder); return KTPermissionUtil::updatePermissionLookup($oDocument); } /** * Delete a selected version of the document. */ function deleteVersion($oDocument, $iVersionID, $sReason){ $oDocument =& KTUtil::getObject('Document', $oDocument); $oVersion =& KTDocumentMetadataVersion::get($iVersionID); $oStorageManager =& KTStorageManagerUtil::getSingleton(); global $default; if (empty($sReason)) { return PEAR::raiseError(_kt('Deletion requires a reason')); } if (PEAR::isError($oDocument) || ($oDocument == false)) { return PEAR::raiseError(_kt('Invalid document object.')); } if (PEAR::isError($oVersion) || ($oVersion == false)) { return PEAR::raiseError(_kt('Invalid document version object.')); } $iContentId = $oVersion->getContentVersionId(); $oContentVersion = KTDocumentContentVersion::get($iContentId); if (PEAR::isError($oContentVersion) || ($oContentVersion == false)) { DBUtil::rollback(); return PEAR::raiseError(_kt('Invalid document content version object.')); } DBUtil::startTransaction(); // now delete the document version $res = $oStorageManager->deleteVersion($oVersion); if (PEAR::isError($res) || ($res == false)) { //could not delete the document version from the file system $default->log->error('Deletion: Filesystem error deleting the metadata version ' . $oVersion->getMetadataVersion() . ' of the document ' . $oDocument->getFileName() . ' from folder ' . Folder::getFolderPath($oDocument->getFolderID()) . ' id=' . $oDocument->getFolderID()); // we use a _real_ transaction here ... DBUtil::rollback(); return PEAR::raiseError(_kt('There was a problem deleting the document from storage.')); } // change status for the metadata version $oVersion->setStatusId(VERSION_DELETED); $oVersion->update(); // set the storage path to empty // $oContentVersion->setStoragePath(''); DBUtil::commit(); } } class KTMetadataValidationError extends PEAR_Error { function KTMetadataValidationError ($aFailed) { $this->aFailed = $aFailed; $message = _kt('Please be sure to enter information for all the Required fields below'); parent::PEAR_Error($message); } } class KTUploadChannel { var $observers = array(); function &getSingleton() { if (!KTUtil::arrayGet($GLOBALS, 'KT_UploadChannel')) { $GLOBALS['KT_UploadChannel'] = new KTUploadChannel; } return $GLOBALS['KT_UploadChannel']; } function sendMessage(&$msg) { foreach ($this->observers as $oObserver) { $oObserver->receiveMessage($msg); } } function addObserver(&$obs) { array_push($this->observers, $obs); } } class KTUploadGenericMessage { function KTUploadGenericMessage($sMessage) { $this->sMessage = $sMessage; } function getString() { return $this->sMessage; } } class KTUploadNewFile { function KTUploadNewFile($sFilename) { $this->sFilename = $sFilename; } function getString() { return $this->sFilename; } } ?>