From cf6dd13faca23af5b6e3188445b84e05d620d499 Mon Sep 17 00:00:00 2001 From: Alexandre Emsenhuber Date: Fri, 1 Jul 2011 20:07:21 +0000 Subject: [PATCH] * Changed action=revert to use a subclass of Action * Added WikiPage::getActionOverrides() to be able to execute different actions depending on the namespace (obviously needed for action=revert). This is only used when the value of $wgActions for the corresponding action is true; so extension can still override this. * Added Action::getDescription() to ease the change of the page header and the element --- includes/Action.php | 18 ++- includes/Article.php | 17 ++- includes/AutoLoader.php | 3 +- includes/DefaultSettings.php | 4 + includes/FileRevertForm.php | 180 ------------------------------ includes/ImagePage.php | 9 -- includes/Wiki.php | 1 - includes/WikiFilePage.php | 4 + includes/WikiPage.php | 12 ++ includes/actions/RevertAction.php | 136 ++++++++++++++++++++++ 10 files changed, 181 insertions(+), 203 deletions(-) delete mode 100644 includes/FileRevertForm.php create mode 100644 includes/actions/RevertAction.php diff --git a/includes/Action.php b/includes/Action.php index dcdd4b328f..ff0ce697d0 100644 --- a/includes/Action.php +++ b/includes/Action.php @@ -47,9 +47,10 @@ abstract class Action { * Get the Action subclass which should be used to handle this action, false if * the action is disabled, or null if it's not recognised * @param $action String + * @param $overrides Array * @return bool|null|string */ - private final static function getClass( $action ) { + private final static function getClass( $action, array $overrides ) { global $wgActions; $action = strtolower( $action ); @@ -59,6 +60,8 @@ abstract class Action { if ( $wgActions[$action] === false ) { return false; + } elseif ( $wgActions[$action] === true && isset( $overrides[$action] ) ) { + return $overrides[$action]; } elseif ( $wgActions[$action] === true ) { return ucfirst( $action ) . 'Action'; } else { @@ -74,7 +77,7 @@ abstract class Action { * if it is not recognised */ public final static function factory( $action, Page $page ) { - $class = self::getClass( $action ); + $class = self::getClass( $action, $page->getActionOverrides() ); if ( $class ) { $obj = new $class( $page ); return $obj; @@ -223,7 +226,7 @@ abstract class Action { protected function setHeaders() { $out = $this->getOutput(); $out->setRobotPolicy( "noindex,nofollow" ); - $out->setPageTitle( $this->getTitle()->getPrefixedText() ); + $out->setPageTitle( $this->getPageTitle() ); $this->getOutput()->setSubtitle( $this->getDescription() ); $out->setArticleRelated( true ); } @@ -233,6 +236,15 @@ abstract class Action { * * @return String */ + protected function getPageTitle() { + return $this->getTitle()->getPrefixedText(); + } + + /** + * Returns the description that goes below the \<h1\> tag + * + * @return String + */ protected function getDescription() { return wfMsg( strtolower( $this->getName() ) ); } diff --git a/includes/Article.php b/includes/Article.php index 5340935a49..76d419e3ba 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -1232,6 +1232,14 @@ class Article extends Page { Action::factory( 'info', $this )->show(); } + /** + * Overriden by ImagePage class, only present here to avoid a fatal error + * Called for ?action=revert + */ + public function revert() { + Action::factory( 'revert', $this )->show(); + } + /** * Output a redirect back to the article. * This is typically used after an edit. @@ -1905,15 +1913,6 @@ class Article extends Page { /**#@-*/ - /** - * Overriden by ImagePage class, only present here to avoid a fatal error - * Called for ?action=revert - */ - public function revert() { - global $wgOut; - $wgOut->showErrorPage( 'nosuchaction', 'nosuchactiontext' ); - } - /** * Add the primary page-view wikitext to the output buffer * Saves the text into the parser cache if possible. diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index b24fa40de9..c49348428e 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -83,7 +83,6 @@ $wgAutoloadLocalClasses = array( 'FeedItem' => 'includes/Feed.php', 'FeedUtils' => 'includes/FeedUtils.php', 'FileDeleteForm' => 'includes/FileDeleteForm.php', - 'FileRevertForm' => 'includes/FileRevertForm.php', 'ForkController' => 'includes/ForkController.php', 'FormlessAction' => 'includes/Action.php', 'FormAction' => 'includes/Action.php', @@ -259,6 +258,8 @@ $wgAutoloadLocalClasses = array( 'InfoAction' => 'includes/actions/InfoAction.php', 'MarkpatrolledAction' => 'includes/actions/MarkpatrolledAction.php', 'PurgeAction' => 'includes/actions/PurgeAction.php', + 'RevertAction' => 'includes/actions/RevertAction.php', + 'RevertFileAction' => 'includes/actions/RevertAction.php', 'RevisiondeleteAction' => 'includes/actions/RevisiondeleteAction.php', 'UnwatchAction' => 'includes/actions/WatchAction.php', 'WatchAction' => 'includes/actions/WatchAction.php', diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index debae38ef8..35d1d01601 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -5049,6 +5049,9 @@ $wgMaxRedirectLinksRetrieved = 500; * Array of allowed values for the title=foo&action=<action> parameter. Syntax is: * 'foo' => 'ClassName' Load the specified class which subclasses Action * 'foo' => true Load the class FooAction which subclasses Action + * If something is specified in the getActionOverrides() + * of the relevant Page object it will be used + * instead of the default class. * 'foo' => false The action is disabled; show an error message * Unsetting core actions will probably cause things to complain loudly. */ @@ -5058,6 +5061,7 @@ $wgActions = array( 'info' => true, 'markpatrolled' => true, 'purge' => true, + 'revert' => true, 'revisiondelete' => true, 'unwatch' => true, 'watch' => true, diff --git a/includes/FileRevertForm.php b/includes/FileRevertForm.php deleted file mode 100644 index 47084aad4f..0000000000 --- a/includes/FileRevertForm.php +++ /dev/null @@ -1,180 +0,0 @@ -<?php - -/** - * File reversion user interface - * - * @ingroup Media - * @author Rob Church <robchur@gmail.com> - */ -class FileRevertForm { - - protected $title = null; - protected $file = null; - protected $archiveName = ''; - protected $timestamp = false; - protected $oldFile; - - /** - * Constructor - * - * @param $file File we're reverting - */ - public function __construct( $file ) { - $this->title = $file->getTitle(); - $this->file = $file; - } - - /** - * Fulfil the request; shows the form or reverts the file, - * pending authentication, confirmation, etc. - */ - public function execute() { - global $wgOut, $wgRequest, $wgUser, $wgLang; - $this->setHeaders(); - - if( wfReadOnly() ) { - $wgOut->readOnlyPage(); - return; - } elseif( !$wgUser->isLoggedIn() ) { - $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' ); - return; - } elseif( !$this->title->userCan( 'edit' ) || !$this->title->userCan( 'upload' ) ) { - // The standard read-only thing doesn't make a whole lot of sense - // here; surely it should show the image or something? -- RC - $article = new Article( $this->title ); - $wgOut->readOnlyPage( $article->getContent(), true ); - return; - } elseif( $wgUser->isBlocked() ) { - $wgOut->blockedPage(); - return; - } - - $this->archiveName = $wgRequest->getText( 'oldimage' ); - $token = $wgRequest->getText( 'wpEditToken' ); - if( !$this->isValidOldSpec() ) { - $wgOut->showUnexpectedValueError( 'oldimage', htmlspecialchars( $this->archiveName ) ); - return; - } - - if( !$this->haveOldVersion() ) { - $wgOut->addHTML( wfMsgExt( 'filerevert-badversion', 'parse' ) ); - $wgOut->returnToMain( false, $this->title ); - return; - } - - // Perform the reversion if appropriate - if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $token, $this->archiveName ) ) { - $source = $this->file->getArchiveVirtualUrl( $this->archiveName ); - $comment = $wgRequest->getText( 'wpComment' ); - // TODO: Preserve file properties from database instead of reloading from file - $status = $this->file->upload( $source, $comment, $comment ); - if( $status->isGood() ) { - $wgOut->addHTML( wfMsgExt( 'filerevert-success', 'parse', $this->title->getText(), - $wgLang->date( $this->getTimestamp(), true ), - $wgLang->time( $this->getTimestamp(), true ), - wfExpandUrl( $this->file->getArchiveUrl( $this->archiveName ) ) ) ); - $wgOut->returnToMain( false, $this->title ); - } else { - $wgOut->addWikiText( $status->getWikiText() ); - } - return; - } - - // Show the form - $this->showForm(); - } - - /** - * Show the confirmation form - */ - protected function showForm() { - global $wgOut, $wgUser, $wgLang, $wgContLang; - $timestamp = $this->getTimestamp(); - - $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction() ) ); - $form .= Html::hidden( 'wpEditToken', $wgUser->editToken( $this->archiveName ) ); - $form .= '<fieldset><legend>' . wfMsgHtml( 'filerevert-legend' ) . '</legend>'; - $form .= wfMsgExt( 'filerevert-intro', 'parse', $this->title->getText(), - $wgLang->date( $timestamp, true ), $wgLang->time( $timestamp, true ), - wfExpandUrl( $this->file->getArchiveUrl( $this->archiveName ) ) ); - $form .= '<p>' . Xml::inputLabel( wfMsg( 'filerevert-comment' ), 'wpComment', 'wpComment', - 60, wfMsgForContent( 'filerevert-defaultcomment', - $wgContLang->date( $timestamp, false, false ), $wgContLang->time( $timestamp, false, false ) ) ) . '</p>'; - $form .= '<p>' . Xml::submitButton( wfMsg( 'filerevert-submit' ) ) . '</p>'; - $form .= '</fieldset>'; - $form .= '</form>'; - - $wgOut->addHTML( $form ); - } - - /** - * Set headers, titles and other bits - */ - protected function setHeaders() { - global $wgOut, $wgUser; - $wgOut->setPageTitle( wfMsg( 'filerevert', $this->title->getText() ) ); - $wgOut->setRobotPolicy( 'noindex,nofollow' ); - $wgOut->setSubtitle( wfMsg( - 'filerevert-backlink', - $wgUser->getSkin()->link( - $this->title, - null, - array(), - array(), - array( 'known', 'noclasses' ) - ) - ) ); - } - - /** - * Is the provided `oldimage` value valid? - * - * @return bool - */ - protected function isValidOldSpec() { - return strlen( $this->archiveName ) >= 16 - && strpos( $this->archiveName, '/' ) === false - && strpos( $this->archiveName, '\\' ) === false; - } - - /** - * Does the provided `oldimage` value correspond - * to an existing, local, old version of this file? - * - * @return bool - */ - protected function haveOldVersion() { - return $this->getOldFile()->exists(); - } - - /** - * Prepare the form action - * - * @return string - */ - protected function getAction() { - $q = array(); - $q[] = 'action=revert'; - $q[] = 'oldimage=' . urlencode( $this->archiveName ); - return $this->title->getLocalUrl( implode( '&', $q ) ); - } - - /** - * Extract the timestamp of the old version - * - * @return string - */ - protected function getTimestamp() { - if( $this->timestamp === false ) { - $this->timestamp = $this->getOldFile()->getTimestamp(); - } - return $this->timestamp; - } - - protected function getOldFile() { - if ( !isset( $this->oldFile ) ) { - $this->oldFile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->title, $this->archiveName ); - } - return $this->oldFile; - } -} diff --git a/includes/ImagePage.php b/includes/ImagePage.php index ebbe36ca6c..ce1cbdd7c8 100644 --- a/includes/ImagePage.php +++ b/includes/ImagePage.php @@ -786,15 +786,6 @@ EOT $deleter->execute(); } - /** - * Revert the file to an earlier version - */ - public function revert() { - $this->loadFile(); - $reverter = new FileRevertForm( $this->mPage->getFile() ); - $reverter->execute(); - } - /** * Override handling of action=purge */ diff --git a/includes/Wiki.php b/includes/Wiki.php index f6d0546ae4..251d1db5ad 100644 --- a/includes/Wiki.php +++ b/includes/Wiki.php @@ -464,7 +464,6 @@ class MediaWiki { wfProfileOut( __METHOD__ . '-raw' ); break; case 'delete': - case 'revert': case 'rollback': case 'protect': case 'unprotect': diff --git a/includes/WikiFilePage.php b/includes/WikiFilePage.php index e4ad0e24ff..6727a8cd8f 100644 --- a/includes/WikiFilePage.php +++ b/includes/WikiFilePage.php @@ -16,6 +16,10 @@ class WikiFilePage extends WikiPage { $this->mRepo = null; } + public function getActionOverrides() { + return array( 'revert' => 'RevertFileAction' ); + } + /** * @param $file File: * @return void diff --git a/includes/WikiPage.php b/includes/WikiPage.php index eb99928c2b..656bce8470 100644 --- a/includes/WikiPage.php +++ b/includes/WikiPage.php @@ -61,6 +61,18 @@ class WikiPage extends Page { # return $t == null ? null : new static( $t ); // PHP 5.3 } + /** + * Returns overrides for action handlers. + * Classes listed here will be used instead of the default one when + * (and only when) $wgActions[$action] === true. This allows subclasses + * to override the default behavior. + * + * @return Array + */ + public function getActionOverrides() { + return array(); + } + /** * If this page is a redirect, get its target * diff --git a/includes/actions/RevertAction.php b/includes/actions/RevertAction.php new file mode 100644 index 0000000000..815606d7f4 --- /dev/null +++ b/includes/actions/RevertAction.php @@ -0,0 +1,136 @@ +<?php +/** + * File reversion user interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * + * @file + * @ingroup Action + * @ingroup Media + * @author Alexandre Emsenhuber + * @author Rob Church <robchur@gmail.com> + */ + +/** + * Dummy class for pages not in NS_FILE + * + * @ingroup Action + */ +class RevertAction extends Action { + + public function getName() { + return 'revert'; + } + + public function getRestriction() { + return 'read'; + } + + public function show() { + $this->getOutput()->showErrorPage( 'nosuchaction', 'nosuchactiontext' ); + } + + public function execute() {} +} + +/** + * Class for pages in NS_FILE + * + * @ingroup Action + */ +class RevertFileAction extends FormAction { + protected $oldFile; + + public function getName() { + return 'revert'; + } + + public function getRestriction() { + return 'upload'; + } + + protected function checkCanExecute( User $user ) { + parent::checkCanExecute( $user ); + + $oldimage = $this->getRequest()->getText( 'oldimage' ); + if ( strlen( $oldimage ) < 16 + || strpos( $oldimage, '/' ) !== false + || strpos( $oldimage, '\\' ) !== false ) + { + throw new ErrorPageError( 'internalerror', 'unexpected', array( 'oldimage', $oldimage ) ); + } + + $this->oldFile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->getTitle(), $oldimage ); + if ( !$this->oldFile->exists() ) { + throw new ErrorPageError( '', 'filerevert-badversion' ); + } + } + + protected function alterForm( HTMLForm $form ) { + $form->setWrapperLegend( wfMsgHtml( 'filerevert-legend' ) ); + $form->setSubmitText( wfMsg( 'filerevert-submit' ) ); + $form->addHiddenField( 'oldimage', $this->getRequest()->getText( 'oldimage' ) ); + } + + protected function getFormFields() { + global $wgContLang; + + $timestamp = $this->oldFile->getTimestamp(); + + return array( + 'intro' => array( + 'type' => 'info', + 'vertical-label' => true, + 'raw' => true, + 'default' => wfMsgExt( 'filerevert-intro', 'parse', $this->getTitle()->getText(), + $this->getLang()->date( $timestamp, true ), $this->getLang()->time( $timestamp, true ), + wfExpandUrl( $this->page->getFile()->getArchiveUrl( $this->getRequest()->getText( 'oldimage' ) ) ) ) + ), + 'comment' => array( + 'type' => 'text', + 'label-message' => 'filerevert-comment', + 'default' => wfMsgForContent( 'filerevert-defaultcomment', + $wgContLang->date( $timestamp, false, false ), $wgContLang->time( $timestamp, false, false ) ), + ) + ); + } + + public function onSubmit( $data ) { + $source = $this->page->getFile()->getArchiveVirtualUrl( $this->getRequest()->getText( 'oldimage' ) ); + $comment = $data['comment']; + // TODO: Preserve file properties from database instead of reloading from file + return $this->page->getFile()->upload( $source, $comment, $comment ); + } + + public function onSuccess() { + $timestamp = $this->oldFile->getTimestamp(); + $this->getOutput()->addHTML( wfMsgExt( 'filerevert-success', 'parse', $this->getTitle()->getText(), + $this->getLang()->date( $timestamp, true ), + $this->getLang()->time( $timestamp, true ), + wfExpandUrl( $this->page->getFile()->getArchiveUrl( $this->getRequest()->getText( 'oldimage' ) ) ) ) ); + $this->getOutput()->returnToMain( false, $this->getTitle() ); + } + + protected function getPageTitle() { + return wfMsg( 'filerevert', $this->getTitle()->getText() ); + } + + protected function getDescription() { + return wfMsg( + 'filerevert-backlink', + Linker::linkKnown( $this->getTitle() ) + ); + } +} -- 2.20.1