Commit 6c26629f2311c60ebda581542d7bf8f6a21d4c17
1 parent
5b892972
Merged in from DEV trunk...
KTS-3133 "Error in storage verification process" Updated. Committed By: Conrad Vermeulen Reviewed By: Megan Watson git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/STABLE/trunk@8890 c91229c3-7414-0410-bfa2-8a42b809f60b
Showing
1 changed file
with
140 additions
and
98 deletions
bin/storageverification.php
| 1 | <?php | 1 | <?php |
| 2 | /** | 2 | /** |
| 3 | * $Id$ | 3 | * $Id$ |
| 4 | - * | 4 | + * |
| 5 | * KnowledgeTree Community Edition | 5 | * KnowledgeTree Community Edition |
| 6 | * Document Management Made Simple | 6 | * Document Management Made Simple |
| 7 | * Copyright (C) 2008 KnowledgeTree Inc. | 7 | * Copyright (C) 2008 KnowledgeTree Inc. |
| 8 | * Portions copyright The Jam Warehouse Software (Pty) Limited | 8 | * Portions copyright The Jam Warehouse Software (Pty) Limited |
| 9 | - * | 9 | + * |
| 10 | * This program is free software; you can redistribute it and/or modify it under | 10 | * This program is free software; you can redistribute it and/or modify it under |
| 11 | * the terms of the GNU General Public License version 3 as published by the | 11 | * the terms of the GNU General Public License version 3 as published by the |
| 12 | * Free Software Foundation. | 12 | * Free Software Foundation. |
| 13 | - * | 13 | + * |
| 14 | * This program is distributed in the hope that it will be useful, but WITHOUT | 14 | * This program is distributed in the hope that it will be useful, but WITHOUT |
| 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 16 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | 16 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| 17 | * details. | 17 | * details. |
| 18 | - * | 18 | + * |
| 19 | * You should have received a copy of the GNU General Public License | 19 | * You should have received a copy of the GNU General Public License |
| 20 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 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 | * California 94120-7775, or email info@knowledgetree.com. | 23 | * California 94120-7775, or email info@knowledgetree.com. |
| 24 | - * | 24 | + * |
| 25 | * The interactive user interfaces in modified source and object code versions | 25 | * The interactive user interfaces in modified source and object code versions |
| 26 | * of this program must display Appropriate Legal Notices, as required under | 26 | * of this program must display Appropriate Legal Notices, as required under |
| 27 | * Section 5 of the GNU General Public License version 3. | 27 | * Section 5 of the GNU General Public License version 3. |
| 28 | - * | 28 | + * |
| 29 | * In accordance with Section 7(b) of the GNU General Public License version 3, | 29 | * In accordance with Section 7(b) of the GNU General Public License version 3, |
| 30 | * these Appropriate Legal Notices must retain the display of the "Powered by | 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 | * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices | 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 | * copyright notice. | 34 | * copyright notice. |
| 35 | * Contributor( s): ______________________________________ | 35 | * Contributor( s): ______________________________________ |
| 36 | */ | 36 | */ |
| 37 | 37 | ||
| 38 | require_once(dirname(__FILE__) . '/../config/dmsDefaults.php'); | 38 | require_once(dirname(__FILE__) . '/../config/dmsDefaults.php'); |
| 39 | -require_once(KT_LIB_DIR . '/dispatcher.inc.php'); | ||
| 40 | -$sectionName = 'Administration'; | ||
| 41 | -require_once(KT_LIB_DIR . '/templating/kt3template.inc.php'); | ||
| 42 | - | ||
| 43 | -require_once(KT_LIB_DIR . '/browse/browseutil.inc.php'); | ||
| 44 | - | ||
| 45 | -class VerifyDispatcher extends KTDispatcher { | ||
| 46 | - function VerifyDispatcher() { | ||
| 47 | - $this->aIgnore = array( | ||
| 48 | - '.', '..', | ||
| 49 | - 'CVS', | ||
| 50 | - '.empty', | ||
| 51 | - '.htaccess', | ||
| 52 | - '.cvsignore', | ||
| 53 | - '.svn', | ||
| 54 | - ); | ||
| 55 | - | ||
| 56 | - $oConfig =& KTConfig::getSingleton(); | ||
| 57 | - $this->fsPath = $oConfig->get('urls/documentRoot'); | ||
| 58 | - | ||
| 59 | - return parent::KTDispatcher(); | 39 | +require_once(KT_LIB_DIR . '/storage/storagemanager.inc.php'); |
| 40 | + | ||
| 41 | +// TODO: this does not verify files that are in the storage system, but not on the database. It is better that the storage system | ||
| 42 | +// be in sync with the database. However, we should have a better way to query the storage driver to give us a list of files. | ||
| 43 | + | ||
| 44 | +class StorageVerification | ||
| 45 | +{ | ||
| 46 | + private $count; | ||
| 47 | + private $lineCount; | ||
| 48 | + private $doc; | ||
| 49 | + const DOCS_PER_DOT = 100; | ||
| 50 | + const DOTS_PER_LINE = 80; | ||
| 51 | + private $nl; | ||
| 52 | + private $tab; | ||
| 53 | + | ||
| 54 | + private | ||
| 55 | + function error($msg) | ||
| 56 | + { | ||
| 57 | + $doc = $this->doc; | ||
| 58 | + $documentId = $doc->getId(); | ||
| 59 | + $path = $doc->getFullPath(); | ||
| 60 | + $filename = $doc->getFileName(); | ||
| 61 | + $storagePath = $doc->getStoragePath(); | ||
| 62 | + | ||
| 63 | + print "{$this->nl}{$this->nl}"; | ||
| 64 | + print "Problem with Document ID: {$documentId}{$this->nl}"; | ||
| 65 | + print "{$this->tab}Path: {$path}{$this->nl}"; | ||
| 66 | + print "{$this->tab}Filename: {$filename}{$this->nl}"; | ||
| 67 | + print "{$this->tab}StoragePath: {$storagePath}{$this->nl}"; | ||
| 68 | + print "{$this->tab}Problem: {$msg}{$this->nl}{$this->nl}"; | ||
| 69 | + flush(); | ||
| 70 | + $this->count = 0; | ||
| 71 | + $this->lineCount = 0; | ||
| 72 | + $this->clearCache(); | ||
| 60 | } | 73 | } |
| 61 | 74 | ||
| 62 | - function do_main() { | ||
| 63 | - global $aFoldersToRemove; | ||
| 64 | - global $aFilesToRemove; | ||
| 65 | - global $aRepoDocumentProblems; | ||
| 66 | - global $aRepoFolderProblems; | ||
| 67 | - global $aRepoVersionProblems; | 75 | + private |
| 76 | + function progress() | ||
| 77 | + { | ||
| 78 | + if ($this->count++ % StorageVerification::DOCS_PER_DOT == 0) | ||
| 79 | + { | ||
| 80 | + $this->lineCount++; | ||
| 81 | + print '.'; | ||
| 82 | + flush(); | ||
| 83 | + } | ||
| 68 | 84 | ||
| 85 | + if ($this->lineCount == StorageVerification::DOTS_PER_LINE ) | ||
| 86 | + { | ||
| 87 | + print "{$this->nl}"; | ||
| 88 | + flush(); | ||
| 89 | + $this->lineCount = 0; | ||
| 90 | + } | ||
| 91 | + $this->clearCache(); | ||
| 92 | + } | ||
| 69 | 93 | ||
| 70 | - $this->checkDirectory(''); | 94 | + private |
| 95 | + function clearCache() | ||
| 96 | + { | ||
| 97 | + $metadataid = $this->doc->getMetadataVersionId(); | ||
| 98 | + $contentid = $this->doc->getContentVersionId(); | ||
| 99 | + $iId = $this->doc->getId(); | ||
| 100 | + $cache = KTCache::getSingleton(); | ||
| 101 | + $cache->remove('KTDocumentMetadataVersion/id', $metadataid); | ||
| 102 | + $cache->remove('KTDocumentContentVersion/id', $contentid); | ||
| 103 | + $cache->remove('KTDocumentCore/id', $iId); | ||
| 104 | + $cache->remove('Document/id', $iId); | ||
| 105 | + unset($GLOBALS['_OBJECTCACHE']['KTDocumentMetadataVersion'][$metadataid]); | ||
| 106 | + unset($GLOBALS['_OBJECTCACHE']['KTDocumentContentVersion'][$contentid]); | ||
| 107 | + unset($GLOBALS['_OBJECTCACHE']['KTDocumentCore'][$iId]); | ||
| 108 | + | ||
| 109 | + unset($this->doc); | ||
| 110 | + } | ||
| 71 | 111 | ||
| 72 | - $aDocuments =& Document::getList(); | ||
| 73 | - foreach ($aDocuments as $oDocument) { | ||
| 74 | - $this->checkRepoDocument($oDocument); | 112 | + public |
| 113 | + function run() | ||
| 114 | + { | ||
| 115 | + global $argc; | ||
| 116 | + | ||
| 117 | + if (isset($argc)) | ||
| 118 | + { | ||
| 119 | + $this->nl = "\n"; | ||
| 120 | + $this->tab = "\t"; | ||
| 121 | + print "Storage Verification{$this->nl}"; | ||
| 122 | + print "===================={$this->nl}"; | ||
| 75 | } | 123 | } |
| 124 | + else | ||
| 125 | + { | ||
| 126 | + $this->nl = '<br>'; | ||
| 127 | + $this->tab = ' '; | ||
| 128 | + print "<b>Storage Verification</b>{$this->nl}"; | ||
| 76 | 129 | ||
| 77 | - if (!($this->aFilesToRemove or $this->aRepoDocumentProblems)) { | ||
| 78 | - return; | ||
| 79 | } | 130 | } |
| 80 | 131 | ||
| 81 | - $oTemplate =& | ||
| 82 | - $this->oValidator->validateTemplate('ktcore/document/cleanup_script'); | ||
| 83 | - $oTemplate->setData(array( | ||
| 84 | - 'aFilesToRemove' => $this->aFilesToRemove, | ||
| 85 | - 'aRepoDocumentProblems' => $this->aRepoDocumentProblems, | ||
| 86 | - )); | ||
| 87 | - print $oTemplate->render(); | ||
| 88 | - exit(0); | ||
| 89 | - } | ||
| 90 | 132 | ||
| 91 | - function checkDirectory($path) { | ||
| 92 | - $fullpath = sprintf('%s/%s', $this->fsPath, $path); | ||
| 93 | 133 | ||
| 94 | - if (!is_dir($fullpath)) { | ||
| 95 | - print "Not a directory: $fullpath\n"; | ||
| 96 | - } | 134 | + $sql = "SELECT |
| 135 | + dmv.id as metadata_version_id, dcv.document_id, dcv.md5hash, dcv.size | ||
| 136 | + FROM | ||
| 137 | + document_content_version dcv | ||
| 138 | + INNER JOIN document_metadata_version dmv ON dcv.id=dmv.content_version_id"; | ||
| 139 | + $rows = DBUtil::getResultArray($sql); | ||
| 140 | + $this->count = 0; | ||
| 141 | + $this->lineCount = 0; | ||
| 97 | 142 | ||
| 98 | - $dh = @opendir($fullpath); | ||
| 99 | - if ($dh === false) { | ||
| 100 | - print "Could not open directory: $fullpath\n"; | ||
| 101 | - } | ||
| 102 | - while (($filename = readdir($dh)) !== false) { | ||
| 103 | - if (in_array($filename, $this->aIgnore)) { continue; } | ||
| 104 | - $subrelpath = sprintf('%s/%s', $path, $filename); | ||
| 105 | - if (substr($subrelpath, 0, 1) == '/') { | ||
| 106 | - $subrelpath = substr($subrelpath, 1); | ||
| 107 | - } | ||
| 108 | - $subfullpath = sprintf('%s/%s', $this->fsPath, $subrelpath); | ||
| 109 | - if (is_dir($subfullpath)) { | ||
| 110 | - $this->checkDirectory($subrelpath); | 143 | + $storage =& KTStorageManagerUtil::getSingleton(); |
| 144 | + foreach($rows as $row) | ||
| 145 | + { | ||
| 146 | + $doc = Document::get($row['document_id'], $row['metadata_version_id']); | ||
| 147 | + | ||
| 148 | + if (PEAR::isError($doc)) | ||
| 149 | + { | ||
| 150 | + $msg = $doc->getMessage(); | ||
| 151 | + $this->error($doc, "Error with document: {$msg}"); | ||
| 152 | + continue; | ||
| 111 | } | 153 | } |
| 112 | - if (is_file($subfullpath)) { | ||
| 113 | - $this->checkFile($subrelpath); | 154 | + $this->doc = $doc; |
| 155 | + | ||
| 156 | + $tmpPath = $storage->temporaryFile($doc); | ||
| 157 | + if (!file_exists($tmpPath)) | ||
| 158 | + { | ||
| 159 | + $this->error("Temporary file could not be resolved: {$tmpPath}"); | ||
| 160 | + continue; | ||
| 114 | } | 161 | } |
| 115 | - } | ||
| 116 | - } | ||
| 117 | 162 | ||
| 118 | - function checkFile($path, $first = true) { | ||
| 119 | - $oDocument = KTEntityUtil::getByDict('KTDocumentContentVersion', array( | ||
| 120 | - 'storage_path' => $path, | ||
| 121 | - )); | ||
| 122 | - if (is_a($oDocument, 'ktentitynoobjects')) { | ||
| 123 | - $this->aFilesToRemove[] = $path; | ||
| 124 | - return; | ||
| 125 | - } | ||
| 126 | - } | 163 | + $expectedSize = $row['size']; |
| 164 | + $currentSize = filesize($tmpPath); | ||
| 165 | + if ($expectedSize != $currentSize) | ||
| 166 | + { | ||
| 167 | + $this->error("Filesize does not match. Expected: {$expectedSize} Current: {$currentSize}"); | ||
| 168 | + continue; | ||
| 169 | + } | ||
| 127 | 170 | ||
| 128 | - function checkRepoDocument($oDocument) { | ||
| 129 | - global $aRepoDocumentProblems; | ||
| 130 | - $aDCVs = KTDocumentContentVersion::getByDocument($oDocument); | ||
| 131 | - foreach ($aDCVs as $oDCV) { | ||
| 132 | - $sDocumentPath = $oDCV->getStoragePath(); | ||
| 133 | - $sFullPath = sprintf('%s/%s', $this->fsPath, $sDocumentPath); | ||
| 134 | - if (!is_file($sFullPath)) { | ||
| 135 | - $this->aRepoDocumentProblems[] = array( | ||
| 136 | - 'document' => $oDocument, | ||
| 137 | - 'content' => $oDCV, | ||
| 138 | - 'path' => $sDocumentPath, | ||
| 139 | - 'doclink' => KTBrowseUtil::getUrlForDocument($oDocument), | ||
| 140 | - ); | 171 | + $expectedHash = $row['md5hash']; |
| 172 | + $currentHash = md5_file($tmpPath); | ||
| 173 | + if ($expectedHash != $currentHash) | ||
| 174 | + { | ||
| 175 | + $this->error("Hash does not match. Expected: {$expectedHash} Current: {$currentHash}"); | ||
| 176 | + continue; | ||
| 141 | } | 177 | } |
| 178 | + $this->progress(); | ||
| 142 | } | 179 | } |
| 180 | + | ||
| 181 | + print "{$this->nl}Done.{$this->nl}{$this->nl}"; | ||
| 143 | } | 182 | } |
| 183 | + | ||
| 144 | } | 184 | } |
| 145 | -$oDispatcher = new VerifyDispatcher; | ||
| 146 | -$oDispatcher->do_main(); | 185 | + |
| 186 | + | ||
| 187 | +$verification = new StorageVerification(); | ||
| 188 | +$verification->run(); | ||
| 147 | 189 | ||
| 148 | ?> | 190 | ?> |