Commit bfba4de4c574e65a024938e17a7c0623b6a4996f
1 parent
19c6b514
KTS-1613
"possibility to delete a document version" Added a new transaction type for deleting a version of a document. The physical document is deleted but the DB record remains. Committed by: Megan Watson Reviewed by: Conrad Vermeulen git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@7270 c91229c3-7414-0410-bfa2-8a42b809f60b
Showing
11 changed files
with
265 additions
and
8 deletions
lib/documentmanagement/Document.inc
| ... | ... | @@ -34,6 +34,7 @@ DEFINE("PUBLISHED", 2); |
| 34 | 34 | DEFINE("DELETED", 3); |
| 35 | 35 | DEFINE("ARCHIVED", 4); |
| 36 | 36 | DEFINE("STATUS_INCOMPLETE", 5); |
| 37 | +DEFINE("VERSION_DELETED", 6); | |
| 37 | 38 | |
| 38 | 39 | require_once(KT_LIB_DIR . "/foldermanagement/Folder.inc"); |
| 39 | 40 | require_once(KT_LIB_DIR . '/documentmanagement/documentcontentversion.inc.php'); |
| ... | ... | @@ -115,6 +116,9 @@ class Document { |
| 115 | 116 | function getStatusID() { return $this->_oDocumentCore->getStatusId(); } |
| 116 | 117 | function setStatusID($iNewValue) { $this->_oDocumentMetadataVersion->setStatusId($iNewValue); $this->_oDocumentCore->setStatusId($iNewValue); } |
| 117 | 118 | |
| 119 | + function getMetadataStatusID() { return $this->_oDocumentMetadataVersion->getStatusId(); } | |
| 120 | + function setMetadataStatusID($iNewValue) { $this->_oDocumentMetadataVersion->setStatusId($iNewValue); } | |
| 121 | + | |
| 118 | 122 | function getMetadataVersion() { return $this->_oDocumentMetadataVersion->getMetadataVersion(); } |
| 119 | 123 | function setMetadataVersion($iNewValue) { $this->_oDocumentMetadataVersion->getMetadataVersion($iNewValue); } |
| 120 | 124 | ... | ... |
lib/documentmanagement/PhysicalDocumentManager.inc
| ... | ... | @@ -284,6 +284,36 @@ class PhysicalDocumentManager { |
| 284 | 284 | } |
| 285 | 285 | } |
| 286 | 286 | |
| 287 | + /** | |
| 288 | + * Delete a single version of a document | |
| 289 | + * | |
| 290 | + * return boolean true on successful delete, false otherwise | |
| 291 | + */ | |
| 292 | + function deleteVersion($oVersion) { | |
| 293 | + global $default; | |
| 294 | + $iContentId = $oVersion->getContentVersionId(); | |
| 295 | + $oContentVersion = KTDocumentContentVersion::get($iContentId); | |
| 296 | + | |
| 297 | + $sFullPath = $default->documentRoot.'/'.$oContentVersion->getStoragePath(); | |
| 298 | + | |
| 299 | + if (file_exists($sFullPath)) { | |
| 300 | + if(@unlink($sFullPath)){ | |
| 301 | + $default->log->info("PhysicalDocumentManager::deleteVersion unlinked $sFullPath"); | |
| 302 | + return true; | |
| 303 | + }else{ | |
| 304 | + $default->log->info("PhysicalDocumentManager::deleteVersion couldn't unlink $sFullPath"); | |
| 305 | + if (file_exists($sFullPath)) { | |
| 306 | + return false; | |
| 307 | + } else { | |
| 308 | + return true; | |
| 309 | + } | |
| 310 | + } | |
| 311 | + }else{ | |
| 312 | + $default->log->info("PhysicalDocumentManager::deleteVersion can't rm $sFullPath because it doesn't exist"); | |
| 313 | + return true; | |
| 314 | + } | |
| 315 | + } | |
| 316 | + | |
| 287 | 317 | /** |
| 288 | 318 | * Restore a document from the Deleted/ folder to the specified folder |
| 289 | 319 | * | ... | ... |
lib/documentmanagement/documentutil.inc.php
| ... | ... | @@ -1013,7 +1013,67 @@ class KTDocumentUtil { |
| 1013 | 1013 | |
| 1014 | 1014 | return KTPermissionUtil::updatePermissionLookup($oDocument); |
| 1015 | 1015 | } |
| 1016 | + | |
| 1017 | + /** | |
| 1018 | + * Delete a selected version of the document. | |
| 1019 | + */ | |
| 1020 | + function deleteVersion($oDocument, $iVersionID, $sReason){ | |
| 1021 | + | |
| 1022 | + $oDocument =& KTUtil::getObject('Document', $oDocument); | |
| 1023 | + $oVersion =& KTDocumentMetadataVersion::get($iVersionID); | |
| 1024 | + | |
| 1025 | + $oStorageManager =& KTStorageManagerUtil::getSingleton(); | |
| 1026 | + | |
| 1027 | + global $default; | |
| 1028 | + | |
| 1029 | + if (empty($sReason)) { | |
| 1030 | + return PEAR::raiseError(_kt('Deletion requires a reason')); | |
| 1031 | + } | |
| 1032 | + | |
| 1033 | + if (PEAR::isError($oDocument) || ($oDocument == false)) { | |
| 1034 | + return PEAR::raiseError(_kt('Invalid document object.')); | |
| 1035 | + } | |
| 1036 | + | |
| 1037 | + if (PEAR::isError($oVersion) || ($oVersion == false)) { | |
| 1038 | + return PEAR::raiseError(_kt('Invalid document version object.')); | |
| 1039 | + } | |
| 1040 | + | |
| 1041 | + $iContentId = $oVersion->getContentVersionId(); | |
| 1042 | + $oContentVersion = KTDocumentContentVersion::get($iContentId); | |
| 1043 | + | |
| 1044 | + if (PEAR::isError($oContentVersion) || ($oContentVersion == false)) { | |
| 1045 | + DBUtil::rollback(); | |
| 1046 | + return PEAR::raiseError(_kt('Invalid document content version object.')); | |
| 1047 | + } | |
| 1016 | 1048 | |
| 1049 | + DBUtil::startTransaction(); | |
| 1050 | + | |
| 1051 | + // now delete the document version | |
| 1052 | + $res = $oStorageManager->deleteVersion($oVersion); | |
| 1053 | + if (PEAR::isError($res) || ($res == false)) { | |
| 1054 | + //could not delete the document version from the file system | |
| 1055 | + $default->log->error('Deletion: Filesystem error deleting the metadata version ' . | |
| 1056 | + $oVersion->getMetadataVersion() . ' of the document ' . | |
| 1057 | + $oDocument->getFileName() . ' from folder ' . | |
| 1058 | + Folder::getFolderPath($oDocument->getFolderID()) . | |
| 1059 | + ' id=' . $oDocument->getFolderID()); | |
| 1060 | + | |
| 1061 | + // we use a _real_ transaction here ... | |
| 1062 | + | |
| 1063 | + DBUtil::rollback(); | |
| 1064 | + | |
| 1065 | + return PEAR::raiseError(_kt('There was a problem deleting the document from storage.')); | |
| 1066 | + } | |
| 1067 | + | |
| 1068 | + // change status for the metadata version | |
| 1069 | + $oVersion->setStatusId(VERSION_DELETED); | |
| 1070 | + $oVersion->update(); | |
| 1071 | + | |
| 1072 | + // set the storage path to empty | |
| 1073 | +// $oContentVersion->setStoragePath(''); | |
| 1074 | + | |
| 1075 | + DBUtil::commit(); | |
| 1076 | + } | |
| 1017 | 1077 | } |
| 1018 | 1078 | |
| 1019 | 1079 | class KTMetadataValidationError extends PEAR_Error { | ... | ... |
lib/storage/ondiskhashedstoragemanager.inc.php
| ... | ... | @@ -302,6 +302,25 @@ class KTOnDiskHashedStorageManager extends KTStorageManager { |
| 302 | 302 | return true; |
| 303 | 303 | } |
| 304 | 304 | |
| 305 | + /** | |
| 306 | + * Completely remove a document version | |
| 307 | + * | |
| 308 | + * return boolean true on successful delete | |
| 309 | + */ | |
| 310 | + function deleteVersion($oVersion) { | |
| 311 | + $oConfig =& KTConfig::getSingleton(); | |
| 312 | + $sDocumentRoot = $oConfig->get('urls/documentRoot'); | |
| 313 | + $iContentId = $oVersion->getContentVersionId(); | |
| 314 | + $oContentVersion = KTDocumentContentVersion::get($iContentId); | |
| 315 | + | |
| 316 | + $sPath = $oContentVersion->getStoragePath(); | |
| 317 | + $sFullPath = sprintf("%s/%s", $sDocumentRoot, $sPath); | |
| 318 | + if (file_exists($sFullPath)) { | |
| 319 | + unlink($sFullPath); | |
| 320 | + } | |
| 321 | + return true; | |
| 322 | + } | |
| 323 | + | |
| 305 | 324 | function restore($oDocument) { |
| 306 | 325 | // Storage doesn't care if the document is deleted or restored |
| 307 | 326 | return true; | ... | ... |
lib/storage/ondiskpathstoragemanager.inc.php
| ... | ... | @@ -399,6 +399,25 @@ class KTOnDiskPathStorageManager extends KTStorageManager { |
| 399 | 399 | } |
| 400 | 400 | return true; |
| 401 | 401 | } |
| 402 | + | |
| 403 | + /** | |
| 404 | + * Completely remove a document version | |
| 405 | + * | |
| 406 | + * return boolean true on successful delete | |
| 407 | + */ | |
| 408 | + function deleteVersion($oVersion) { | |
| 409 | + $oConfig =& KTConfig::getSingleton(); | |
| 410 | + $sDocumentRoot = $oConfig->get('urls/documentRoot'); | |
| 411 | + $iContentId = $oVersion->getContentVersionId(); | |
| 412 | + $oContentVersion = KTDocumentContentVersion::get($iContentId); | |
| 413 | + | |
| 414 | + $sPath = $oContentVersion->getStoragePath(); | |
| 415 | + $sFullPath = sprintf("%s/%s", $sDocumentRoot, $sPath); | |
| 416 | + if (file_exists($sFullPath)) { | |
| 417 | + unlink($sFullPath); | |
| 418 | + } | |
| 419 | + return true; | |
| 420 | + } | |
| 402 | 421 | |
| 403 | 422 | /** |
| 404 | 423 | * Restore a document from the Deleted/ folder to the specified folder | ... | ... |
lib/storage/storagemanager.inc.php
| ... | ... | @@ -125,6 +125,10 @@ class KTStorageManager { |
| 125 | 125 | $indexer->deleteDocument($documentid); |
| 126 | 126 | } |
| 127 | 127 | |
| 128 | + function deleteVersion(&$oVersion) { | |
| 129 | + return PEAR::raiseError(_kt("Not implemented")); | |
| 130 | + } | |
| 131 | + | |
| 128 | 132 | /** |
| 129 | 133 | * Performs any storage changes necessary to account for the |
| 130 | 134 | * document (previously marked as deleted) being restored. | ... | ... |
plugins/ktcore/KTDocumentActions.php
| ... | ... | @@ -114,6 +114,8 @@ class KTDocumentVersionHistoryAction extends KTDocumentAction { |
| 114 | 114 | } |
| 115 | 115 | |
| 116 | 116 | function do_main() { |
| 117 | + $show_version = KTUtil::arrayGet($_REQUEST, 'show'); | |
| 118 | + $showall = (isset($show_version) && ($show_version == 'all')) ? true : false; | |
| 117 | 119 | |
| 118 | 120 | $this->oPage->setSecondaryTitle($this->oDocument->getName()); |
| 119 | 121 | $this->oPage->setBreadcrumbDetails(_kt('Version History')); |
| ... | ... | @@ -121,7 +123,12 @@ class KTDocumentVersionHistoryAction extends KTDocumentAction { |
| 121 | 123 | $aMetadataVersions = KTDocumentMetadataVersion::getByDocument($this->oDocument); |
| 122 | 124 | $aVersions = array(); |
| 123 | 125 | foreach ($aMetadataVersions as $oVersion) { |
| 124 | - $aVersions[] = Document::get($this->oDocument->getId(), $oVersion->getId()); | |
| 126 | + $version = Document::get($this->oDocument->getId(), $oVersion->getId()); | |
| 127 | + if($showall){ | |
| 128 | + $aVersions[] = $version; | |
| 129 | + }else if($version->getMetadataStatusID() != VERSION_DELETED){ | |
| 130 | + $aVersions[] = $version; | |
| 131 | + } | |
| 125 | 132 | } |
| 126 | 133 | |
| 127 | 134 | // render pass. |
| ... | ... | @@ -134,12 +141,24 @@ class KTDocumentVersionHistoryAction extends KTDocumentAction { |
| 134 | 141 | |
| 135 | 142 | $oAction->setDocument($this->oDocument); |
| 136 | 143 | |
| 144 | + // create delete action if user is sys admin or folder admin | |
| 145 | + $bShowDelete = false; | |
| 146 | + require_once(KT_LIB_DIR . '/security/Permission.inc'); | |
| 147 | + $oUser =& User::get($_SESSION['userID']); | |
| 148 | + $iFolderId = $this->oDocument->getFolderId(); | |
| 149 | + if (Permission::userIsSystemAdministrator($oUser) || Permission::isUnitAdministratorForFolder($oUser, $iFolderId)) { | |
| 150 | + // Check if admin mode is enabled | |
| 151 | + $bShowDelete = KTUtil::arrayGet($_SESSION, 'adminmode', false); | |
| 152 | + } | |
| 153 | + | |
| 137 | 154 | $aTemplateData = array( |
| 138 | 155 | 'context' => $this, |
| 139 | 156 | 'document_id' => $this->oDocument->getId(), |
| 140 | 157 | 'document' => $this->oDocument, |
| 141 | 158 | 'versions' => $aVersions, |
| 142 | 159 | 'downloadaction' => $oAction, |
| 160 | + 'showdelete' => $bShowDelete, | |
| 161 | + 'showall' => $showall, | |
| 143 | 162 | ); |
| 144 | 163 | return $oTemplate->render($aTemplateData); |
| 145 | 164 | } |
| ... | ... | @@ -195,12 +214,50 @@ class KTDocumentVersionHistoryAction extends KTDocumentAction { |
| 195 | 214 | redirect(KTUtil::ktLink('view.php',null,implode('&', $frag))); |
| 196 | 215 | } |
| 197 | 216 | |
| 198 | - | |
| 199 | 217 | function getUserForId($iUserId) { |
| 200 | 218 | $u = User::get($iUserId); |
| 201 | 219 | if (PEAR::isError($u) || ($u == false)) { return _kt('User no longer exists'); } |
| 202 | 220 | return $u->getName(); |
| 203 | 221 | } |
| 222 | + | |
| 223 | + function do_confirmdeleteVersion() { | |
| 224 | + $this->oPage->setSecondaryTitle($this->oDocument->getName()); | |
| 225 | + $this->oPage->setBreadcrumbDetails(_kt('Delete document version')); | |
| 226 | + | |
| 227 | + // Display the version name and number | |
| 228 | + $iVersionId = $_REQUEST['version']; | |
| 229 | + $oVersion = Document::get($this->oDocument->getId(), $iVersionId); | |
| 230 | + | |
| 231 | + $oTemplating =& KTTemplating::getSingleton(); | |
| 232 | + $oTemplate = $oTemplating->loadTemplate('ktcore/document/delete_version'); | |
| 233 | + $aTemplateData = array( | |
| 234 | + 'context' => $this, | |
| 235 | + 'fDocumentId' => $this->oDocument->getId(), | |
| 236 | + 'oVersion' => $oVersion, | |
| 237 | + ); | |
| 238 | + return $oTemplate->render($aTemplateData); | |
| 239 | + } | |
| 240 | + | |
| 241 | + function do_deleteVersion() { | |
| 242 | + $iVersionId = $_REQUEST['versionid']; | |
| 243 | + $sReason = $_REQUEST['reason']; | |
| 244 | + $oVersion = Document::get($this->oDocument->getId(), $iVersionId); | |
| 245 | + | |
| 246 | + $res = KTDocumentUtil::deleteVersion($this->oDocument, $iVersionId, $sReason); | |
| 247 | + | |
| 248 | + if(PEAR::isError($res)){ | |
| 249 | + $this->addErrorMessage($res->getMessage()); | |
| 250 | + redirect(KTDocumentAction::getURL()); | |
| 251 | + exit(0); | |
| 252 | + } | |
| 253 | + | |
| 254 | + // Record the transaction | |
| 255 | + $aOptions['version'] = sprintf('%d.%d', $oVersion->getMajorVersionNumber(), $oVersion->getMinorVersionNumber()); | |
| 256 | + $oDocumentTransaction = & new DocumentTransaction($this->oDocument, _kt('Document version deleted'), 'ktcore.transactions.delete_version', $aOptions); | |
| 257 | + $oDocumentTransaction->create(); | |
| 258 | + | |
| 259 | + redirect(KTDocumentAction::getURL()); | |
| 260 | + } | |
| 204 | 261 | } |
| 205 | 262 | // }}} |
| 206 | 263 | |
| ... | ... | @@ -225,7 +282,7 @@ class KTDocumentViewAction extends KTDocumentAction { |
| 225 | 282 | $iVersion = KTUtil::arrayGet($_REQUEST, 'version'); |
| 226 | 283 | if ($iVersion) { |
| 227 | 284 | $oVersion = KTDocumentContentVersion::get($iVersion); |
| 228 | - $aOptions['version'] = sprintf('%d.%d', $oVersion->getMajorVersionNumber(), $oVersion->getMinorVersionNumber());; | |
| 285 | + $aOptions['version'] = sprintf('%d.%d', $oVersion->getMajorVersionNumber(), $oVersion->getMinorVersionNumber()); | |
| 229 | 286 | $res = $oStorage->downloadVersion($this->oDocument, $iVersion); |
| 230 | 287 | } else { |
| 231 | 288 | $res = $oStorage->download($this->oDocument); | ... | ... |
sql/mysql/install/data.sql
No preview for this file type
sql/mysql/upgrade/3.5.0/document_transaction_type.sql
0 → 100644
templates/ktcore/document/delete_version.smarty
0 → 100644
| 1 | +<h3>{i18n}Delete Document Version{/i18n}</h3> | |
| 2 | + | |
| 3 | +<span class="descriptiveText">{i18n}On deleting a document version the version history will remain but the document will be <b>permanently</b> deleted.{/i18n}</span> | |
| 4 | +<p>{i18n}The following document version has been selected for deletion:{/i18n} </p> | |
| 5 | + | |
| 6 | +<table> | |
| 7 | +<tr> | |
| 8 | +<td>Document Version: </td><td>{$oVersion->getName()}</td> | |
| 9 | +</tr><tr> | |
| 10 | +<td>Metadata version: </td><td>{$oVersion->getMetadataVersion()}</td> | |
| 11 | +</tr><tr> | |
| 12 | +<td>Content version: </td><td>{$oVersion->getMajorVersionNumber()}.{$oVersion->getMinorVersionNumber()}</td> | |
| 13 | +</tr><tr> | |
| 14 | +<td>Date created: </td><td>{$oVersion->getVersionCreated()}</td> | |
| 15 | +</tr></table> | |
| 16 | + | |
| 17 | +<p> | |
| 18 | +<form action="{$smarty.server.PHP_SELF}" method="POST" /> | |
| 19 | +<input type="hidden" name="action" value="deleteVersion" /> | |
| 20 | +<input type="hidden" name="fDocumentId" value="{$fDocumentId}" /> | |
| 21 | +<input type="hidden" name="versionid" value="{$oVersion->getCurrentMetadataVersionId()}" /> | |
| 22 | +<b>Reason:</b> | |
| 23 | +<br /><textarea rows="10" cols="50" name="reason"></textarea> | |
| 24 | +<br /><input type="submit" value="continue" /> | |
| 25 | +</form> | |
| 26 | +</p> | |
| 0 | 27 | \ No newline at end of file | ... | ... |
templates/ktcore/document/metadata_history.smarty
| ... | ... | @@ -13,6 +13,9 @@ |
| 13 | 13 | <th class="compare">{i18n}Compare with Current{/i18n}</th> |
| 14 | 14 | <th class="compare">{i18n}Compare with Other Version{/i18n}</th> |
| 15 | 15 | <th class="versiondate">{i18n}Date Created{/i18n}</th> |
| 16 | + {if $showdelete} | |
| 17 | + <th class="deleteversion">{i18n}Delete Version{/i18n}</th> | |
| 18 | + {/if} | |
| 16 | 19 | </tr> |
| 17 | 20 | </thead> |
| 18 | 21 | <tbody> |
| ... | ... | @@ -22,24 +25,57 @@ |
| 22 | 25 | <td class="date">{$oVersion->getMetadataVersion()}</td> |
| 23 | 26 | {capture assign=version}{$oVersion->getMajorVersionNumber()}.{$oVersion->getMinorVersionNumber()}{/capture} |
| 24 | 27 | {capture assign=versionid}{$oVersion->getContentVersionId()}{/capture} |
| 25 | - <td class="date"><a href="{$downloadaction->getURL()}&version={$versionid}">{$version}</a></td> | |
| 28 | + <td class="date"> | |
| 29 | + {if ($oVersion->getMetadataStatusID() != VERSION_DELETED)} | |
| 30 | + <a href="{$downloadaction->getURL()}&version={$versionid}">{$version}</a> | |
| 31 | + {else} | |
| 32 | + {$version} | |
| 33 | + {/if} | |
| 34 | + </td> | |
| 26 | 35 | <td class="compare"> |
| 27 | 36 | {if ($document->getMetadataVersion() == $oVersion->getMetadataVersion())} |
| 28 | 37 | <strong>{i18n}current version{/i18n}</strong> |
| 29 | 38 | {else} |
| 30 | - <a href="{addQS}action=viewComparison&fDocumentId={$document->getId()}&fBaseVersion={$oVersion->getMetadataVersionId()}&fComparisonVersion={$oVersion->getCurrentMetadataVersionId()}{/addQS}">{i18n}compare with current{/i18n}</a></td> | |
| 39 | + {if ($oVersion->getMetadataStatusID() != VERSION_DELETED)} | |
| 40 | + <a href="{addQS}action=viewComparison&fDocumentId={$document->getId()}&fBaseVersion={$oVersion->getMetadataVersionId()}&fComparisonVersion={$oVersion->getCurrentMetadataVersionId()}{/addQS}">{i18n}compare with current{/i18n}</a></td> | |
| 41 | + {else} | |
| 42 | + — | |
| 43 | + {/if} | |
| 31 | 44 | {/if} |
| 32 | 45 | </td> |
| 33 | 46 | <td> |
| 34 | 47 | {if (count($versions) == 1)} |
| 35 | 48 | — |
| 36 | 49 | {else} |
| 37 | - <a href="{addQS}action=startComparison&fDocumentId={$document->getId()}&fComparisonVersion={$oVersion->getCurrentMetadataVersionId()}{/addQS}">{i18n}compare with other version{/i18n}</a> | |
| 50 | + {if ($oVersion->getMetadataStatusID() != VERSION_DELETED)} | |
| 51 | + <a href="{addQS}action=startComparison&fDocumentId={$document->getId()}&fComparisonVersion={$oVersion->getCurrentMetadataVersionId()}{/addQS}">{i18n}compare with other version{/i18n}</a> | |
| 52 | + {else} | |
| 53 | + — | |
| 54 | + {/if} | |
| 38 | 55 | {/if} |
| 39 | 56 | </td> |
| 40 | 57 | <td class="versiondate">{$oVersion->getVersionCreated()}</td> |
| 58 | + {if $showdelete} | |
| 59 | + <td class="deleteversion"> | |
| 60 | + {if ($document->getMetadataVersion() != $oVersion->getMetadataVersion())} | |
| 61 | + {if ($oVersion->getMetadataStatusID() == VERSION_DELETED)} | |
| 62 | + <strong>{i18n}Version deleted{/i18n}</strong> | |
| 63 | + {else} | |
| 64 | + <a href="{addQS}action=confirmdeleteVersion&fDocumentId={$document->getId()}&version={$oVersion->getCurrentMetadataVersionId()}{/addQS}">{i18n}delete version{/i18n}</a> | |
| 65 | + {/if} | |
| 66 | + {else} | |
| 67 | + | |
| 68 | + {/if} | |
| 69 | + </td> | |
| 70 | + {/if} | |
| 41 | 71 | </tr> |
| 42 | 72 | {/foreach} |
| 43 | 73 | </tbody> |
| 44 | 74 | |
| 45 | 75 | </table> |
| 76 | + | |
| 77 | +{if ($showdelete && !$showall)} | |
| 78 | + <p> | |
| 79 | + <a href="{addQS}fDocumentId={$document->getId()}&show=all{/addQS}">{i18n}Show deleted versions{/i18n}</a> | |
| 80 | + </p> | |
| 81 | +{/if} | |
| 46 | 82 | \ No newline at end of file | ... | ... |