diff options
Diffstat (limited to 'includes/actions')
-rw-r--r-- | includes/actions/Action.php | 386 | ||||
-rw-r--r-- | includes/actions/CachedAction.php | 23 | ||||
-rw-r--r-- | includes/actions/CreditsAction.php | 51 | ||||
-rw-r--r-- | includes/actions/DeleteAction.php | 10 | ||||
-rw-r--r-- | includes/actions/EditAction.php | 38 | ||||
-rw-r--r-- | includes/actions/FormAction.php | 123 | ||||
-rw-r--r-- | includes/actions/FormlessAction.php | 45 | ||||
-rw-r--r-- | includes/actions/HistoryAction.php | 218 | ||||
-rw-r--r-- | includes/actions/InfoAction.php | 97 | ||||
-rw-r--r-- | includes/actions/MarkpatrolledAction.php | 1 | ||||
-rw-r--r-- | includes/actions/ProtectAction.php | 29 | ||||
-rw-r--r-- | includes/actions/RawAction.php | 97 | ||||
-rw-r--r-- | includes/actions/RenderAction.php | 3 | ||||
-rw-r--r-- | includes/actions/RevertAction.php | 61 | ||||
-rw-r--r-- | includes/actions/RevisiondeleteAction.php | 1 | ||||
-rw-r--r-- | includes/actions/RollbackAction.php | 27 | ||||
-rw-r--r-- | includes/actions/SubmitAction.php | 42 | ||||
-rw-r--r-- | includes/actions/UnprotectAction.php | 43 | ||||
-rw-r--r-- | includes/actions/UnwatchAction.php | 57 | ||||
-rw-r--r-- | includes/actions/ViewAction.php | 1 | ||||
-rw-r--r-- | includes/actions/WatchAction.php | 59 |
21 files changed, 1074 insertions, 338 deletions
diff --git a/includes/actions/Action.php b/includes/actions/Action.php new file mode 100644 index 00000000..8d11d901 --- /dev/null +++ b/includes/actions/Action.php @@ -0,0 +1,386 @@ +<?php +/** + * Base classes for actions done on pages. + * + * 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 + */ + +/** + * @defgroup Actions Action done on pages + */ + +/** + * Actions are things which can be done to pages (edit, delete, rollback, etc). They + * are distinct from Special Pages because an action must apply to exactly one page. + * + * To add an action in an extension, create a subclass of Action, and add the key to + * $wgActions. There is also the deprecated UnknownAction hook + * + * Actions generally fall into two groups: the show-a-form-then-do-something-with-the-input + * format (protect, delete, move, etc), and the just-do-something format (watch, rollback, + * patrol, etc). The FormAction and FormlessAction classes represent these two groups. + */ +abstract class Action { + + /** + * Page on which we're performing the action + * @since 1.17 + * @var WikiPage|Article|ImagePage|CategoryPage|Page $page + */ + protected $page; + + /** + * IContextSource if specified; otherwise we'll use the Context from the Page + * @since 1.17 + * @var IContextSource $context + */ + protected $context; + + /** + * The fields used to create the HTMLForm + * @since 1.17 + * @var array $fields + */ + protected $fields; + + /** + * 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 string $action + * @param array $overrides + * @return bool|null|string|callable + */ + final private static function getClass( $action, array $overrides ) { + global $wgActions; + $action = strtolower( $action ); + + if ( !isset( $wgActions[$action] ) ) { + return null; + } + + 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 { + return $wgActions[$action]; + } + } + + /** + * Get an appropriate Action subclass for the given action + * @since 1.17 + * @param string $action + * @param Page $page + * @param IContextSource $context + * @return Action|bool|null False if the action is disabled, null + * if it is not recognised + */ + final public static function factory( $action, Page $page, IContextSource $context = null ) { + $classOrCallable = self::getClass( $action, $page->getActionOverrides() ); + + if ( is_string( $classOrCallable ) ) { + $obj = new $classOrCallable( $page, $context ); + return $obj; + } + + if ( is_callable( $classOrCallable ) ) { + return call_user_func_array( $classOrCallable, array( $page, $context ) ); + } + + return $classOrCallable; + } + + /** + * Get the action that will be executed, not necessarily the one passed + * passed through the "action" request parameter. Actions disabled in + * $wgActions will be replaced by "nosuchaction". + * + * @since 1.19 + * @param IContextSource $context + * @return string Action name + */ + final public static function getActionName( IContextSource $context ) { + global $wgActions; + + $request = $context->getRequest(); + $actionName = $request->getVal( 'action', 'view' ); + + // Check for disabled actions + if ( isset( $wgActions[$actionName] ) && $wgActions[$actionName] === false ) { + $actionName = 'nosuchaction'; + } + + // Workaround for bug #20966: inability of IE to provide an action dependent + // on which submit button is clicked. + if ( $actionName === 'historysubmit' ) { + if ( $request->getBool( 'revisiondelete' ) ) { + $actionName = 'revisiondelete'; + } else { + $actionName = 'view'; + } + } elseif ( $actionName == 'editredlink' ) { + $actionName = 'edit'; + } + + // Trying to get a WikiPage for NS_SPECIAL etc. will result + // in WikiPage::factory throwing "Invalid or virtual namespace -1 given." + // For SpecialPages et al, default to action=view. + if ( !$context->canUseWikiPage() ) { + return 'view'; + } + + $action = Action::factory( $actionName, $context->getWikiPage(), $context ); + if ( $action instanceof Action ) { + return $action->getName(); + } + + return 'nosuchaction'; + } + + /** + * Check if a given action is recognised, even if it's disabled + * @since 1.17 + * + * @param string $name Name of an action + * @return bool + */ + final public static function exists( $name ) { + return self::getClass( $name, array() ) !== null; + } + + /** + * Get the IContextSource in use here + * @since 1.17 + * @return IContextSource + */ + final public function getContext() { + if ( $this->context instanceof IContextSource ) { + return $this->context; + } elseif ( $this->page instanceof Article ) { + // NOTE: $this->page can be a WikiPage, which does not have a context. + wfDebug( __METHOD__ . ": no context known, falling back to Article's context.\n" ); + return $this->page->getContext(); + } + + wfWarn( __METHOD__ . ': no context known, falling back to RequestContext::getMain().' ); + return RequestContext::getMain(); + } + + /** + * Get the WebRequest being used for this instance + * @since 1.17 + * + * @return WebRequest + */ + final public function getRequest() { + return $this->getContext()->getRequest(); + } + + /** + * Get the OutputPage being used for this instance + * @since 1.17 + * + * @return OutputPage + */ + final public function getOutput() { + return $this->getContext()->getOutput(); + } + + /** + * Shortcut to get the User being used for this instance + * @since 1.17 + * + * @return User + */ + final public function getUser() { + return $this->getContext()->getUser(); + } + + /** + * Shortcut to get the Skin being used for this instance + * @since 1.17 + * + * @return Skin + */ + final public function getSkin() { + return $this->getContext()->getSkin(); + } + + /** + * Shortcut to get the user Language being used for this instance + * + * @return Language + */ + final public function getLanguage() { + return $this->getContext()->getLanguage(); + } + + /** + * Shortcut to get the Title object from the page + * @since 1.17 + * + * @return Title + */ + final public function getTitle() { + return $this->page->getTitle(); + } + + /** + * Get a Message object with context set + * Parameters are the same as wfMessage() + * + * @return Message + */ + final public function msg() { + $params = func_get_args(); + return call_user_func_array( array( $this->getContext(), 'msg' ), $params ); + } + + /** + * Constructor. + * + * Only public since 1.21 + * + * @param Page $page + * @param IContextSource $context + */ + public function __construct( Page $page, IContextSource $context = null ) { + if ( $context === null ) { + wfWarn( __METHOD__ . ' called without providing a Context object.' ); + // NOTE: We could try to initialize $context using $page->getContext(), + // if $page is an Article. That however seems to not work seamlessly. + } + + $this->page = $page; + $this->context = $context; + } + + /** + * Return the name of the action this object responds to + * @since 1.17 + * + * @return string Lowercase name + */ + abstract public function getName(); + + /** + * Get the permission required to perform this action. Often, but not always, + * the same as the action name + * @since 1.17 + * + * @return string|null + */ + public function getRestriction() { + return null; + } + + /** + * Checks if the given user (identified by an object) can perform this action. Can be + * overridden by sub-classes with more complicated permissions schemes. Failures here + * must throw subclasses of ErrorPageError + * @since 1.17 + * + * @param User $user The user to check, or null to use the context user + * @throws UserBlockedError|ReadOnlyError|PermissionsError + */ + protected function checkCanExecute( User $user ) { + $right = $this->getRestriction(); + if ( $right !== null ) { + $errors = $this->getTitle()->getUserPermissionsErrors( $right, $user ); + if ( count( $errors ) ) { + throw new PermissionsError( $right, $errors ); + } + } + + if ( $this->requiresUnblock() && $user->isBlocked() ) { + $block = $user->getBlock(); + throw new UserBlockedError( $block ); + } + + // This should be checked at the end so that the user won't think the + // error is only temporary when he also don't have the rights to execute + // this action + if ( $this->requiresWrite() && wfReadOnly() ) { + throw new ReadOnlyError(); + } + } + + /** + * Whether this action requires the wiki not to be locked + * @since 1.17 + * + * @return bool + */ + public function requiresWrite() { + return true; + } + + /** + * Whether this action can still be executed by a blocked user + * @since 1.17 + * + * @return bool + */ + public function requiresUnblock() { + return true; + } + + /** + * Set output headers for noindexing etc. This function will not be called through + * the execute() entry point, so only put UI-related stuff in here. + * @since 1.17 + */ + protected function setHeaders() { + $out = $this->getOutput(); + $out->setRobotPolicy( "noindex,nofollow" ); + $out->setPageTitle( $this->getPageTitle() ); + $out->setSubtitle( $this->getDescription() ); + $out->setArticleRelated( true ); + } + + /** + * Returns the name that goes in the \<h1\> page title + * + * @return string + */ + protected function getPageTitle() { + return $this->getTitle()->getPrefixedText(); + } + + /** + * Returns the description that goes below the \<h1\> tag + * @since 1.17 + * + * @return string + */ + protected function getDescription() { + return $this->msg( strtolower( $this->getName() ) )->escaped(); + } + + /** + * The main action entry point. Do all output for display and send it to the context + * output. Do not use globals $wgOut, $wgRequest, etc, in implementations; use + * $this->getOutput(), etc. + * @since 1.17 + * + * @throws ErrorPageError + */ + abstract public function show(); +} diff --git a/includes/actions/CachedAction.php b/includes/actions/CachedAction.php index bfdda7b9..bc4df349 100644 --- a/includes/actions/CachedAction.php +++ b/includes/actions/CachedAction.php @@ -58,7 +58,7 @@ abstract class CachedAction extends FormlessAction implements ICacheHelper { * If the cache is enabled or not. * * @since 1.20 - * @var boolean + * @var bool */ protected $cacheEnabled = true; @@ -66,7 +66,7 @@ abstract class CachedAction extends FormlessAction implements ICacheHelper { * Sets if the cache should be enabled or not. * * @since 1.20 - * @param boolean $cacheEnabled + * @param bool $cacheEnabled */ public function setCacheEnabled( $cacheEnabled ) { $this->cacheHelper->setCacheEnabled( $cacheEnabled ); @@ -78,8 +78,8 @@ abstract class CachedAction extends FormlessAction implements ICacheHelper { * * @since 1.20 * - * @param integer|null $cacheExpiry Sets the cache expiry, either ttl in seconds or unix timestamp. - * @param boolean|null $cacheEnabled Sets if the cache should be enabled or not. + * @param int|null $cacheExpiry Sets the cache expiry, either ttl in seconds or unix timestamp. + * @param bool|null $cacheEnabled Sets if the cache should be enabled or not. */ public function startCache( $cacheExpiry = null, $cacheEnabled = null ) { $this->cacheHelper = new CacheHelper(); @@ -110,7 +110,7 @@ abstract class CachedAction extends FormlessAction implements ICacheHelper { * * @since 1.20 * - * @param {function} $computeFunction + * @param callable $computeFunction * @param array|mixed $args * @param string|null $key * @@ -128,12 +128,13 @@ abstract class CachedAction extends FormlessAction implements ICacheHelper { * * @since 1.20 * - * @param {function} $computeFunction + * @param callable $computeFunction * @param array $args * @param string|null $key */ public function addCachedHTML( $computeFunction, $args = array(), $key = null ) { - $this->getOutput()->addHTML( $this->cacheHelper->getCachedValue( $computeFunction, $args, $key ) ); + $html = $this->cacheHelper->getCachedValue( $computeFunction, $args, $key ); + $this->getOutput()->addHTML( $html ); } /** @@ -147,11 +148,12 @@ abstract class CachedAction extends FormlessAction implements ICacheHelper { } /** - * Sets the time to live for the cache, in seconds or a unix timestamp indicating the point of expiry. + * Sets the time to live for the cache, in seconds or a unix timestamp + * indicating the point of expiry. * * @since 1.20 * - * @param integer $cacheExpiry + * @param int $cacheExpiry */ public function setExpiry( $cacheExpiry ) { $this->cacheHelper->setExpiry( $cacheExpiry ); @@ -177,12 +179,11 @@ abstract class CachedAction extends FormlessAction implements ICacheHelper { * * @since 1.20 * - * @param boolean $hasCached + * @param bool $hasCached */ public function onCacheInitialized( $hasCached ) { if ( $hasCached ) { $this->getOutput()->setSubtitle( $this->cacheHelper->getCachedNotice( $this->getContext() ) ); } } - } diff --git a/includes/actions/CreditsAction.php b/includes/actions/CreditsAction.php index 0a2bf306..e064aab4 100644 --- a/includes/actions/CreditsAction.php +++ b/includes/actions/CreditsAction.php @@ -39,7 +39,7 @@ class CreditsAction extends FormlessAction { /** * This is largely cadged from PageHistory::history * - * @return String HTML + * @return string HTML */ public function onView() { wfProfileIn( __METHOD__ ); @@ -58,9 +58,9 @@ class CreditsAction extends FormlessAction { /** * Get a list of contributors * - * @param int $cnt maximum list of contributors to show - * @param bool $showIfMax whether to contributors if there more than $cnt - * @return String: html + * @param int $cnt Maximum list of contributors to show + * @param bool $showIfMax Whether to contributors if there more than $cnt + * @return string Html */ public function getCredits( $cnt, $showIfMax = true ) { wfProfileIn( __METHOD__ ); @@ -74,13 +74,14 @@ class CreditsAction extends FormlessAction { } wfProfileOut( __METHOD__ ); + return $s; } /** * Get the last author with the last modification time * @param Page $page - * @return String HTML + * @return string HTML */ protected function getAuthor( Page $page ) { $user = User::newFromName( $page->getUserText(), false ); @@ -94,19 +95,29 @@ class CreditsAction extends FormlessAction { $d = ''; $t = ''; } + return $this->msg( 'lastmodifiedatby', $d, $t )->rawParams( $this->userLink( $user ) )->params( $user->getName() )->escaped(); } /** + * Whether we can display the user's real name (not a hidden pref) + * + * @since 1.24 + * @return bool + */ + protected function canShowRealUserName() { + $hiddenPrefs = $this->context->getConfig()->get( 'HiddenPrefs' ); + return !in_array( 'realname', $hiddenPrefs ); + } + + /** * Get a list of contributors of $article - * @param int $cnt maximum list of contributors to show - * @param bool $showIfMax whether to contributors if there more than $cnt - * @return String: html + * @param int $cnt Maximum list of contributors to show + * @param bool $showIfMax Whether to contributors if there more than $cnt + * @return string Html */ protected function getContributors( $cnt, $showIfMax ) { - global $wgHiddenPrefs; - $contributors = $this->page->getContributors(); $others_link = false; @@ -125,11 +136,12 @@ class CreditsAction extends FormlessAction { $anon_ips = array(); # Sift for real versus user names + /** @var $user User */ foreach ( $contributors as $user ) { $cnt--; if ( $user->isLoggedIn() ) { $link = $this->link( $user ); - if ( !in_array( 'realname', $wgHiddenPrefs ) && $user->getRealName() ) { + if ( $this->canShowRealUserName() && $user->getRealName() ) { $real_names[] = $link; } else { $user_names[] = $link; @@ -175,6 +187,7 @@ class CreditsAction extends FormlessAction { } $count = count( $fulllist ); + # "Based on work by ..." return $count ? $this->msg( 'othercontribs' )->rawParams( @@ -184,12 +197,11 @@ class CreditsAction extends FormlessAction { /** * Get a link to $user's user page - * @param $user User object - * @return String: html + * @param User $user + * @return string Html */ protected function link( User $user ) { - global $wgHiddenPrefs; - if ( !in_array( 'realname', $wgHiddenPrefs ) && !$user->isAnon() ) { + if ( $this->canShowRealUserName() && !$user->isAnon() ) { $real = $user->getRealName(); } else { $real = false; @@ -204,16 +216,15 @@ class CreditsAction extends FormlessAction { /** * Get a link to $user's user page - * @param $user User object - * @return String: html + * @param User $user + * @return string Html */ protected function userLink( User $user ) { $link = $this->link( $user ); if ( $user->isAnon() ) { return $this->msg( 'anonuser' )->rawParams( $link )->parse(); } else { - global $wgHiddenPrefs; - if ( !in_array( 'realname', $wgHiddenPrefs ) && $user->getRealName() ) { + if ( $this->canShowRealUserName() && $user->getRealName() ) { return $link; } else { return $this->msg( 'siteuser' )->rawParams( $link )->params( $user->getName() )->escaped(); @@ -223,7 +234,7 @@ class CreditsAction extends FormlessAction { /** * Get a link to action=credits of $article page - * @return String: HTML link + * @return string HTML link */ protected function othersLink() { return Linker::linkKnown( diff --git a/includes/actions/DeleteAction.php b/includes/actions/DeleteAction.php index db7123db..12f0dff0 100644 --- a/includes/actions/DeleteAction.php +++ b/includes/actions/DeleteAction.php @@ -41,9 +41,13 @@ class DeleteAction extends FormlessAction { } public function show() { - + if ( $this->getContext()->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) { + $out = $this->getOutput(); + $out->addModuleStyles( array( + 'mediawiki.ui.input', + 'mediawiki.ui.checkbox', + ) ); + } $this->page->delete(); - } - } diff --git a/includes/actions/EditAction.php b/includes/actions/EditAction.php index 3dd4c483..88767244 100644 --- a/includes/actions/EditAction.php +++ b/includes/actions/EditAction.php @@ -1,6 +1,6 @@ <?php /** - * action=edit / action=submit handler + * action=edit handler * * Copyright © 2012 Timo Tijhof * @@ -26,8 +26,7 @@ /** * Page edition handler * - * This is a wrapper that will call the EditPage class, or ExternalEdit - * if $wgUseExternalEditor is set to true and requested by the user. + * This is a wrapper that will call the EditPage class or a custom editor from an extension. * * @ingroup Actions */ @@ -42,6 +41,13 @@ class EditAction extends FormlessAction { } public function show() { + if ( $this->getContext()->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) { + $out = $this->getOutput(); + $out->addModuleStyles( array( + 'mediawiki.ui.input', + 'mediawiki.ui.checkbox', + ) ); + } $page = $this->page; $user = $this->getUser(); @@ -49,31 +55,5 @@ class EditAction extends FormlessAction { $editor = new EditPage( $page ); $editor->edit(); } - } - -} - -/** - * Edit submission handler - * - * This is the same as EditAction; except that it sets the session cookie. - * - * @ingroup Actions - */ -class SubmitAction extends EditAction { - - public function getName() { - return 'submit'; - } - - public function show() { - if ( session_id() == '' ) { - // Send a cookie so anons get talk message notifications - wfSetupSession(); - } - - parent::show(); - } - } diff --git a/includes/actions/FormAction.php b/includes/actions/FormAction.php new file mode 100644 index 00000000..4c9e85dd --- /dev/null +++ b/includes/actions/FormAction.php @@ -0,0 +1,123 @@ +<?php +/** + * Base classes for actions done on pages. + * + * 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 Actions + */ + +/** + * An action which shows a form and does something based on the input from the form + * + * @ingroup Actions + */ +abstract class FormAction extends Action { + + /** + * Get an HTMLForm descriptor array + * @return array + */ + abstract protected function getFormFields(); + + /** + * Add pre- or post-text to the form + * @return string HTML which will be sent to $form->addPreText() + */ + protected function preText() { + return ''; + } + + /** + * @return string + */ + protected function postText() { + return ''; + } + + /** + * Play with the HTMLForm if you need to more substantially + * @param HTMLForm $form + */ + protected function alterForm( HTMLForm $form ) { + } + + /** + * Get the HTMLForm to control behavior + * @return HTMLForm|null + */ + protected function getForm() { + $this->fields = $this->getFormFields(); + + // Give hooks a chance to alter the form, adding extra fields or text etc + wfRunHooks( 'ActionModifyFormFields', array( $this->getName(), &$this->fields, $this->page ) ); + + $form = new HTMLForm( $this->fields, $this->getContext(), $this->getName() ); + $form->setSubmitCallback( array( $this, 'onSubmit' ) ); + + // Retain query parameters (uselang etc) + $form->addHiddenField( 'action', $this->getName() ); // Might not be the same as the query string + $params = array_diff_key( + $this->getRequest()->getQueryValues(), + array( 'action' => null, 'title' => null ) + ); + $form->addHiddenField( 'redirectparams', wfArrayToCgi( $params ) ); + + $form->addPreText( $this->preText() ); + $form->addPostText( $this->postText() ); + $this->alterForm( $form ); + + // Give hooks a chance to alter the form, adding extra fields or text etc + wfRunHooks( 'ActionBeforeFormDisplay', array( $this->getName(), &$form, $this->page ) ); + + return $form; + } + + /** + * Process the form on POST submission. If you return false from getFormFields(), + * this will obviously never be reached. If you don't want to do anything with the + * form, just return false here + * @param array $data + * @return bool|array True for success, false for didn't-try, array of errors on failure + */ + abstract public function onSubmit( $data ); + + /** + * Do something exciting on successful processing of the form. This might be to show + * a confirmation message (watch, rollback, etc) or to redirect somewhere else (edit, + * protect, etc). + */ + abstract public function onSuccess(); + + /** + * The basic pattern for actions is to display some sort of HTMLForm UI, maybe with + * some stuff underneath (history etc); to do some processing on submission of that + * form (delete, protect, etc) and to do something exciting on 'success', be that + * display something new or redirect to somewhere. Some actions have more exotic + * behavior, but that's what subclassing is for :D + */ + public function show() { + $this->setHeaders(); + + // This will throw exceptions if there's a problem + $this->checkCanExecute( $this->getUser() ); + + $form = $this->getForm(); + if ( $form->show() ) { + $this->onSuccess(); + } + } +} diff --git a/includes/actions/FormlessAction.php b/includes/actions/FormlessAction.php new file mode 100644 index 00000000..a6f1e295 --- /dev/null +++ b/includes/actions/FormlessAction.php @@ -0,0 +1,45 @@ +<?php +/** + * Base classes for actions done on pages. + * + * 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 Actions + */ + +/** + * An action which just does something, without showing a form first. + * + * @ingroup Actions + */ +abstract class FormlessAction extends Action { + + /** + * Show something on GET request. + * @return string|null Will be added to the HTMLForm if present, or just added to the + * output if not. Return null to not add anything + */ + abstract public function onView(); + + public function show() { + $this->setHeaders(); + + // This will throw exceptions if there's a problem + $this->checkCanExecute( $this->getUser() ); + + $this->getOutput()->addHTML( $this->onView() ); + } +} diff --git a/includes/actions/HistoryAction.php b/includes/actions/HistoryAction.php index e58791ea..8522e560 100644 --- a/includes/actions/HistoryAction.php +++ b/includes/actions/HistoryAction.php @@ -37,6 +37,9 @@ class HistoryAction extends FormlessAction { const DIR_PREV = 0; const DIR_NEXT = 1; + /** @var array Array of message keys and strings */ + public $message; + public function getName() { return 'history'; } @@ -89,8 +92,6 @@ class HistoryAction extends FormlessAction { * Print the history page for an article. */ function onView() { - global $wgScript, $wgUseFileCache; - $out = $this->getOutput(); $request = $this->getRequest(); @@ -104,10 +105,12 @@ class HistoryAction extends FormlessAction { wfProfileIn( __METHOD__ ); $this->preCacheMessages(); + $config = $this->context->getConfig(); # Fill in the file cache if not set already - if ( $wgUseFileCache && HTMLFileCache::useFileCache( $this->getContext() ) ) { - $cache = HTMLFileCache::newFromTitle( $this->getTitle(), 'history' ); + $useFileCache = $config->get( 'UseFileCache' ); + if ( $useFileCache && HTMLFileCache::useFileCache( $this->getContext() ) ) { + $cache = new HTMLFileCache( $this->getTitle(), 'history' ); if ( !$cache->isCacheGood( /* Assume up to date */ ) ) { ob_start( array( &$cache, 'saveToFileCache' ) ); } @@ -116,12 +119,20 @@ class HistoryAction extends FormlessAction { // Setup page variables. $out->setFeedAppendQuery( 'action=history' ); $out->addModules( 'mediawiki.action.history' ); + if ( $config->get( 'UseMediaWikiUIEverywhere' ) ) { + $out = $this->getOutput(); + $out->addModuleStyles( array( + 'mediawiki.ui.input', + 'mediawiki.ui.checkbox', + ) ); + } // Handle atom/RSS feeds. $feedType = $request->getVal( 'feed' ); if ( $feedType ) { $this->feed( $feedType ); wfProfileOut( __METHOD__ ); + return; } @@ -141,6 +152,7 @@ class HistoryAction extends FormlessAction { ) ); wfProfileOut( __METHOD__ ); + return; } @@ -162,13 +174,13 @@ class HistoryAction extends FormlessAction { } if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) { $checkDeleted = Xml::checkLabel( $this->msg( 'history-show-deleted' )->text(), - 'deleted', 'mw-show-deleted-only', $request->getBool( 'deleted' ) ) . "\n"; + 'deleted', 'mw-show-deleted-only', $request->getBool( 'deleted' ) ) . "\n"; } else { $checkDeleted = ''; } // Add the general form - $action = htmlspecialchars( $wgScript ); + $action = htmlspecialchars( wfScript() ); $out->addHTML( "<form action=\"$action\" method=\"get\" id=\"mw-history-searchform\">" . Xml::fieldset( @@ -178,7 +190,10 @@ class HistoryAction extends FormlessAction { ) . Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . "\n" . Html::hidden( 'action', 'history' ) . "\n" . - Xml::dateMenu( ( $year == null ? MWTimestamp::getLocalInstance()->format( 'Y' ) : $year ), $month ) . ' ' . + Xml::dateMenu( + ( $year == null ? MWTimestamp::getLocalInstance()->format( 'Y' ) : $year ), + $month + ) . ' ' . ( $tagSelector ? ( implode( ' ', $tagSelector ) . ' ' ) : '' ) . $checkDeleted . Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ) . "\n" . @@ -204,9 +219,9 @@ class HistoryAction extends FormlessAction { * direction. This is now only used by the feeds. It was previously * used by the main UI but that's now handled by the pager. * - * @param $limit Integer: the limit number of revisions to get - * @param $offset Integer - * @param $direction Integer: either HistoryPage::DIR_PREV or HistoryPage::DIR_NEXT + * @param int $limit The limit number of revisions to get + * @param int $offset + * @param int $direction Either self::DIR_PREV or self::DIR_NEXT * @return ResultWrapper */ function fetchRevisions( $limit, $offset, $direction ) { @@ -217,9 +232,9 @@ class HistoryAction extends FormlessAction { $dbr = wfGetDB( DB_SLAVE ); - if ( $direction == HistoryPage::DIR_PREV ) { + if ( $direction === self::DIR_PREV ) { list( $dirs, $oper ) = array( "ASC", ">=" ); - } else { /* $direction == HistoryPage::DIR_NEXT */ + } else { /* $direction === self::DIR_NEXT */ list( $dirs, $oper ) = array( "DESC", "<=" ); } @@ -243,16 +258,17 @@ class HistoryAction extends FormlessAction { /** * Output a subscription feed listing recent edits to this page. * - * @param string $type feed type + * @param string $type Feed type */ function feed( $type ) { - global $wgFeedClasses, $wgFeedLimit; if ( !FeedUtils::checkFeedOutput( $type ) ) { return; } $request = $this->getRequest(); - $feed = new $wgFeedClasses[$type]( + $feedClasses = $this->context->getConfig()->get( 'FeedClasses' ); + /** @var RSSFeed|AtomFeed $feed */ + $feed = new $feedClasses[$type]( $this->getTitle()->getPrefixedText() . ' - ' . $this->msg( 'history-feed-title' )->inContentLanguage()->text(), $this->msg( 'history-feed-description' )->inContentLanguage()->text(), @@ -262,9 +278,12 @@ class HistoryAction extends FormlessAction { // Get a limit on number of feed entries. Provide a sane default // of 10 if none is defined (but limit to $wgFeedLimit max) $limit = $request->getInt( 'limit', 10 ); - $limit = min( max( $limit, 1 ), $wgFeedLimit ); + $limit = min( + max( $limit, 1 ), + $this->context->getConfig()->get( 'FeedLimit' ) + ); - $items = $this->fetchRevisions( $limit, 0, HistoryPage::DIR_NEXT ); + $items = $this->fetchRevisions( $limit, 0, self::DIR_NEXT ); // Generate feed elements enclosed between header and footer. $feed->outHeader(); @@ -294,7 +313,7 @@ class HistoryAction extends FormlessAction { * Borrows Recent Changes' feed generation functions for formatting; * includes a diff to the previous revision (if any). * - * @param $row Object: database row + * @param stdClass|array $row Database row * @return FeedItem */ function feedItem( $row ) { @@ -316,9 +335,10 @@ class HistoryAction extends FormlessAction { $wgContLang->time( $rev->getTimestamp() ) )->inContentLanguage()->text(); } else { $title = $rev->getUserText() . - $this->msg( 'colon-separator' )->inContentLanguage()->text() . - FeedItem::stripComment( $rev->getComment() ); + $this->msg( 'colon-separator' )->inContentLanguage()->text() . + FeedItem::stripComment( $rev->getComment() ); } + return new FeedItem( $title, $text, @@ -335,14 +355,28 @@ class HistoryAction extends FormlessAction { * @ingroup Actions */ class HistoryPager extends ReverseChronologicalPager { - public $lastRow = false, $counter, $historyPage, $buttons, $conds; + /** + * @var bool|stdClass + */ + public $lastRow = false; + + public $counter, $historyPage, $buttons, $conds; + protected $oldIdChecked; + protected $preventClickjacking = false; /** * @var array */ protected $parentLens; + /** + * @param HistoryAction $historyPage + * @param string $year + * @param string $month + * @param string $tagFilter + * @param array $conds + */ function __construct( $historyPage, $year = '', $month = '', $tagFilter = '', $conds = array() ) { parent::__construct( $historyPage->getContext() ); $this->historyPage = $historyPage; @@ -383,6 +417,7 @@ class HistoryPager extends ReverseChronologicalPager { $this->tagFilter ); wfRunHooks( 'PageHistoryPager::getQueryInfo', array( &$this, &$queryInfo ) ); + return $queryInfo; } @@ -390,6 +425,10 @@ class HistoryPager extends ReverseChronologicalPager { return 'rev_timestamp'; } + /** + * @param stdClass $row + * @return string + */ function formatRow( $row ) { if ( $this->lastRow ) { $latest = ( $this->counter == 1 && $this->mIsFirst ); @@ -401,6 +440,7 @@ class HistoryPager extends ReverseChronologicalPager { $s = ''; } $this->lastRow = $row; + return $s; } @@ -432,21 +472,24 @@ class HistoryPager extends ReverseChronologicalPager { * @return string HTML output */ function getStartBody() { - global $wgScript; $this->lastRow = false; $this->counter = 1; $this->oldIdChecked = 0; $this->getOutput()->wrapWikiMsg( "<div class='mw-history-legend'>\n$1\n</div>", 'histlegend' ); - $s = Html::openElement( 'form', array( 'action' => $wgScript, + $s = Html::openElement( 'form', array( 'action' => wfScript(), 'id' => 'mw-history-compare' ) ) . "\n"; $s .= Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . "\n"; $s .= Html::hidden( 'action', 'historysubmit' ) . "\n"; // Button container stored in $this->buttons for re-use in getEndBody() $this->buttons = '<div>'; + $className = 'historysubmit mw-history-compareselectedversions-button'; + if ( $this->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) { + $className .= ' mw-ui-button mw-ui-constructive'; + } $this->buttons .= $this->submitButton( $this->msg( 'compareselectedversions' )->text(), - array( 'class' => 'historysubmit mw-history-compareselectedversions-button' ) + array( 'class' => $className ) + Linker::tooltipAndAccesskeyAttribs( 'compareselectedversions' ) ) . "\n"; @@ -457,13 +500,15 @@ class HistoryPager extends ReverseChronologicalPager { $s .= $this->buttons; $s .= '<ul id="pagehistory">' . "\n"; + return $s; } private function getRevisionButton( $name, $msg ) { $this->preventClickjacking(); # Note bug #20966, <button> is non-standard in IE<8 - $element = Html::element( 'button', + $element = Html::element( + 'button', array( 'type' => 'submit', 'name' => $name, @@ -502,15 +547,16 @@ class HistoryPager extends ReverseChronologicalPager { $s .= $this->buttons; } $s .= '</form>'; + return $s; } /** * Creates a submit button * - * @param string $message text of the submit button, will be escaped - * @param array $attributes attributes - * @return String: HTML output for the submit button + * @param string $message Text of the submit button, will be escaped + * @param array $attributes Attributes + * @return string HTML output for the submit button */ function submitButton( $message, $attributes = array() ) { # Disable submit button if history has 1 revision only @@ -526,16 +572,17 @@ class HistoryPager extends ReverseChronologicalPager { * * @todo document some more, and maybe clean up the code (some params redundant?) * - * @param $row Object: the database row corresponding to the previous line. - * @param $next Mixed: the database row corresponding to the next line. (chronologically previous) - * @param $notificationtimestamp - * @param $latest Boolean: whether this row corresponds to the page's latest revision. - * @param $firstInList Boolean: whether this row corresponds to the first displayed on this history page. - * @return String: HTML output for the row + * @param stdClass $row The database row corresponding to the previous line. + * @param mixed $next The database row corresponding to the next line + * (chronologically previous) + * @param bool|string $notificationtimestamp + * @param bool $latest Whether this row corresponds to the page's latest revision. + * @param bool $firstInList Whether this row corresponds to the first + * displayed on this history page. + * @return string HTML output for the row */ function historyLine( $row, $next, $notificationtimestamp = false, - $latest = false, $firstInList = false ) - { + $latest = false, $firstInList = false ) { $rev = new Revision( $row ); $rev->setTitle( $this->getTitle() ); @@ -548,12 +595,14 @@ class HistoryPager extends ReverseChronologicalPager { $curlink = $this->curLink( $rev, $latest ); $lastlink = $this->lastLink( $rev, $next ); - $diffButtons = $this->diffButtons( $rev, $firstInList ); + $curLastlinks = $curlink . $this->historyPage->message['pipe-separator'] . $lastlink; $histLinks = Html::rawElement( - 'span', - array( 'class' => 'mw-history-histlinks' ), - $this->msg( 'parentheses' )->rawParams( $curlink . $this->historyPage->message['pipe-separator'] . $lastlink )->escaped() + 'span', + array( 'class' => 'mw-history-histlinks' ), + $this->msg( 'parentheses' )->rawParams( $curLastlinks )->escaped() ); + + $diffButtons = $this->diffButtons( $rev, $firstInList ); $s = $histLinks . $diffButtons; $link = $this->revLink( $rev ); @@ -627,7 +676,11 @@ class HistoryPager extends ReverseChronologicalPager { if ( $prevRev && $this->getTitle()->quickUserCan( 'edit', $user ) ) { if ( $latest && $this->getTitle()->quickUserCan( 'rollback', $user ) ) { // Get a rollback link without the brackets - $rollbackLink = Linker::generateRollback( $rev, $this->getContext(), array( 'verify', 'noBrackets' ) ); + $rollbackLink = Linker::generateRollback( + $rev, + $this->getContext(), + array( 'verify', 'noBrackets' ) + ); if ( $rollbackLink ) { $this->preventClickjacking(); $tools[] = $rollbackLink; @@ -635,8 +688,8 @@ class HistoryPager extends ReverseChronologicalPager { } if ( !$rev->isDeleted( Revision::DELETED_TEXT ) - && !$prevRev->isDeleted( Revision::DELETED_TEXT ) ) - { + && !$prevRev->isDeleted( Revision::DELETED_TEXT ) + ) { # Create undo tooltip for the first (=latest) line only $undoTooltip = $latest ? array( 'title' => $this->msg( 'tooltip-undo' )->text() ) @@ -686,8 +739,8 @@ class HistoryPager extends ReverseChronologicalPager { /** * Create a link to view this revision of the page * - * @param $rev Revision - * @return String + * @param Revision $rev + * @return string */ function revLink( $rev ) { $date = $this->getLanguage()->userTimeAndDate( $rev->getTimestamp(), $this->getUser() ); @@ -705,15 +758,16 @@ class HistoryPager extends ReverseChronologicalPager { if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) { $link = "<span class=\"history-deleted\">$link</span>"; } + return $link; } /** * Create a diff-to-current link for this revision for this page * - * @param $rev Revision - * @param $latest Boolean: this is the latest revision of the page? - * @return String + * @param Revision $rev + * @param bool $latest This is the latest revision of the page? + * @return string */ function curLink( $rev, $latest ) { $cur = $this->historyPage->message['cur']; @@ -735,18 +789,21 @@ class HistoryPager extends ReverseChronologicalPager { /** * Create a diff-to-previous link for this revision for this page. * - * @param $prevRev Revision: the previous revision - * @param $next Mixed: the newer revision - * @return String + * @param Revision $prevRev The revision being displayed + * @param stdClass|string|null $next The next revision in list (that is + * the previous one in chronological order). + * May either be a row, "unknown" or null. + * @return string */ function lastLink( $prevRev, $next ) { $last = $this->historyPage->message['last']; - # $next may either be a Row, null, or "unkown" - $nextRev = is_object( $next ) ? new Revision( $next ) : $next; - if ( is_null( $next ) ) { + + if ( $next === null ) { # Probably no next row return $last; - } elseif ( $next === 'unknown' ) { + } + + if ( $next === 'unknown' ) { # Next row probably exists but is unknown, use an oldid=prev link return Linker::linkKnown( $this->getTitle(), @@ -757,30 +814,34 @@ class HistoryPager extends ReverseChronologicalPager { 'oldid' => 'prev' ) ); - } elseif ( !$prevRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) - || !$nextRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) - { + } + + $nextRev = new Revision( $next ); + + if ( !$prevRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) + || !$nextRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) + ) { return $last; - } else { - return Linker::linkKnown( - $this->getTitle(), - $last, - array(), - array( - 'diff' => $prevRev->getId(), - 'oldid' => $next->rev_id - ) - ); } + + return Linker::linkKnown( + $this->getTitle(), + $last, + array(), + array( + 'diff' => $prevRev->getId(), + 'oldid' => $next->rev_id + ) + ); } /** * Create radio buttons for page history * - * @param $rev Revision object - * @param $firstInList Boolean: is this version the first one? + * @param Revision $rev + * @param bool $firstInList Is this version the first one? * - * @return String: HTML output for the radio buttons + * @return string HTML output for the radio buttons */ function diffButtons( $rev, $firstInList ) { if ( $this->getNumRows() > 1 ) { @@ -816,6 +877,7 @@ class HistoryPager extends ReverseChronologicalPager { array_merge( $radio, $checkmark, array( 'name' => 'diff', 'id' => "mw-diff-$id" ) ) ); + return $first . $second; } else { return ''; @@ -824,6 +886,7 @@ class HistoryPager extends ReverseChronologicalPager { /** * This is called if a write operation is possible from the generated HTML + * @param bool $enable */ function preventClickjacking( $enable = true ) { $this->preventClickjacking = $enable; @@ -837,16 +900,3 @@ class HistoryPager extends ReverseChronologicalPager { return $this->preventClickjacking; } } - -/** - * Backwards-compatibility alias - */ -class HistoryPage extends HistoryAction { - public function __construct( Page $article ) { # Just to make it public - parent::__construct( $article ); - } - - public function history() { - $this->onView(); - } -} diff --git a/includes/actions/InfoAction.php b/includes/actions/InfoAction.php index 0a16f4a8..f932a405 100644 --- a/includes/actions/InfoAction.php +++ b/includes/actions/InfoAction.php @@ -33,7 +33,7 @@ class InfoAction extends FormlessAction { /** * Returns the name of the action this object responds to. * - * @return string lowercase + * @return string Lowercase name */ public function getName() { return 'info'; @@ -153,6 +153,7 @@ class InfoAction extends FormlessAction { */ protected function makeHeader( $header ) { $spanAttribs = array( 'class' => 'mw-headline', 'id' => Sanitizer::escapeId( $header ) ); + return Html::rawElement( 'h2', array(), Html::element( 'span', $spanAttribs, $header ) ); } @@ -192,13 +193,13 @@ class InfoAction extends FormlessAction { * @return array */ protected function pageInfo() { - global $wgContLang, $wgRCMaxAge, $wgMemc, - $wgUnwatchedPageThreshold, $wgPageInfoTransclusionLimit; + global $wgContLang, $wgMemc; $user = $this->getUser(); $lang = $this->getLanguage(); $title = $this->getTitle(); $id = $title->getArticleID(); + $config = $this->context->getConfig(); $memcKey = wfMemcKey( 'infoaction', sha1( $title->getPrefixedText() ), $this->page->getLatest() ); @@ -206,7 +207,7 @@ class InfoAction extends FormlessAction { $version = isset( $pageCounts['cacheversion'] ) ? $pageCounts['cacheversion'] : false; if ( $pageCounts === false || $version !== self::CACHE_VERSION ) { // Get page information that would be too "expensive" to retrieve by normal means - $pageCounts = self::pageCounts( $title ); + $pageCounts = $this->pageCounts( $title ); $pageCounts['cacheversion'] = self::CACHE_VERSION; $wgMemc->set( $memcKey, $pageCounts ); @@ -274,15 +275,37 @@ class InfoAction extends FormlessAction { // Language in which the page content is (supposed to be) written $pageLang = $title->getPageLanguage()->getCode(); - $pageInfo['header-basic'][] = array( $this->msg( 'pageinfo-language' ), + + if ( $config->get( 'PageLanguageUseDB' ) && $this->getTitle()->userCan( 'pagelang' ) ) { + // Link to Special:PageLanguage with pre-filled page title if user has permissions + $titleObj = SpecialPage::getTitleFor( 'PageLanguage', $title->getPrefixedText() ); + $langDisp = Linker::link( + $titleObj, + $this->msg( 'pageinfo-language' )->escaped() + ); + } else { + // Display just the message + $langDisp = $this->msg( 'pageinfo-language' )->escaped(); + } + + $pageInfo['header-basic'][] = array( $langDisp, Language::fetchLanguageName( $pageLang, $lang->getCode() ) . ' ' . $this->msg( 'parentheses', $pageLang ) ); + // Content model of the page + $pageInfo['header-basic'][] = array( + $this->msg( 'pageinfo-content-model' ), + ContentHandler::getLocalizedName( $title->getContentModel() ) + ); + // Search engine status $pOutput = new ParserOutput(); if ( isset( $pageProperties['noindex'] ) ) { $pOutput->setIndexPolicy( 'noindex' ); } + if ( isset( $pageProperties['index'] ) ) { + $pOutput->setIndexPolicy( 'index' ); + } // Use robot policy logic $policy = $this->page->getRobotPolicy( 'view', $pOutput ); @@ -298,19 +321,20 @@ class InfoAction extends FormlessAction { ); } + $unwatchedPageThreshold = $config->get( 'UnwatchedPageThreshold' ); if ( $user->isAllowed( 'unwatchedpages' ) || - ( $wgUnwatchedPageThreshold !== false && - $pageCounts['watchers'] >= $wgUnwatchedPageThreshold ) + ( $unwatchedPageThreshold !== false && + $pageCounts['watchers'] >= $unwatchedPageThreshold ) ) { // Number of page watchers $pageInfo['header-basic'][] = array( $this->msg( 'pageinfo-watchers' ), $lang->formatNum( $pageCounts['watchers'] ) ); - } elseif ( $wgUnwatchedPageThreshold !== false ) { + } elseif ( $unwatchedPageThreshold !== false ) { $pageInfo['header-basic'][] = array( $this->msg( 'pageinfo-watchers' ), - $this->msg( 'pageinfo-few-watchers' )->numParams( $wgUnwatchedPageThreshold ) + $this->msg( 'pageinfo-few-watchers' )->numParams( $unwatchedPageThreshold ) ); } @@ -498,7 +522,7 @@ class InfoAction extends FormlessAction { // Recent number of edits (within past 30 days) $pageInfo['header-edits'][] = array( - $this->msg( 'pageinfo-recent-edits', $lang->formatDuration( $wgRCMaxAge ) ), + $this->msg( 'pageinfo-recent-edits', $lang->formatDuration( $config->get( 'RCMaxAge' ) ) ), $lang->formatNum( $pageCounts['recent_edits'] ) ); @@ -532,9 +556,13 @@ class InfoAction extends FormlessAction { $pageCounts['transclusion']['from'] > 0 || $pageCounts['transclusion']['to'] > 0 ) { - $options = array( 'LIMIT' => $wgPageInfoTransclusionLimit ); + $options = array( 'LIMIT' => $config->get( 'PageInfoTransclusionLimit' ) ); $transcludedTemplates = $title->getTemplateLinksFrom( $options ); - $transcludedTargets = $title->getTemplateLinksTo( $options ); + if ( $config->get( 'MiserMode' ) ) { + $transcludedTargets = array(); + } else { + $transcludedTargets = $title->getTemplateLinksTo( $options ); + } // Page properties $pageInfo['header-properties'] = array(); @@ -575,7 +603,7 @@ class InfoAction extends FormlessAction { ); } - if ( $pageCounts['transclusion']['to'] > 0 ) { + if ( !$config->get( 'MiserMode' ) && $pageCounts['transclusion']['to'] > 0 ) { if ( $pageCounts['transclusion']['to'] > count( $transcludedTargets ) ) { $more = Linker::link( $whatLinksHere, @@ -608,16 +636,15 @@ class InfoAction extends FormlessAction { * @param Title $title Title to get counts for * @return array */ - protected static function pageCounts( Title $title ) { - global $wgRCMaxAge, $wgDisableCounters; - + protected function pageCounts( Title $title ) { wfProfileIn( __METHOD__ ); $id = $title->getArticleID(); + $config = $this->context->getConfig(); $dbr = wfGetDB( DB_SLAVE ); $result = array(); - if ( !$wgDisableCounters ) { + if ( !$config->get( 'DisableCounters' ) ) { // Number of views $views = (int)$dbr->selectField( 'page', @@ -658,8 +685,8 @@ class InfoAction extends FormlessAction { ); $result['authors'] = $authors; - // "Recent" threshold defined by $wgRCMaxAge - $threshold = $dbr->timestamp( time() - $wgRCMaxAge ); + // "Recent" threshold defined by RCMaxAge setting + $threshold = $dbr->timestamp( time() - $config->get( 'RCMaxAge' ) ); // Recent number of edits $edits = (int)$dbr->selectField( @@ -713,15 +740,19 @@ class InfoAction extends FormlessAction { } // Counts for the number of transclusion links (to/from) - $result['transclusion']['to'] = (int)$dbr->selectField( - 'templatelinks', - 'COUNT(tl_from)', - array( - 'tl_namespace' => $title->getNamespace(), - 'tl_title' => $title->getDBkey() - ), - __METHOD__ - ); + if ( $config->get( 'MiserMode' ) ) { + $result['transclusion']['to'] = 0; + } else { + $result['transclusion']['to'] = (int)$dbr->selectField( + 'templatelinks', + 'COUNT(tl_from)', + array( + 'tl_namespace' => $title->getNamespace(), + 'tl_title' => $title->getDBkey() + ), + __METHOD__ + ); + } $result['transclusion']['from'] = (int)$dbr->selectField( 'templatelinks', @@ -731,6 +762,7 @@ class InfoAction extends FormlessAction { ); wfProfileOut( __METHOD__ ); + return $result; } @@ -745,25 +777,25 @@ class InfoAction extends FormlessAction { /** * Get a list of contributors of $article - * @return string: html + * @return string Html */ protected function getContributors() { - global $wgHiddenPrefs; - $contributors = $this->page->getContributors(); $real_names = array(); $user_names = array(); $anon_ips = array(); # Sift for real versus user names + /** @var $user User */ foreach ( $contributors as $user ) { $page = $user->isAnon() ? SpecialPage::getTitleFor( 'Contributions', $user->getName() ) : $user->getUserPage(); + $hiddenPrefs = $this->context->getConfig()->get( 'HiddenPrefs' ); if ( $user->getID() == 0 ) { $anon_ips[] = Linker::link( $page, htmlspecialchars( $user->getName() ) ); - } elseif ( !in_array( 'realname', $wgHiddenPrefs ) && $user->getRealName() ) { + } elseif ( !in_array( 'realname', $hiddenPrefs ) && $user->getRealName() ) { $real_names[] = Linker::link( $page, htmlspecialchars( $user->getRealName() ) ); } else { $user_names[] = Linker::link( $page, htmlspecialchars( $user->getName() ) ); @@ -798,6 +830,7 @@ class InfoAction extends FormlessAction { } $count = count( $fulllist ); + # "Based on work by ..." return $count ? $this->msg( 'othercontribs' )->rawParams( diff --git a/includes/actions/MarkpatrolledAction.php b/includes/actions/MarkpatrolledAction.php index ff6cf13a..4016f672 100644 --- a/includes/actions/MarkpatrolledAction.php +++ b/includes/actions/MarkpatrolledAction.php @@ -70,6 +70,7 @@ class MarkpatrolledAction extends FormlessAction { $this->getOutput()->setPageTitle( $this->msg( 'markedaspatrollederror' ) ); $this->getOutput()->addWikiMsg( 'markedaspatrollederror-noautopatrol' ); $this->getOutput()->returnToMain( null, $return ); + return; } diff --git a/includes/actions/ProtectAction.php b/includes/actions/ProtectAction.php index ec6648e2..a7f1ac34 100644 --- a/includes/actions/ProtectAction.php +++ b/includes/actions/ProtectAction.php @@ -41,30 +41,15 @@ class ProtectAction extends FormlessAction { } public function show() { + if ( $this->getContext()->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) { + $out = $this->getOutput(); + $out->addModuleStyles( array( + 'mediawiki.ui.input', + 'mediawiki.ui.checkbox', + ) ); + } $this->page->protect(); - } - } -/** - * Handle page unprotection - * - * This is a wrapper that will call Article::unprotect(). - * - * @ingroup Actions - */ -class UnprotectAction extends ProtectAction { - - public function getName() { - return 'unprotect'; - } - - public function show() { - - $this->page->unprotect(); - - } - -} diff --git a/includes/actions/RawAction.php b/includes/actions/RawAction.php index 1a2b3cb1..d0d956ec 100644 --- a/includes/actions/RawAction.php +++ b/includes/actions/RawAction.php @@ -5,7 +5,7 @@ * Copyright © 2004 Gabriel Wicke <wicke@wikidev.net> * http://wikidev.net/ * - * Based on HistoryPage and SpecialExport + * Based on HistoryAction and SpecialExport * * 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 @@ -33,7 +33,13 @@ * @ingroup Actions */ class RawAction extends FormlessAction { - private $mGen; + /** + * @var bool Does the request include a gen=css|javascript parameter + * @deprecated This used to be a string for "css" or "javascript" but + * it is no longer used. Setting this parameter results in empty content + * being served + */ + private $gen = false; public function getName() { return 'raw'; @@ -48,10 +54,9 @@ class RawAction extends FormlessAction { } function onView() { - global $wgSquidMaxage, $wgForcedRawSMaxage; - $this->getOutput()->disable(); $request = $this->getRequest(); + $config = $this->context->getConfig(); if ( !$request->checkUrlExtension() ) { return; @@ -67,26 +72,25 @@ class RawAction extends FormlessAction { $smaxage = $request->getIntOrNull( 'smaxage' ); if ( $gen == 'css' || $gen == 'js' ) { - $this->mGen = $gen; + $this->gen = true; if ( $smaxage === null ) { - $smaxage = $wgSquidMaxage; + $smaxage = $config->get( 'SquidMaxage' ); } - } else { - $this->mGen = false; } $contentType = $this->getContentType(); - # Force caching for CSS and JS raw content, default: 5 minutes + # Force caching for CSS and JS raw content, default: 5 minutes. + # Note: If using a canonical url for userpage css/js, we send an HTCP purge. if ( $smaxage === null ) { if ( $contentType == 'text/css' || $contentType == 'text/javascript' ) { - $smaxage = intval( $wgForcedRawSMaxage ); + $smaxage = intval( $config->get( 'ForcedRawSMaxage' ) ); } else { $smaxage = 0; } } - $maxage = $request->getInt( 'maxage', $wgSquidMaxage ); + $maxage = $request->getInt( 'maxage', $config->get( 'SquidMaxage' ) ); $response = $request->response(); @@ -99,7 +103,9 @@ class RawAction extends FormlessAction { $privateCache = $privateCache ?: $this->getUser()->isLoggedIn(); # allow the client to cache this for 24 hours $mode = $privateCache ? 'private' : 'public'; - $response->header( 'Cache-Control: ' . $mode . ', s-maxage=' . $smaxage . ', max-age=' . $maxage ); + $response->header( + 'Cache-Control: ' . $mode . ', s-maxage=' . $smaxage . ', max-age=' . $maxage + ); $text = $this->getRawText(); @@ -122,13 +128,13 @@ class RawAction extends FormlessAction { * Get the text that should be returned, or false if the page or revision * was not found. * - * @return String|Bool + * @return string|bool */ public function getRawText() { global $wgParser; # No longer used - if ( $this->mGen ) { + if ( $this->gen ) { return ''; } @@ -138,8 +144,9 @@ class RawAction extends FormlessAction { // If it's a MediaWiki message we can just hit the message cache if ( $request->getBool( 'usemsgcache' ) && $title->getNamespace() == NS_MEDIAWIKI ) { - // The first "true" is to use the database, the second is to use the content langue - // and the last one is to specify the message key already contains the language in it ("/de", etc.) + // The first "true" is to use the database, the second is to use + // the content langue and the last one is to specify the message + // key already contains the language in it ("/de", etc.). $text = MessageCache::singleton()->get( $title->getDBkey(), true, true, true ); // If the message doesn't exist, return a blank if ( $text === false ) { @@ -161,7 +168,7 @@ class RawAction extends FormlessAction { } elseif ( !$content instanceof TextContent ) { // non-text content wfHttpError( 415, "Unsupported Media Type", "The requested page uses the content model `" - . $content->getModel() . "` which is not supported via this interface." ); + . $content->getModel() . "` which is not supported via this interface." ); die(); } else { // want a section? @@ -181,7 +188,11 @@ class RawAction extends FormlessAction { } if ( $text !== false && $text !== '' && $request->getVal( 'templates' ) === 'expand' ) { - $text = $wgParser->preprocess( $text, $title, ParserOptions::newFromContext( $this->getContext() ) ); + $text = $wgParser->preprocess( + $text, + $title, + ParserOptions::newFromContext( $this->getContext() ) + ); } return $text; @@ -190,17 +201,18 @@ class RawAction extends FormlessAction { /** * Get the ID of the revision that should used to get the text. * - * @return Integer + * @return int */ public function getOldId() { $oldid = $this->getRequest()->getInt( 'oldid' ); switch ( $this->getRequest()->getText( 'direction' ) ) { case 'next': # output next revision, or nothing if there isn't one + $nextid = 0; if ( $oldid ) { - $oldid = $this->getTitle()->getNextRevisionID( $oldid ); + $nextid = $this->getTitle()->getNextRevisionID( $oldid ); } - $oldid = $oldid ? $oldid : -1; + $oldid = $nextid ?: -1; break; case 'prev': # output previous revision, or nothing if there isn't one @@ -208,20 +220,21 @@ class RawAction extends FormlessAction { # get the current revision so we can get the penultimate one $oldid = $this->page->getLatest(); } - $prev = $this->getTitle()->getPreviousRevisionID( $oldid ); - $oldid = $prev ? $prev : -1; + $previd = $this->getTitle()->getPreviousRevisionID( $oldid ); + $oldid = $previd ?: -1; break; case 'cur': $oldid = 0; break; } + return $oldid; } /** * Get the content type to use for the response * - * @return String + * @return string */ public function getContentType() { $ctype = $this->getRequest()->getVal( 'ctype' ); @@ -243,39 +256,3 @@ class RawAction extends FormlessAction { return $ctype; } } - -/** - * Backward compatibility for extensions - * - * @deprecated in 1.19 - */ -class RawPage extends RawAction { - public $mOldId; - - /** - * @param Page $page - * @param WebRequest|bool $request The WebRequest (default: false). - */ - function __construct( Page $page, $request = false ) { - wfDeprecated( __CLASS__, '1.19' ); - parent::__construct( $page ); - - if ( $request !== false ) { - $context = new DerivativeContext( $this->getContext() ); - $context->setRequest( $request ); - $this->context = $context; - } - } - - public function view() { - $this->onView(); - } - - public function getOldId() { - # Some extensions like to set $mOldId - if ( $this->mOldId !== null ) { - return $this->mOldId; - } - return parent::getOldId(); - } -} diff --git a/includes/actions/RenderAction.php b/includes/actions/RenderAction.php index 3d244fb3..16e407f4 100644 --- a/includes/actions/RenderAction.php +++ b/includes/actions/RenderAction.php @@ -41,9 +41,6 @@ class RenderAction extends FormlessAction { } public function show() { - $this->page->render(); - } - } diff --git a/includes/actions/RevertAction.php b/includes/actions/RevertAction.php index a5fc4e17..6481630e 100644 --- a/includes/actions/RevertAction.php +++ b/includes/actions/RevertAction.php @@ -24,29 +24,14 @@ */ /** - * Dummy class for pages not in NS_FILE - * - * @ingroup Actions - */ -class RevertAction extends Action { - - public function getName() { - return 'revert'; - } - - public function show() { - $this->getOutput()->showErrorPage( 'nosuchaction', 'nosuchactiontext' ); - } - - public function execute() {} -} - -/** - * Class for pages in NS_FILE + * File reversion user interface * * @ingroup Actions */ -class RevertFileAction extends FormAction { +class RevertAction extends FormAction { + /** + * @var OldLocalFile + */ protected $oldFile; public function getName() { @@ -58,17 +43,24 @@ class RevertFileAction extends FormAction { } protected function checkCanExecute( User $user ) { + if ( $this->getTitle()->getNamespace() !== NS_FILE ) { + throw new ErrorPageError( $this->msg( 'nosuchaction' ), $this->msg( 'nosuchactiontext' ) ); + } parent::checkCanExecute( $user ); $oldimage = $this->getRequest()->getText( 'oldimage' ); if ( strlen( $oldimage ) < 16 || strpos( $oldimage, '/' ) !== false - || strpos( $oldimage, '\\' ) !== false ) - { + || strpos( $oldimage, '\\' ) !== false + ) { throw new ErrorPageError( 'internalerror', 'unexpected', array( 'oldimage', $oldimage ) ); } - $this->oldFile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->getTitle(), $oldimage ); + $this->oldFile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( + $this->getTitle(), + $oldimage + ); + if ( !$this->oldFile->exists() ) { throw new ErrorPageError( '', 'filerevert-badversion' ); } @@ -78,6 +70,7 @@ class RevertFileAction extends FormAction { $form->setWrapperLegendMsg( 'filerevert-legend' ); $form->setSubmitTextMsg( 'filerevert-submit' ); $form->addHiddenField( 'oldimage', $this->getRequest()->getText( 'oldimage' ) ); + $form->setTokenSalt( array( 'revert', $this->getTitle()->getPrefixedDBkey() ) ); } protected function getFormFields() { @@ -99,8 +92,10 @@ class RevertFileAction extends FormAction { 'raw' => true, 'default' => $this->msg( 'filerevert-intro', $this->getTitle()->getText(), $userDate, $userTime, - wfExpandUrl( $this->page->getFile()->getArchiveUrl( $this->getRequest()->getText( 'oldimage' ) ), - PROTO_CURRENT ) )->parseAsBlock() + wfExpandUrl( + $this->page->getFile()->getArchiveUrl( $this->getRequest()->getText( 'oldimage' ) ), + PROTO_CURRENT + ) )->parseAsBlock() ), 'comment' => array( 'type' => 'text', @@ -112,10 +107,21 @@ class RevertFileAction extends FormAction { } public function onSubmit( $data ) { - $source = $this->page->getFile()->getArchiveVirtualUrl( $this->getRequest()->getText( 'oldimage' ) ); + $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, 0, false, false, $this->getUser() ); + return $this->page->getFile()->upload( + $source, + $comment, + $comment, + 0, + false, + false, + $this->getUser() + ); } public function onSuccess() { @@ -139,6 +145,7 @@ class RevertFileAction extends FormAction { protected function getDescription() { $this->getOutput()->addBacklinkSubtitle( $this->getTitle() ); + return ''; } } diff --git a/includes/actions/RevisiondeleteAction.php b/includes/actions/RevisiondeleteAction.php index 2949fa95..b6eeb7b4 100644 --- a/includes/actions/RevisiondeleteAction.php +++ b/includes/actions/RevisiondeleteAction.php @@ -49,6 +49,7 @@ class RevisiondeleteAction extends FormlessAction { public function show() { $special = SpecialPageFactory::getPage( 'Revisiondelete' ); $special->setContext( $this->getContext() ); + $special->getContext()->setTitle( $special->getPageTitle() ); $special->run( '' ); } } diff --git a/includes/actions/RollbackAction.php b/includes/actions/RollbackAction.php index 81bad9da..76d70d70 100644 --- a/includes/actions/RollbackAction.php +++ b/includes/actions/RollbackAction.php @@ -39,6 +39,7 @@ class RollbackAction extends FormlessAction { $details = null; $request = $this->getRequest(); + $user = $this->getUser(); $result = $this->page->doRollback( $request->getVal( 'from' ), @@ -53,13 +54,16 @@ class RollbackAction extends FormlessAction { throw new ThrottledError; } - if ( isset( $result[0][0] ) && ( $result[0][0] == 'alreadyrolled' || $result[0][0] == 'cantrollback' ) ) { + if ( isset( $result[0][0] ) && + ( $result[0][0] == 'alreadyrolled' || $result[0][0] == 'cantrollback' ) + ) { $this->getOutput()->setPageTitle( $this->msg( 'rollbackfailed' ) ); $errArray = $result[0]; $errMsg = array_shift( $errArray ); $this->getOutput()->addWikiMsgArray( $errMsg, $errArray ); if ( isset( $details['current'] ) ) { + /** @var Revision $current */ $current = $details['current']; if ( $current->getComment() != '' ) { @@ -83,6 +87,7 @@ class RollbackAction extends FormlessAction { throw new ErrorPageError( 'rollbackfailed', $error[0], array_slice( $error, 1 ) ); } + /** @var Revision $current */ $current = $details['current']; $target = $details['target']; $newId = $details['newid']; @@ -91,12 +96,26 @@ class RollbackAction extends FormlessAction { $old = Linker::revUserTools( $current ); $new = Linker::revUserTools( $target ); - $this->getOutput()->addHTML( $this->msg( 'rollback-success' )->rawParams( $old, $new )->parseAsBlock() ); + $this->getOutput()->addHTML( $this->msg( 'rollback-success' )->rawParams( $old, $new ) + ->parseAsBlock() ); + + if ( $user->getBoolOption( 'watchrollback' ) ) { + $user->addWatch( $this->page->getTitle(), WatchedItem::IGNORE_USER_RIGHTS ); + } + $this->getOutput()->returnToMain( false, $this->getTitle() ); - if ( !$request->getBool( 'hidediff', false ) && !$this->getUser()->getBoolOption( 'norollbackdiff', false ) ) { + if ( !$request->getBool( 'hidediff', false ) && + !$this->getUser()->getBoolOption( 'norollbackdiff', false ) + ) { $contentHandler = $current->getContentHandler(); - $de = $contentHandler->createDifferenceEngine( $this->getContext(), $current->getId(), $newId, false, true ); + $de = $contentHandler->createDifferenceEngine( + $this->getContext(), + $current->getId(), + $newId, + false, + true + ); $de->showDiff( '', '' ); } } diff --git a/includes/actions/SubmitAction.php b/includes/actions/SubmitAction.php new file mode 100644 index 00000000..fae49f61 --- /dev/null +++ b/includes/actions/SubmitAction.php @@ -0,0 +1,42 @@ +<?php +/** + * Wrapper for EditAction; sets the session cookie. + * + * 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 Actions + */ + +/** + * This is the same as EditAction; except that it sets the session cookie. + * + * @ingroup Actions + */ +class SubmitAction extends EditAction { + + public function getName() { + return 'submit'; + } + + public function show() { + if ( session_id() === '' ) { + // Send a cookie so anons get talk message notifications + wfSetupSession(); + } + + parent::show(); + } +} diff --git a/includes/actions/UnprotectAction.php b/includes/actions/UnprotectAction.php new file mode 100644 index 00000000..bc28c8ed --- /dev/null +++ b/includes/actions/UnprotectAction.php @@ -0,0 +1,43 @@ +<?php +/** + * action=unprotect handler + * + * Copyright © 2012 Timo Tijhof + * + * 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 Actions + * @author Timo Tijhof + */ + +/** + * Handle page unprotection + * + * This is a wrapper that will call Article::unprotect(). + * + * @ingroup Actions + */ +class UnprotectAction extends ProtectAction { + + public function getName() { + return 'unprotect'; + } + + public function show() { + + $this->page->unprotect(); + } +} diff --git a/includes/actions/UnwatchAction.php b/includes/actions/UnwatchAction.php new file mode 100644 index 00000000..e2e5a1d8 --- /dev/null +++ b/includes/actions/UnwatchAction.php @@ -0,0 +1,57 @@ +<?php +/** + * Performs the unwatch actions on a page + * + * 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 Actions + */ + +/** + * Page removal from a user's watchlist + * + * @ingroup Actions + */ +class UnwatchAction extends WatchAction { + + public function getName() { + return 'unwatch'; + } + + protected function getDescription() { + return $this->msg( 'removewatch' )->escaped(); + } + + public function onSubmit( $data ) { + wfProfileIn( __METHOD__ ); + self::doUnwatch( $this->getTitle(), $this->getUser() ); + wfProfileOut( __METHOD__ ); + + return true; + } + + protected function alterForm( HTMLForm $form ) { + $form->setSubmitTextMsg( 'confirm-unwatch-button' ); + } + + protected function preText() { + return $this->msg( 'confirm-unwatch-top' )->parse(); + } + + public function onSuccess() { + $this->getOutput()->addWikiMsg( 'removedwatchtext', $this->getTitle()->getPrefixedText() ); + } +} diff --git a/includes/actions/ViewAction.php b/includes/actions/ViewAction.php index e227197d..3a24565c 100644 --- a/includes/actions/ViewAction.php +++ b/includes/actions/ViewAction.php @@ -43,5 +43,4 @@ class ViewAction extends FormlessAction { public function show() { $this->page->view(); } - } diff --git a/includes/actions/WatchAction.php b/includes/actions/WatchAction.php index 929c1b5f..8c9a46a5 100644 --- a/includes/actions/WatchAction.php +++ b/includes/actions/WatchAction.php @@ -1,6 +1,6 @@ <?php /** - * Performs the watch and unwatch actions on a page + * Performs the watch actions on a page * * 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 @@ -51,6 +51,7 @@ class WatchAction extends FormAction { wfProfileIn( __METHOD__ ); self::doWatch( $this->getTitle(), $this->getUser() ); wfProfileOut( __METHOD__ ); + return true; } @@ -81,10 +82,10 @@ class WatchAction extends FormAction { protected function checkCanExecute( User $user ) { // Must be logged in if ( $user->isAnon() ) { - throw new ErrorPageError( 'watchnologin', 'watchnologintext' ); + throw new UserNotLoggedIn( 'watchlistanontext', 'watchnologin' ); } - return parent::checkCanExecute( $user ); + parent::checkCanExecute( $user ); } /** @@ -96,7 +97,9 @@ class WatchAction extends FormAction { * @return Status */ public static function doWatchOrUnwatch( $watch, Title $title, User $user ) { - if ( $user->isLoggedIn() && $user->isWatched( $title, WatchedItem::IGNORE_USER_RIGHTS ) != $watch ) { + if ( $user->isLoggedIn() && + $user->isWatched( $title, WatchedItem::IGNORE_USER_RIGHTS ) != $watch + ) { // If the user doesn't have 'editmywatchlist', we still want to // allow them to add but not remove items via edits and such. if ( $watch ) { @@ -105,6 +108,7 @@ class WatchAction extends FormAction { return self::doUnwatch( $title, $user ); } } + return Status::newGood(); } @@ -116,8 +120,12 @@ class WatchAction extends FormAction { * @param int $checkRights Passed through to $user->addWatch() * @return Status */ - public static function doWatch( Title $title, User $user, $checkRights = WatchedItem::CHECK_USER_RIGHTS ) { - if ( $checkRights !== WatchedItem::IGNORE_USER_RIGHTS && !$user->isAllowed( 'editmywatchlist' ) ) { + public static function doWatch( Title $title, User $user, + $checkRights = WatchedItem::CHECK_USER_RIGHTS + ) { + if ( $checkRights !== WatchedItem::IGNORE_USER_RIGHTS && + !$user->isAllowed( 'editmywatchlist' ) + ) { return User::newFatalPermissionDeniedStatus( 'editmywatchlist' ); } @@ -129,6 +137,7 @@ class WatchAction extends FormAction { $user->addWatch( $title, $checkRights ); wfRunHooks( 'WatchArticleComplete', array( &$user, &$page ) ); } + return $status; } @@ -152,6 +161,7 @@ class WatchAction extends FormAction { $user->removeWatch( $title ); wfRunHooks( 'UnwatchArticleComplete', array( &$user, &$page ) ); } + return $status; } @@ -168,7 +178,7 @@ class WatchAction extends FormAction { if ( $action != 'unwatch' ) { $action = 'watch'; } - $salt = array( $action, $title->getDBkey() ); + $salt = array( $action, $title->getPrefixedDBkey() ); // This token stronger salted and not compatible with ApiWatch // It's title/action specific because index.php is GET and API is POST @@ -200,38 +210,3 @@ class WatchAction extends FormAction { $this->getOutput()->addWikiMsg( 'addedwatchtext', $this->getTitle()->getPrefixedText() ); } } - -/** - * Page removal from a user's watchlist - * - * @ingroup Actions - */ -class UnwatchAction extends WatchAction { - - public function getName() { - return 'unwatch'; - } - - protected function getDescription() { - return $this->msg( 'removewatch' )->escaped(); - } - - public function onSubmit( $data ) { - wfProfileIn( __METHOD__ ); - self::doUnwatch( $this->getTitle(), $this->getUser() ); - wfProfileOut( __METHOD__ ); - return true; - } - - protected function alterForm( HTMLForm $form ) { - $form->setSubmitTextMsg( 'confirm-unwatch-button' ); - } - - protected function preText() { - return $this->msg( 'confirm-unwatch-top' )->parse(); - } - - public function onSuccess() { - $this->getOutput()->addWikiMsg( 'removedwatchtext', $this->getTitle()->getPrefixedText() ); - } -} |