X-Git-Url: https://git.cyclocoop.org/?a=blobdiff_plain;f=includes%2FSpecialUndelete.php;h=fb7c43bc5baebd6a830df5b8a4ce337a69bfda4c;hb=1e7e73444a9357be1c58301e8707c029f31f5ef7;hp=2175b6670cda455b2f543bc509099f40143540ba;hpb=599b1b8d2056e76697aad0b0ae49f7bbd79f999f;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/SpecialUndelete.php b/includes/SpecialUndelete.php index 2175b6670c..fb7c43bc5b 100644 --- a/includes/SpecialUndelete.php +++ b/includes/SpecialUndelete.php @@ -57,7 +57,7 @@ class PageArchive { $title = Title::newFromText( $prefix ); if( $title ) { $ns = $title->getNamespace(); - $encPrefix = $dbr->escapeLike( $title->getDbKey() ); + $encPrefix = $dbr->escapeLike( $title->getDBkey() ); } else { // Prolly won't work too good // @todo handle bare namespace names cleanly? @@ -78,7 +78,7 @@ class PageArchive { array( 'ar_namespace', 'ar_title', - 'COUNT(*) AS count', + 'COUNT(*) AS count' ), $condition, __METHOD__, @@ -97,19 +97,14 @@ class PageArchive { * * @return ResultWrapper */ - function listRevisions( $startTime, $limit ) { - $whereClause = array( 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDBkey() ); - if ( $startTime && is_numeric($startTime) ) - $whereClause[] = "ar_timestamp < $startTime"; - + function listRevisions() { $dbr = wfGetDB( DB_SLAVE ); $res = $dbr->select( 'archive', - array( 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', 'ar_comment', 'ar_len' ), - $whereClause, + array( 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', 'ar_comment', 'ar_len', 'ar_deleted' ), + array( 'ar_namespace' => $this->title->getNamespace(), + 'ar_title' => $this->title->getDBkey() ), 'PageArchive::listRevisions', - array( 'ORDER BY' => 'ar_timestamp DESC', - 'LIMIT' => intval($limit) ) ); + array( 'ORDER BY' => 'ar_timestamp DESC' ) ); $ret = $dbr->resultObject( $res ); return $ret; } @@ -129,15 +124,23 @@ class PageArchive { array( 'fa_id', 'fa_name', + 'fa_archive_name', 'fa_storage_key', + 'fa_storage_group', 'fa_size', 'fa_width', 'fa_height', + 'fa_bits', + 'fa_metadata', + 'fa_media_type', + 'fa_major_mime', + 'fa_minor_mime', 'fa_description', 'fa_user', 'fa_user_text', - 'fa_timestamp' ), - array( 'fa_name' => $this->title->getDbKey() ), + 'fa_timestamp', + 'fa_deleted' ), + array( 'fa_name' => $this->title->getDBkey() ), __METHOD__, array( 'ORDER BY' => 'fa_timestamp DESC' ) ); $ret = $dbr->resultObject( $res ); @@ -177,9 +180,10 @@ class PageArchive { 'ar_minor_edit', 'ar_flags', 'ar_text_id', + 'ar_deleted', 'ar_len' ), array( 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDbkey(), + 'ar_title' => $this->title->getDBkey(), 'ar_timestamp' => $dbr->timestamp( $timestamp ) ), __METHOD__ ); if( $row ) { @@ -194,7 +198,9 @@ class PageArchive { 'user_text' => $row->ar_user_text, 'timestamp' => $row->ar_timestamp, 'minor_edit' => $row->ar_minor_edit, - 'text_id' => $row->ar_text_id ) ); + 'text_id' => $row->ar_text_id, + 'deleted' => $row->ar_deleted, + 'len' => $row->ar_len) ); } else { return null; } @@ -217,7 +223,7 @@ class PageArchive { $row = $dbr->selectRow( 'archive', 'ar_timestamp', array( 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDbkey(), + 'ar_title' => $this->title->getDBkey(), 'ar_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ), __METHOD__, @@ -230,7 +236,7 @@ class PageArchive { array( 'rev_id', 'rev_timestamp' ), array( 'page_namespace' => $this->title->getNamespace(), - 'page_title' => $this->title->getDbkey(), + 'page_title' => $this->title->getDBkey(), 'page_id = rev_page', 'rev_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ), @@ -308,9 +314,6 @@ class PageArchive { return ($n > 0); } - const UNDELETE_NOTHINGRESTORED = 0; // No revisions could be restored - const UNDELETE_NOTAVAIL = -1; // Not all requested revisions are available - const UNDELETE_UNKNOWNERR = -2; // Unknown error /** * Restore the given (or all) text and file revisions for the page. * Once restored, the items will be removed from the archive tables. @@ -319,10 +322,12 @@ class PageArchive { * @param array $timestamps Pass an empty array to restore all revisions, otherwise list the ones to undelete. * @param string $comment * @param array $fileVersions + * @param bool $unsuppress * - * @return array(number of revisions restored, number of file versions restored, log reason) on success or UNDELETE_* on failure + * @return array(number of file revisions restored, number of image revisions restored, log message) + * on success, false on failure */ - function undelete( $timestamps, $comment = '', $fileVersions = array() ) { + function undelete( $timestamps, $comment = '', $fileVersions = array(), $unsuppress = false ) { // If both the set of text revisions and file revisions are empty, // restore everything. Otherwise, just restore the requested items. $restoreAll = empty( $timestamps ) && empty( $fileVersions ); @@ -332,16 +337,16 @@ class PageArchive { if( $restoreFiles && $this->title->getNamespace() == NS_IMAGE ) { $img = wfLocalFile( $this->title ); - $this->fileStatus = $img->restore( $fileVersions ); + $this->fileStatus = $img->restore( $fileVersions, $unsuppress ); $filesRestored = $this->fileStatus->successCount; } else { $filesRestored = 0; } if( $restoreText ) { - $textRestored = $this->undeleteRevisions( $timestamps ); - if($textRestored < 0) // It must be one of UNDELETE_* - return $textRestored; + $textRestored = $this->undeleteRevisions( $timestamps, $unsuppress ); + if($textRestored === false) // It must be one of UNDELETE_* + return false; } else { $textRestored = 0; } @@ -362,7 +367,7 @@ class PageArchive { $wgContLang->formatNum( $filesRestored ) ); } else { wfDebug( "Undelete: nothing undeleted...\n" ); - return self::UNDELETE_NOTHINGRESTORED; + return false; } if( trim( $comment ) != '' ) @@ -380,12 +385,13 @@ class PageArchive { * @param array $timestamps Pass an empty array to restore all revisions, otherwise list the ones to undelete. * @param string $comment * @param array $fileVersions + * @param bool $unsuppress, remove all ar_deleted/fa_deleted restrictions of seletected revs * - * @return int number of revisions restored on success or UNDELETE_* on failure + * @return mixed number of revisions restored or false on failure */ - private function undeleteRevisions( $timestamps ) { - if ( wfReadOnly() ) return 0; - + private function undeleteRevisions( $timestamps, $unsuppress = false ) { + if ( wfReadOnly() ) + return false; $restoreAll = empty( $timestamps ); $dbw = wfGetDB( DB_MASTER ); @@ -400,16 +406,25 @@ class PageArchive { __METHOD__, $options ); if( $page ) { + $makepage = false; # Page already exists. Import the history, and if necessary # we'll update the latest revision field in the record. $newid = 0; $pageId = $page->page_id; $previousRevId = $page->page_latest; + # Get the time span of this page + $previousTimestamp = $dbw->selectField( 'revision', 'rev_timestamp', + array( 'rev_id' => $previousRevId ), + __METHOD__ ); + if( $previousTimestamp === false ) { + wfDebug( __METHOD__.": existing page refers to a page_latest that does not exist\n" ); + return 0; + } } else { # Have to create a new article... - $newid = $article->insertOn( $dbw ); - $pageId = $newid; + $makepage = true; $previousRevId = 0; + $previousTimestamp = 0; } if( $restoreAll ) { @@ -424,7 +439,7 @@ class PageArchive { } /** - * Restore each revision... + * Select each archived revision... */ $result = $dbw->select( 'archive', /* fields */ array( @@ -437,6 +452,8 @@ class PageArchive { 'ar_minor_edit', 'ar_flags', 'ar_text_id', + 'ar_deleted', + 'ar_page_id', 'ar_len' ), /* WHERE */ array( 'ar_namespace' => $this->title->getNamespace(), @@ -446,15 +463,32 @@ class PageArchive { /* options */ array( 'ORDER BY' => 'ar_timestamp' ) ); - if( $dbw->numRows( $result ) < count( $timestamps ) ) { - wfDebug( __METHOD__.": couldn't find all requested rows\n" ); - return self::UNDELETE_NOTAVAIL; + $ret = $dbw->resultObject( $result ); + + $rev_count = $dbw->numRows( $result ); + if( $rev_count ) { + # We need to seek around as just using DESC in the ORDER BY + # would leave the revisions inserted in the wrong order + $first = $ret->fetchObject(); + $ret->seek( $rev_count - 1 ); + $last = $ret->fetchObject(); + // We don't handle well changing the top revision's settings + if( !$unsuppress && $last->ar_deleted && $last->ar_timestamp > $previousTimestamp ) { + wfDebug( __METHOD__.": restoration would result in a deleted top revision\n" ); + return false; + } + $ret->seek( 0 ); } + if( $makepage ) { + $newid = $article->insertOn( $dbw ); + $pageId = $newid; + } + $revision = null; $restored = 0; - - while( $row = $dbw->fetchObject( $result ) ) { + + while( $row = $ret->fetchObject() ) { if( $row->ar_text_id ) { // Revision was deleted in 1.5+; text is in // the regular text table, use the reference. @@ -477,10 +511,13 @@ class PageArchive { 'timestamp' => $row->ar_timestamp, 'minor_edit' => $row->ar_minor_edit, 'text_id' => $row->ar_text_id, - 'len' => $row->ar_len + 'deleted' => $unsuppress ? 0 : $row->ar_deleted, + 'len' => $row->ar_len ) ); $revision->insertOn( $dbw ); $restored++; + + wfRunHooks( 'ArticleRevisionUndeleted', array( &$this->title, $revision, $row->ar_page_id ) ); } // Was anything restored at all? if($restored == 0) @@ -502,6 +539,11 @@ class PageArchive { wfRunHooks( 'ArticleUndelete', array( &$this->title, false ) ); Article::onArticleEdit( $this->title ); } + + if( $this->title->getNamespace() == NS_IMAGE ) { + $update = new HTMLCacheUpdate( $this->title, 'imagelinks' ); + $update->doUpdate(); + } } else { // Revision couldn't be created. This is very weird return self::UNDELETE_UNKNOWNERR; @@ -545,6 +587,7 @@ class UndeleteForm { $this->mPreview = $request->getCheck( 'preview' ) && $posted; $this->mDiff = $request->getCheck( 'diff' ); $this->mComment = $request->getText( 'wpComment' ); + $this->mUnsuppress = $request->getVal( 'wpUnsuppress' ) && $wgUser->isAllowed( 'oversight' ); if( $par != "" ) { $this->mTarget = $par; @@ -580,7 +623,7 @@ class UndeleteForm { } function execute() { - global $wgOut; + global $wgOut, $wgUser; if ( $this->mAllowed ) { $wgOut->setPagetitle( wfMsg( "undeletepage" ) ); } else { @@ -588,13 +631,17 @@ class UndeleteForm { } if( is_null( $this->mTargetObj ) ) { - $this->showSearchForm(); - - # List undeletable articles - if( $this->mSearchPrefix ) { - $result = PageArchive::listPagesByPrefix( - $this->mSearchPrefix ); - $this->showList( $result ); + # Not all users can just browse every deleted page from the list + if( $wgUser->isAllowed( 'browsearchive' ) ) { + $this->showSearchForm(); + + # List undeletable articles + if( $this->mSearchPrefix ) { + $result = PageArchive::listPagesByPrefix( $this->mSearchPrefix ); + $this->showList( $result ); + } + } else { + $wgOut->addWikiText( wfMsgHtml( 'undelete-header' ) ); } return; } @@ -602,7 +649,14 @@ class UndeleteForm { return $this->showRevision( $this->mTimestamp ); } if( $this->mFile !== null ) { - return $this->showFile( $this->mFile ); + $file = new ArchivedFile( $this->mTargetObj, '', $this->mFile ); + // Check if user is allowed to see this file + if( !$file->userCan( File::DELETED_FILE ) ) { + $wgOut->permissionRequired( 'hiderevision' ); + return false; + } else { + return $this->showFile( $this->mFile ); + } } if( $this->mRestore && $this->mAction == "submit" ) { return $this->undelete(); @@ -612,7 +666,7 @@ class UndeleteForm { function showSearchForm() { global $wgOut, $wgScript; - $wgOut->addWikiText( wfMsg( 'undelete-header' ) ); + $wgOut->addWikiMsg( 'undelete-header' ); $wgOut->addHtml( Xml::openElement( 'form', array( @@ -631,22 +685,24 @@ class UndeleteForm { '' ); } - /* private */ function showList( $result ) { + // Generic list of deleted pages + private function showList( $result ) { global $wgLang, $wgContLang, $wgUser, $wgOut; if( $result->numRows() == 0 ) { - $wgOut->addWikiText( wfMsg( 'undelete-no-results' ) ); + $wgOut->addWikiMsg( 'undelete-no-results' ); return; } - $wgOut->addWikiText( wfMsg( "undeletepagetext" ) ); + $wgOut->addWikiMsg( "undeletepagetext" ); $sk = $wgUser->getSkin(); $undelete = SpecialPage::getTitleFor( 'Undelete' ); $wgOut->addHTML( ""); } else { - $wgOut->addWikiText( wfMsg( "nohistory" ) ); + $wgOut->addWikiMsg( "nohistory" ); } if( $haveFiles ) { - $wgOut->addHtml( "

" . wfMsgHtml( 'filehist' ) . "

\n" ); + $wgOut->addHtml( Xml::element( 'h2', null, wfMsg( 'filehist' ) ) . "\n" ); $wgOut->addHtml( "" ); @@ -1046,6 +1048,114 @@ class UndeleteForm { return true; } + + private function formatRevisionRow( $row, $earliestLiveTime, $remaining, $sk ) { + global $wgUser, $wgLang; + + $rev = new Revision( array( + 'page' => $this->mTargetObj->getArticleId(), + 'comment' => $row->ar_comment, + 'user' => $row->ar_user, + 'user_text' => $row->ar_user_text, + 'timestamp' => $row->ar_timestamp, + 'minor_edit' => $row->ar_minor_edit, + 'deleted' => $row->ar_deleted, + 'len' => $row->ar_len ) ); + + $stxt = ''; + $ts = wfTimestamp( TS_MW, $row->ar_timestamp ); + if( $this->mAllowed ) { + $checkBox = Xml::check( "ts$ts" ); + $titleObj = SpecialPage::getTitleFor( "Undelete" ); + $pageLink = $this->getPageLink( $rev, $titleObj, $ts, $sk ); + # Last link + if( !$rev->userCan( Revision::DELETED_TEXT ) ) { + $last = wfMsgHtml('diff'); + } else if( $remaining > 0 || ($earliestLiveTime && $ts > $earliestLiveTime) ) { + $last = $sk->makeKnownLinkObj( $titleObj, wfMsgHtml('diff'), + "target=" . $this->mTargetObj->getPrefixedUrl() . "×tamp=$ts&diff=prev" ); + } else { + $last = wfMsgHtml('diff'); + } + } else { + $checkBox = ''; + $pageLink = $wgLang->timeanddate( $ts, true ); + $last = wfMsgHtml('diff'); + } + $userLink = $sk->revUserTools( $rev ); + + if(!is_null($size = $row->ar_len)) { + if($size == 0) + $stxt = wfMsgHtml('historyempty'); + else + $stxt = wfMsgHtml('historysize', $wgLang->formatNum( $size ) ); + } + $comment = $sk->revComment( $rev ); + $revdlink = ''; + if( $wgUser->isAllowed( 'deleterevision' ) ) { + $revdel = SpecialPage::getTitleFor( 'Revisiondelete' ); + if( !$rev->userCan( Revision::DELETED_RESTRICTED ) ) { + // If revision was hidden from sysops + $del = wfMsgHtml('rev-delundel'); + } else { + $ts = wfTimestamp( TS_MW, $row->ar_timestamp ); + $del = $sk->makeKnownLinkObj( $revdel, + wfMsgHtml('rev-delundel'), + 'target=' . $this->mTargetObj->getPrefixedUrl() . "&artimestamp=$ts" ); + // Bolden oversighted content + if( $rev->isDeleted( Revision::DELETED_RESTRICTED ) ) + $del = "$del"; + } + $revdlink = "($del)"; + } + + return "
  • $checkBox $revdlink ($last) $pageLink . . $userLink $stxt $comment
  • "; + } + + private function formatFileRow( $row, $sk ) { + global $wgUser, $wgLang; + + $file = ArchivedFile::newFromRow( $row ); + + $ts = wfTimestamp( TS_MW, $row->fa_timestamp ); + if( $this->mAllowed && $row->fa_storage_key ) { + $checkBox = Xml::check( "fileid" . $row->fa_id ); + $key = urlencode( $row->fa_storage_key ); + $target = urlencode( $this->mTarget ); + $titleObj = SpecialPage::getTitleFor( "Undelete" ); + $pageLink = $this->getFileLink( $file, $titleObj, $ts, $key, $sk ); + } else { + $checkBox = ''; + $pageLink = $wgLang->timeanddate( $ts, true ); + } + $userLink = $this->getFileUser( $file, $sk ); + $data = + wfMsgHtml( 'widthheight', + $wgLang->formatNum( $row->fa_width ), + $wgLang->formatNum( $row->fa_height ) ) . + ' (' . + wfMsgHtml( 'nbytes', $wgLang->formatNum( $row->fa_size ) ) . + ')'; + $comment = $this->getFileComment( $file, $sk ); + $revdlink = ''; + if( $wgUser->isAllowed( 'deleterevision' ) ) { + $revdel = SpecialPage::getTitleFor( 'Revisiondelete' ); + if( !$file->userCan(File::DELETED_RESTRICTED ) ) { + // If revision was hidden from sysops + $del = $this->messages['rev-delundel']; + } else { + $del = $sk->makeKnownLinkObj( $revdel, + wfMsgHtml('rev-delundel'), + 'target=' . $this->mTargetObj->getPrefixedUrl() . + '&fileid=' . $row->fa_id ); + // Bolden oversighted content + if( $file->isDeleted( File::DELETED_RESTRICTED ) ) + $del = "$del"; + } + $revdlink = "($del)"; + } + return "
  • $checkBox $revdlink $pageLink . . $userLink $data $comment
  • \n"; + } private function getEarliestTime( $title ) { $dbr = wfGetDB( DB_SLAVE ); @@ -1059,27 +1169,104 @@ class UndeleteForm { return null; } + /** + * Fetch revision text link if it's available to all users + * @return string + */ + function getPageLink( $rev, $titleObj, $ts, $sk ) { + global $wgLang; + + if( !$rev->userCan(Revision::DELETED_TEXT) ) { + return '' . $wgLang->timeanddate( $ts, true ) . ''; + } else { + $link = $sk->makeKnownLinkObj( $titleObj, $wgLang->timeanddate( $ts, true ), + "target=".$this->mTargetObj->getPrefixedUrl()."×tamp=$ts" ); + if( $rev->isDeleted(Revision::DELETED_TEXT) ) + $link = '' . $link . ''; + return $link; + } + } + + /** + * Fetch image view link if it's available to all users + * @return string + */ + function getFileLink( $file, $titleObj, $ts, $key, $sk ) { + global $wgLang; + + if( !$file->userCan(File::DELETED_FILE) ) { + return '' . $wgLang->timeanddate( $ts, true ) . ''; + } else { + $link = $sk->makeKnownLinkObj( $titleObj, $wgLang->timeanddate( $ts, true ), + "target=".$this->mTargetObj->getPrefixedUrl()."&file=$key" ); + if( $file->isDeleted(File::DELETED_FILE) ) + $link = '' . $link . ''; + return $link; + } + } + + /** + * Fetch file's user id if it's available to this user + * @return string + */ + function getFileUser( $file, $sk ) { + if( !$file->userCan(File::DELETED_USER) ) { + return '' . wfMsgHtml( 'rev-deleted-user' ) . ''; + } else { + $link = $sk->userLink( $file->getRawUser(), $file->getRawUserText() ) . + $sk->userToolLinks( $file->getRawUser(), $file->getRawUserText() ); + if( $file->isDeleted(File::DELETED_USER) ) + $link = '' . $link . ''; + return $link; + } + } + + /** + * Fetch file upload comment if it's available to this user + * @return string + */ + function getFileComment( $file, $sk ) { + if( !$file->userCan(File::DELETED_COMMENT) ) { + return '' . wfMsgHtml( 'rev-deleted-comment' ) . ''; + } else { + $link = $sk->commentBlock( $file->getRawDescription() ); + if( $file->isDeleted(File::DELETED_COMMENT) ) + $link = '' . $link . ''; + return $link; + } + } + function undelete() { global $wgOut, $wgUser; + if ( wfReadOnly() ) { + $wgOut->readOnlyPage(); + return; + } if( !is_null( $this->mTargetObj ) ) { $archive = new PageArchive( $this->mTargetObj ); - $ok = $archive->undelete( $this->mTargetTimestamp, $this->mComment, - $this->mFileVersions ); + $this->mFileVersions, + $this->mUnsuppress ); if( is_array($ok) ) { + if ( $ok[1] ) // Undeleted file count + wfRunHooks( 'FileUndeleteComplete', array( + $this->mTargetObj, $this->mFileVersions, + $wgUser, $this->mComment) ); + $skin = $wgUser->getSkin(); $link = $skin->makeKnownLinkObj( $this->mTargetObj ); $wgOut->addHtml( wfMsgWikiHtml( 'undeletedpage', $link ) ); } else { $wgOut->showFatalError( wfMsg( "cannotundelete" ) ); + $wgOut->addHtml( '

    ' . wfMsgHtml( "undeleterevdel" ) . '

    ' ); } // Show file deletion warnings and errors $status = $archive->getFileStatus(); - if ( $status && !$status->isGood() ) { + if( $status && !$status->isGood() ) { $wgOut->addWikiText( $status->getWikiText( 'undelete-error-short', 'undelete-error-long' ) ); } } else {