Revert r86001: Brion says it's too scary :D will recommit in pieces
This commit is contained in:
parent
887f4eff82
commit
bc4a096805
18 changed files with 635 additions and 1173 deletions
|
|
@ -264,17 +264,6 @@ $reason: the reason for the move (added in 1.13)
|
|||
$user: the User object about to be created (read-only, incomplete)
|
||||
$message: out parameter: error message to display on abort
|
||||
|
||||
'ActionBeforeFormDisplay': Modify the form shown for an action (added 1.18)
|
||||
$action: String
|
||||
$form: HTMLForm
|
||||
$page: Article
|
||||
|
||||
'ActionModifyFormFields': Modify the descriptor array which will be used to create an
|
||||
action form
|
||||
$action: String
|
||||
$fields: Array
|
||||
$page: Article
|
||||
|
||||
'AddNewAccount': after a user account is created
|
||||
$user: the User object that was created. (Parameter added in 1.7)
|
||||
$byEmail: true when account was created "by email" (added in 1.12)
|
||||
|
|
@ -395,6 +384,12 @@ the database
|
|||
$article: the article (object) being loaded from the database
|
||||
$content: the content (string) of the article
|
||||
|
||||
'ArticleConfirmDelete': before writing the confirmation form for article
|
||||
deletion
|
||||
$article: the article (object) being deleted
|
||||
$output: the OutputPage object ($wgOut)
|
||||
&$reason: the reason (string) the article is being deleted
|
||||
|
||||
'ArticleContentOnDiff': before showing the article content below a diff.
|
||||
Use this to change the content in this area or how it is loaded.
|
||||
$diffEngine: the DifferenceEngine
|
||||
|
|
|
|||
|
|
@ -1,440 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
abstract class Action {
|
||||
|
||||
// Page on which we're performing the action
|
||||
// @var Article
|
||||
protected $page;
|
||||
|
||||
// RequestContext if specified; otherwise we'll use the Context from the Page
|
||||
// @var RequestContext
|
||||
protected $context;
|
||||
|
||||
// The fields used to create the HTMLForm
|
||||
// @var Array
|
||||
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 $action String
|
||||
* @return bool|null|string
|
||||
*/
|
||||
private final static function getClass( $action ){
|
||||
global $wgActions;
|
||||
$action = strtolower( $action );
|
||||
|
||||
if( !isset( $wgActions[$action] ) ){
|
||||
return null;
|
||||
}
|
||||
|
||||
if( $wgActions[$action] === false ){
|
||||
return false;
|
||||
}
|
||||
|
||||
elseif( $wgActions[$action] === true ){
|
||||
return ucfirst( $action ) . 'Action';
|
||||
}
|
||||
|
||||
else {
|
||||
return $wgActions[$action];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an appropriate Action subclass for the given action
|
||||
* @param $action String
|
||||
* @param $page Article
|
||||
* @return Action|false|null false if the action is disabled, null
|
||||
* if it is not recognised
|
||||
*/
|
||||
public final static function factory( $action, Article $page ){
|
||||
$class = self::getClass( $action );
|
||||
if( $class ){
|
||||
$obj = new $class( $page );
|
||||
return $obj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given action is recognised, even if it's disabled
|
||||
*
|
||||
* @param $name String: name of an action
|
||||
* @return Bool
|
||||
*/
|
||||
public final static function exists( $name ) {
|
||||
return self::getClass( $name ) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the RequestContext in use here
|
||||
* @return RequestContext
|
||||
*/
|
||||
protected final function getContext(){
|
||||
if( $this->context instanceof RequestContext ){
|
||||
return $this->context;
|
||||
}
|
||||
return $this->page->getContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WebRequest being used for this instance
|
||||
*
|
||||
* @return WebRequest
|
||||
*/
|
||||
protected final function getRequest() {
|
||||
return $this->getContext()->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OutputPage being used for this instance
|
||||
*
|
||||
* @return OutputPage
|
||||
*/
|
||||
protected final function getOutput() {
|
||||
return $this->getContext()->output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to get the skin being used for this instance
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
protected final function getUser() {
|
||||
return $this->getContext()->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to get the skin being used for this instance
|
||||
*
|
||||
* @return Skin
|
||||
*/
|
||||
protected final function getSkin() {
|
||||
return $this->getContext()->skin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to get the Title object from the page
|
||||
* @return Title
|
||||
*/
|
||||
protected final function getTitle(){
|
||||
return $this->page->getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected constructor: use Action::factory( $action, $page ) to actually build
|
||||
* these things in the real world
|
||||
* @param Article $page
|
||||
*/
|
||||
protected function __construct( Article $page ){
|
||||
$this->page = $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the action this object responds to
|
||||
* @return String lowercase
|
||||
*/
|
||||
public abstract function getName();
|
||||
|
||||
/**
|
||||
* Get the permission required to perform this action. Often, but not always,
|
||||
* the same as the action name
|
||||
*/
|
||||
public abstract function getRestriction();
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @param $user User: the user to check, or null to use the context user
|
||||
* @throws ErrorPageError
|
||||
*/
|
||||
protected function checkCanExecute( User $user ) {
|
||||
if( $this->requiresWrite() && wfReadOnly() ){
|
||||
throw new ReadOnlyError();
|
||||
}
|
||||
|
||||
if( $this->getRestriction() !== null && !$user->isAllowed( $this->getRestriction() ) ){
|
||||
throw new PermissionsError( $this->getRestriction() );
|
||||
}
|
||||
|
||||
if( $this->requiresUnblock() && $user->isBlocked() ){
|
||||
$block = $user->mBlock;
|
||||
throw new UserBlockedError( $block );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this action requires the wiki not to be locked
|
||||
* @return Bool
|
||||
*/
|
||||
public function requiresWrite(){
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this action can still be executed by a blocked user
|
||||
* @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.
|
||||
*/
|
||||
protected function setHeaders() {
|
||||
$out = $this->getOutput();
|
||||
$out->setRobotPolicy( "noindex,nofollow" );
|
||||
$out->setPageTitle( $this->getTitle()->getPrefixedText() );
|
||||
$this->getOutput()->setSubtitle( $this->getDescription() );
|
||||
$out->setArticleRelated( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name that goes in the \<h1\> page title
|
||||
*
|
||||
* Derived classes can override this, but usually it is easier to keep the
|
||||
* default behaviour. Messages can be added at run-time, see
|
||||
* MessageCache.php.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
protected function getDescription() {
|
||||
return wfMsg( strtolower( $this->getName() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* behaviour, 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 instanceof HTMLForm ){
|
||||
if( $form->show() ){
|
||||
$this->onSuccess();
|
||||
}
|
||||
} else {
|
||||
// You're using the wrong type of Action
|
||||
throw new MWException( "Action::getFormFields() must produce a form. Use GetAction if you don't want one." );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action in a silent fashion: do not display anything or release any errors.
|
||||
* @param $data Array values that would normally be in the POST request
|
||||
* @param $captureErrors Bool whether to catch exceptions and just return false
|
||||
* @return Bool whether execution was successful
|
||||
*/
|
||||
public function execute( array $data = null, $captureErrors = true ){
|
||||
try {
|
||||
// Set a new context so output doesn't leak.
|
||||
$this->context = clone $this->page->getContext();
|
||||
|
||||
// This will throw exceptions if there's a problem
|
||||
$this->checkCanExecute( $this->getUser() );
|
||||
|
||||
$form = $this->getForm();
|
||||
if( $form instanceof HTMLForm ){
|
||||
// Great, so there's a form. Ignore it and go straight to the submission callback
|
||||
$fields = array();
|
||||
foreach( $this->fields as $key => $params ){
|
||||
if( isset( $data[$key] ) ){
|
||||
$fields[$key] = $data[$key];
|
||||
} elseif( isset( $params['default'] ) ) {
|
||||
$fields[$key] = $params['default'];
|
||||
} else {
|
||||
$fields[$key] = null;
|
||||
}
|
||||
}
|
||||
$status = $this->onSubmit( $fields );
|
||||
if( $status === true ){
|
||||
// This might do permanent stuff
|
||||
$this->onSuccess();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// You're using the wrong type of Action
|
||||
throw new MWException( "Action::getFormFields() must produce a form. Use GetAction if you don't want one." );
|
||||
}
|
||||
}
|
||||
catch ( ErrorPageError $e ){
|
||||
if( $captureErrors ){
|
||||
return false;
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an HTMLForm descriptor array, or false if you don't want a form
|
||||
* @return Array
|
||||
*/
|
||||
protected abstract function getFormFields();
|
||||
|
||||
/**
|
||||
* Add pre- or post-text to the form
|
||||
* @return String
|
||||
*/
|
||||
protected function preText(){ return ''; }
|
||||
protected function postText(){ return ''; }
|
||||
|
||||
/**
|
||||
* Play with the HTMLForm if you need to more substantially
|
||||
* @param &$form HTMLForm
|
||||
*/
|
||||
protected function alterForm( HTMLForm &$form ){}
|
||||
|
||||
/**
|
||||
* Get the HTMLForm to control behaviour
|
||||
* @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 ) );
|
||||
|
||||
if( $this->fields === false ){
|
||||
return null;
|
||||
}
|
||||
|
||||
$form = new HTMLForm( $this->fields, $this->getContext() );
|
||||
$form->setSubmitCallback( array( $this, 'onSubmit' ) );
|
||||
$form->addHiddenField( 'action', $this->getName() );
|
||||
|
||||
$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 $data Array
|
||||
* @return Bool|Array true for success, false for didn't-try, array of errors on failure
|
||||
*/
|
||||
public abstract 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).
|
||||
*/
|
||||
public abstract function onSuccess();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
abstract class FormlessAction extends Action {
|
||||
|
||||
/**
|
||||
* Show something on GET request. This is displayed as the postText() of the HTMLForm
|
||||
* if there is one; you can always use alterForm() to add pre text if you need it. If
|
||||
* you call addPostText() from alterForm() as well as overriding this function, you
|
||||
* might get strange ordering.
|
||||
* @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
|
||||
*/
|
||||
public abstract function onView();
|
||||
|
||||
/**
|
||||
* We don't want an HTMLForm
|
||||
*/
|
||||
protected function getFormFields(){
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onSubmit( $data ){
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onSuccess(){
|
||||
return false;
|
||||
}
|
||||
|
||||
public function show(){
|
||||
$this->setHeaders();
|
||||
|
||||
// This will throw exceptions if there's a problem
|
||||
$this->checkCanExecute( $this->getUser() );
|
||||
$this->getOutput()->addHTML( $this->onView() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action silently, not giving any output. Since these actions don't have
|
||||
* forms, they probably won't have any data, but some (eg rollback) may do
|
||||
* @param $data Array values that would normally be in the GET request
|
||||
* @param $captureErrors Bool whether to catch exceptions and just return false
|
||||
* @return Bool whether execution was successful
|
||||
*/
|
||||
public function execute( array $data = null, $captureErrors = true){
|
||||
try {
|
||||
// Set a new context so output doesn't leak.
|
||||
$this->context = clone $this->page->getContext();
|
||||
if( is_array( $data ) ){
|
||||
$this->context->setRequest( new FauxRequest( $data, false ) );
|
||||
}
|
||||
|
||||
// This will throw exceptions if there's a problem
|
||||
$this->checkCanExecute( $this->getUser() );
|
||||
|
||||
$this->onView();
|
||||
return true;
|
||||
}
|
||||
catch ( ErrorPageError $e ){
|
||||
if( $captureErrors ){
|
||||
return false;
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2345,10 +2345,27 @@ class Article {
|
|||
|
||||
/**
|
||||
* User-interface handler for the "watch" action
|
||||
* @deprecated since 1.18
|
||||
*/
|
||||
public function watch() {
|
||||
Action::factory( 'watch', $this )->show();
|
||||
global $wgOut;
|
||||
|
||||
if ( $wgOut->getUser()->isAnon() ) {
|
||||
$wgOut->showErrorPage( 'watchnologin', 'watchnologintext' );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( wfReadOnly() ) {
|
||||
$wgOut->readOnlyPage();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->doWatch() ) {
|
||||
$wgOut->setPagetitle( wfMsg( 'addedwatch' ) );
|
||||
$wgOut->setRobotPolicy( 'noindex,nofollow' );
|
||||
$wgOut->addWikiMsg( 'addedwatchtext', $this->mTitle->getPrefixedText() );
|
||||
}
|
||||
|
||||
$wgOut->returnToMain( true, $this->mTitle->getPrefixedText() );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2357,27 +2374,64 @@ class Article {
|
|||
* This is safe to be called multiple times
|
||||
*
|
||||
* @return bool true on successful watch operation
|
||||
* @deprecated since 1.18
|
||||
*/
|
||||
public function doWatch() {
|
||||
return Action::factory( 'watch', $this )->execute();
|
||||
global $wgUser;
|
||||
|
||||
if ( $wgUser->isAnon() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( wfRunHooks( 'WatchArticle', array( &$wgUser, &$this ) ) ) {
|
||||
$wgUser->addWatch( $this->mTitle );
|
||||
return wfRunHooks( 'WatchArticleComplete', array( &$wgUser, &$this ) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* User interface handler for the "unwatch" action.
|
||||
* @deprecated since 1.18
|
||||
*/
|
||||
public function unwatch() {
|
||||
Action::factory( 'unwatch', $this )->show();
|
||||
global $wgOut;
|
||||
|
||||
if ( $wgOut->getUser()->isAnon() ) {
|
||||
$wgOut->showErrorPage( 'watchnologin', 'watchnologintext' );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( wfReadOnly() ) {
|
||||
$wgOut->readOnlyPage();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->doUnwatch() ) {
|
||||
$wgOut->setPagetitle( wfMsg( 'removedwatch' ) );
|
||||
$wgOut->setRobotPolicy( 'noindex,nofollow' );
|
||||
$wgOut->addWikiMsg( 'removedwatchtext', $this->mTitle->getPrefixedText() );
|
||||
}
|
||||
|
||||
$wgOut->returnToMain( true, $this->mTitle->getPrefixedText() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop watching a page
|
||||
* @return bool true on successful unwatch
|
||||
* @deprecated since 1.18
|
||||
*/
|
||||
public function doUnwatch() {
|
||||
return Action::factory( 'unwatch', $this )->execute();
|
||||
global $wgUser;
|
||||
|
||||
if ( $wgUser->isAnon() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( wfRunHooks( 'UnwatchArticle', array( &$wgUser, &$this ) ) ) {
|
||||
$wgUser->removeWatch( $this->mTitle );
|
||||
return wfRunHooks( 'UnwatchArticleComplete', array( &$wgUser, &$this ) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2609,28 +2663,229 @@ class Article {
|
|||
* @param &$hasHistory Boolean: whether the page has a history
|
||||
* @return mixed String containing deletion reason or empty string, or boolean false
|
||||
* if no revision occurred
|
||||
* @deprecated since 1.18
|
||||
*/
|
||||
public function generateReason( &$hasHistory ) {
|
||||
return DeleteAction::getAutoReason( $this );
|
||||
global $wgContLang;
|
||||
|
||||
$dbw = wfGetDB( DB_MASTER );
|
||||
// Get the last revision
|
||||
$rev = Revision::newFromTitle( $this->mTitle );
|
||||
|
||||
if ( is_null( $rev ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the article's contents
|
||||
$contents = $rev->getText();
|
||||
$blank = false;
|
||||
|
||||
// If the page is blank, use the text from the previous revision,
|
||||
// which can only be blank if there's a move/import/protect dummy revision involved
|
||||
if ( $contents == '' ) {
|
||||
$prev = $rev->getPrevious();
|
||||
|
||||
if ( $prev ) {
|
||||
$contents = $prev->getText();
|
||||
$blank = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Find out if there was only one contributor
|
||||
// Only scan the last 20 revisions
|
||||
$res = $dbw->select( 'revision', 'rev_user_text',
|
||||
array( 'rev_page' => $this->getID(), $dbw->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' ),
|
||||
__METHOD__,
|
||||
array( 'LIMIT' => 20 )
|
||||
);
|
||||
|
||||
if ( $res === false ) {
|
||||
// This page has no revisions, which is very weird
|
||||
return false;
|
||||
}
|
||||
|
||||
$hasHistory = ( $res->numRows() > 1 );
|
||||
$row = $dbw->fetchObject( $res );
|
||||
|
||||
if ( $row ) { // $row is false if the only contributor is hidden
|
||||
$onlyAuthor = $row->rev_user_text;
|
||||
// Try to find a second contributor
|
||||
foreach ( $res as $row ) {
|
||||
if ( $row->rev_user_text != $onlyAuthor ) { // Bug 22999
|
||||
$onlyAuthor = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$onlyAuthor = false;
|
||||
}
|
||||
|
||||
// Generate the summary with a '$1' placeholder
|
||||
if ( $blank ) {
|
||||
// The current revision is blank and the one before is also
|
||||
// blank. It's just not our lucky day
|
||||
$reason = wfMsgForContent( 'exbeforeblank', '$1' );
|
||||
} else {
|
||||
if ( $onlyAuthor ) {
|
||||
$reason = wfMsgForContent( 'excontentauthor', '$1', $onlyAuthor );
|
||||
} else {
|
||||
$reason = wfMsgForContent( 'excontent', '$1' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $reason == '-' ) {
|
||||
// Allow these UI messages to be blanked out cleanly
|
||||
return '';
|
||||
}
|
||||
|
||||
// Replace newlines with spaces to prevent uglyness
|
||||
$contents = preg_replace( "/[\n\r]/", ' ', $contents );
|
||||
// Calculate the maximum amount of chars to get
|
||||
// Max content length = max comment length - length of the comment (excl. $1)
|
||||
$maxLength = 255 - ( strlen( $reason ) - 2 );
|
||||
$contents = $wgContLang->truncate( $contents, $maxLength );
|
||||
// Remove possible unfinished links
|
||||
$contents = preg_replace( '/\[\[([^\]]*)\]?$/', '$1', $contents );
|
||||
// Now replace the '$1' placeholder
|
||||
$reason = str_replace( '$1', $contents, $reason );
|
||||
|
||||
return $reason;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* UI entry point for page deletion
|
||||
* @deprecated since 1.18
|
||||
*/
|
||||
public function delete() {
|
||||
return Action::factory( 'delete', $this )->show();
|
||||
global $wgOut, $wgRequest;
|
||||
|
||||
$confirm = $wgRequest->wasPosted() &&
|
||||
$wgOut->getUser()->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
|
||||
|
||||
$this->DeleteReasonList = $wgRequest->getText( 'wpDeleteReasonList', 'other' );
|
||||
$this->DeleteReason = $wgRequest->getText( 'wpReason' );
|
||||
|
||||
$reason = $this->DeleteReasonList;
|
||||
|
||||
if ( $reason != 'other' && $this->DeleteReason != '' ) {
|
||||
// Entry from drop down menu + additional comment
|
||||
$reason .= wfMsgForContent( 'colon-separator' ) . $this->DeleteReason;
|
||||
} elseif ( $reason == 'other' ) {
|
||||
$reason = $this->DeleteReason;
|
||||
}
|
||||
|
||||
# Flag to hide all contents of the archived revisions
|
||||
$suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgOut->getUser()->isAllowed( 'suppressrevision' );
|
||||
|
||||
# This code desperately needs to be totally rewritten
|
||||
|
||||
# Read-only check...
|
||||
if ( wfReadOnly() ) {
|
||||
$wgOut->readOnlyPage();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
# Check permissions
|
||||
$permission_errors = $this->mTitle->getUserPermissionsErrors( 'delete', $wgOut->getUser() );
|
||||
|
||||
if ( count( $permission_errors ) > 0 ) {
|
||||
$wgOut->showPermissionsErrorPage( $permission_errors );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$wgOut->setPagetitle( wfMsg( 'delete-confirm', $this->mTitle->getPrefixedText() ) );
|
||||
|
||||
# Better double-check that it hasn't been deleted yet!
|
||||
$dbw = wfGetDB( DB_MASTER );
|
||||
$conds = $this->mTitle->pageCond();
|
||||
$latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
|
||||
if ( $latest === false ) {
|
||||
$wgOut->showFatalError(
|
||||
Html::rawElement(
|
||||
'div',
|
||||
array( 'class' => 'error mw-error-cannotdelete' ),
|
||||
wfMsgExt( 'cannotdelete', array( 'parse' ), $this->mTitle->getPrefixedText() )
|
||||
)
|
||||
);
|
||||
$wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
|
||||
LogEventsList::showLogExtract(
|
||||
$wgOut,
|
||||
'delete',
|
||||
$this->mTitle->getPrefixedText()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
# Hack for big sites
|
||||
$bigHistory = $this->isBigDeletion();
|
||||
if ( $bigHistory && !$this->mTitle->userCan( 'bigdelete' ) ) {
|
||||
global $wgLang, $wgDeleteRevisionsLimit;
|
||||
|
||||
$wgOut->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
|
||||
array( 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $confirm ) {
|
||||
$this->doDelete( $reason, $suppress );
|
||||
|
||||
if ( $wgRequest->getCheck( 'wpWatch' ) && $wgOut->getUser()->isLoggedIn() ) {
|
||||
$this->doWatch();
|
||||
} elseif ( $this->mTitle->userIsWatching() ) {
|
||||
$this->doUnwatch();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate deletion reason
|
||||
$hasHistory = false;
|
||||
if ( !$reason ) {
|
||||
$reason = $this->generateReason( $hasHistory );
|
||||
}
|
||||
|
||||
// If the page has a history, insert a warning
|
||||
if ( $hasHistory && !$confirm ) {
|
||||
global $wgLang;
|
||||
|
||||
$skin = $wgOut->getSkin();
|
||||
$revisions = $this->estimateRevisionCount();
|
||||
//FIXME: lego
|
||||
$wgOut->addHTML( '<strong class="mw-delete-warning-revisions">' .
|
||||
wfMsgExt( 'historywarning', array( 'parseinline' ), $wgLang->formatNum( $revisions ) ) .
|
||||
wfMsgHtml( 'word-separator' ) . $skin->link( $this->mTitle,
|
||||
wfMsgHtml( 'history' ),
|
||||
array( 'rel' => 'archives' ),
|
||||
array( 'action' => 'history' ) ) .
|
||||
'</strong>'
|
||||
);
|
||||
|
||||
if ( $bigHistory ) {
|
||||
global $wgDeleteRevisionsLimit;
|
||||
$wgOut->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
|
||||
array( 'delete-warning-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
return $this->confirmDelete( $reason );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool whether or not the page surpasses $wgDeleteRevisionsLimit revisions
|
||||
* @deprecated since 1.18
|
||||
*/
|
||||
public function isBigDeletion() {
|
||||
global $wgDeleteRevisionsLimit;
|
||||
return $wgDeleteRevisionsLimit && $this->estimateRevisionCount() > $wgDeleteRevisionsLimit;
|
||||
|
||||
if ( $wgDeleteRevisionsLimit ) {
|
||||
$revCount = $this->estimateRevisionCount();
|
||||
|
||||
return $revCount > $wgDeleteRevisionsLimit;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2698,20 +2953,151 @@ class Article {
|
|||
return $authors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output deletion confirmation dialog
|
||||
* FIXME: Move to another file?
|
||||
* @param $reason String: prefilled reason
|
||||
*/
|
||||
public function confirmDelete( $reason ) {
|
||||
global $wgOut;
|
||||
|
||||
wfDebug( "Article::confirmDelete\n" );
|
||||
|
||||
$deleteBackLink = $wgOut->getSkin()->linkKnown( $this->mTitle );
|
||||
$wgOut->setSubtitle( wfMsgHtml( 'delete-backlink', $deleteBackLink ) );
|
||||
$wgOut->setRobotPolicy( 'noindex,nofollow' );
|
||||
$wgOut->addWikiMsg( 'confirmdeletetext' );
|
||||
|
||||
wfRunHooks( 'ArticleConfirmDelete', array( $this, $wgOut, &$reason ) );
|
||||
|
||||
if ( $wgOut->getUser()->isAllowed( 'suppressrevision' ) ) {
|
||||
$suppress = "<tr id=\"wpDeleteSuppressRow\">
|
||||
<td></td>
|
||||
<td class='mw-input'><strong>" .
|
||||
Xml::checkLabel( wfMsg( 'revdelete-suppress' ),
|
||||
'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '4' ) ) .
|
||||
"</strong></td>
|
||||
</tr>";
|
||||
} else {
|
||||
$suppress = '';
|
||||
}
|
||||
$checkWatch = $wgOut->getUser()->getBoolOption( 'watchdeletion' ) || $this->mTitle->userIsWatching();
|
||||
|
||||
$form = Xml::openElement( 'form', array( 'method' => 'post',
|
||||
'action' => $this->mTitle->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) .
|
||||
Xml::openElement( 'fieldset', array( 'id' => 'mw-delete-table' ) ) .
|
||||
Xml::tags( 'legend', null, wfMsgExt( 'delete-legend', array( 'parsemag', 'escapenoentities' ) ) ) .
|
||||
Xml::openElement( 'table', array( 'id' => 'mw-deleteconfirm-table' ) ) .
|
||||
"<tr id=\"wpDeleteReasonListRow\">
|
||||
<td class='mw-label'>" .
|
||||
Xml::label( wfMsg( 'deletecomment' ), 'wpDeleteReasonList' ) .
|
||||
"</td>
|
||||
<td class='mw-input'>" .
|
||||
Xml::listDropDown( 'wpDeleteReasonList',
|
||||
wfMsgForContent( 'deletereason-dropdown' ),
|
||||
wfMsgForContent( 'deletereasonotherlist' ), '', 'wpReasonDropDown', 1 ) .
|
||||
"</td>
|
||||
</tr>
|
||||
<tr id=\"wpDeleteReasonRow\">
|
||||
<td class='mw-label'>" .
|
||||
Xml::label( wfMsg( 'deleteotherreason' ), 'wpReason' ) .
|
||||
"</td>
|
||||
<td class='mw-input'>" .
|
||||
Html::input( 'wpReason', $reason, 'text', array(
|
||||
'size' => '60',
|
||||
'maxlength' => '255',
|
||||
'tabindex' => '2',
|
||||
'id' => 'wpReason',
|
||||
'autofocus'
|
||||
) ) .
|
||||
"</td>
|
||||
</tr>";
|
||||
|
||||
# Disallow watching if user is not logged in
|
||||
if ( $wgOut->getUser()->isLoggedIn() ) {
|
||||
$form .= "
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class='mw-input'>" .
|
||||
Xml::checkLabel( wfMsg( 'watchthis' ),
|
||||
'wpWatch', 'wpWatch', $checkWatch, array( 'tabindex' => '3' ) ) .
|
||||
"</td>
|
||||
</tr>";
|
||||
}
|
||||
|
||||
$form .= "
|
||||
$suppress
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class='mw-submit'>" .
|
||||
Xml::submitButton( wfMsg( 'deletepage' ),
|
||||
array( 'name' => 'wpConfirmB', 'id' => 'wpConfirmB', 'tabindex' => '5' ) ) .
|
||||
"</td>
|
||||
</tr>" .
|
||||
Xml::closeElement( 'table' ) .
|
||||
Xml::closeElement( 'fieldset' ) .
|
||||
Html::hidden( 'wpEditToken', $wgOut->getUser()->editToken() ) .
|
||||
Xml::closeElement( 'form' );
|
||||
|
||||
if ( $wgOut->getUser()->isAllowed( 'editinterface' ) ) {
|
||||
$skin = $wgOut->getSkin();
|
||||
$title = Title::makeTitle( NS_MEDIAWIKI, 'Deletereason-dropdown' );
|
||||
$link = $skin->link(
|
||||
$title,
|
||||
wfMsgHtml( 'delete-edit-reasonlist' ),
|
||||
array(),
|
||||
array( 'action' => 'edit' )
|
||||
);
|
||||
$form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
|
||||
}
|
||||
|
||||
$wgOut->addHTML( $form );
|
||||
$wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
|
||||
LogEventsList::showLogExtract( $wgOut, 'delete',
|
||||
$this->mTitle->getPrefixedText()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a deletion and output success or failure messages
|
||||
* @deprecated since 1.18
|
||||
*/
|
||||
public function doDelete( $reason, $suppress = false ) {
|
||||
return DeleteAction::doDeleteArticle(
|
||||
$this,
|
||||
$this->getContext(),
|
||||
array(
|
||||
'Suppress' => $suppress !== false,
|
||||
'Reason' => $reason,
|
||||
),
|
||||
true
|
||||
);
|
||||
global $wgOut;
|
||||
|
||||
$id = $this->mTitle->getArticleID( Title::GAID_FOR_UPDATE );
|
||||
|
||||
$error = '';
|
||||
if ( $this->doDeleteArticle( $reason, $suppress, $id, $error ) ) {
|
||||
$deleted = $this->mTitle->getPrefixedText();
|
||||
|
||||
$wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
|
||||
$wgOut->setRobotPolicy( 'noindex,nofollow' );
|
||||
|
||||
$loglink = '[[Special:Log/delete|' . wfMsgNoTrans( 'deletionlog' ) . ']]';
|
||||
|
||||
$wgOut->addWikiMsg( 'deletedtext', $deleted, $loglink );
|
||||
$wgOut->returnToMain( false );
|
||||
} else {
|
||||
if ( $error == '' ) {
|
||||
$wgOut->showFatalError(
|
||||
Html::rawElement(
|
||||
'div',
|
||||
array( 'class' => 'error mw-error-cannotdelete' ),
|
||||
wfMsgExt( 'cannotdelete', array( 'parse' ), $this->mTitle->getPrefixedText() )
|
||||
)
|
||||
);
|
||||
|
||||
$wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
|
||||
|
||||
LogEventsList::showLogExtract(
|
||||
$wgOut,
|
||||
'delete',
|
||||
$this->mTitle->getPrefixedText()
|
||||
);
|
||||
} else {
|
||||
$wgOut->showFatalError( $error );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2727,19 +3113,143 @@ class Article {
|
|||
* @param $id int article ID
|
||||
* @param $commit boolean defaults to true, triggers transaction end
|
||||
* @return boolean true if successful
|
||||
*
|
||||
* @deprecated since 1.18
|
||||
*/
|
||||
public function doDeleteArticle( $reason, $suppress = false, $id = 0, $commit = true, &$error = '' ) {
|
||||
return DeleteAction::doDeleteArticle(
|
||||
$this,
|
||||
$this->getContext(),
|
||||
global $wgDeferredUpdateList, $wgUseTrackbacks;
|
||||
global $wgUser;
|
||||
|
||||
wfDebug( __METHOD__ . "\n" );
|
||||
|
||||
if ( ! wfRunHooks( 'ArticleDelete', array( &$this, &$wgUser, &$reason, &$error ) ) ) {
|
||||
return false;
|
||||
}
|
||||
$dbw = wfGetDB( DB_MASTER );
|
||||
$t = $this->mTitle->getDBkey();
|
||||
$id = $id ? $id : $this->mTitle->getArticleID( Title::GAID_FOR_UPDATE );
|
||||
|
||||
if ( $t === '' || $id == 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$u = new SiteStatsUpdate( 0, 1, - (int)$this->isCountable( $this->getRawText() ), -1 );
|
||||
array_push( $wgDeferredUpdateList, $u );
|
||||
|
||||
// Bitfields to further suppress the content
|
||||
if ( $suppress ) {
|
||||
$bitfield = 0;
|
||||
// This should be 15...
|
||||
$bitfield |= Revision::DELETED_TEXT;
|
||||
$bitfield |= Revision::DELETED_COMMENT;
|
||||
$bitfield |= Revision::DELETED_USER;
|
||||
$bitfield |= Revision::DELETED_RESTRICTED;
|
||||
} else {
|
||||
$bitfield = 'rev_deleted';
|
||||
}
|
||||
|
||||
$dbw->begin();
|
||||
// For now, shunt the revision data into the archive table.
|
||||
// Text is *not* removed from the text table; bulk storage
|
||||
// is left intact to avoid breaking block-compression or
|
||||
// immutable storage schemes.
|
||||
//
|
||||
// For backwards compatibility, note that some older archive
|
||||
// table entries will have ar_text and ar_flags fields still.
|
||||
//
|
||||
// In the future, we may keep revisions and mark them with
|
||||
// the rev_deleted field, which is reserved for this purpose.
|
||||
$dbw->insertSelect( 'archive', array( 'page', 'revision' ),
|
||||
array(
|
||||
'Suppress' => $suppress !== false,
|
||||
'Reason' => $reason,
|
||||
),
|
||||
$commit
|
||||
'ar_namespace' => 'page_namespace',
|
||||
'ar_title' => 'page_title',
|
||||
'ar_comment' => 'rev_comment',
|
||||
'ar_user' => 'rev_user',
|
||||
'ar_user_text' => 'rev_user_text',
|
||||
'ar_timestamp' => 'rev_timestamp',
|
||||
'ar_minor_edit' => 'rev_minor_edit',
|
||||
'ar_rev_id' => 'rev_id',
|
||||
'ar_text_id' => 'rev_text_id',
|
||||
'ar_text' => '\'\'', // Be explicit to appease
|
||||
'ar_flags' => '\'\'', // MySQL's "strict mode"...
|
||||
'ar_len' => 'rev_len',
|
||||
'ar_page_id' => 'page_id',
|
||||
'ar_deleted' => $bitfield
|
||||
), array(
|
||||
'page_id' => $id,
|
||||
'page_id = rev_page'
|
||||
), __METHOD__
|
||||
);
|
||||
|
||||
# Delete restrictions for it
|
||||
$dbw->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ );
|
||||
|
||||
# Now that it's safely backed up, delete it
|
||||
$dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__ );
|
||||
$ok = ( $dbw->affectedRows() > 0 ); // getArticleId() uses slave, could be laggy
|
||||
|
||||
if ( !$ok ) {
|
||||
$dbw->rollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
# Fix category table counts
|
||||
$cats = array();
|
||||
$res = $dbw->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ );
|
||||
|
||||
foreach ( $res as $row ) {
|
||||
$cats [] = $row->cl_to;
|
||||
}
|
||||
|
||||
$this->updateCategoryCounts( array(), $cats );
|
||||
|
||||
# If using cascading deletes, we can skip some explicit deletes
|
||||
if ( !$dbw->cascadingDeletes() ) {
|
||||
$dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
|
||||
|
||||
if ( $wgUseTrackbacks )
|
||||
$dbw->delete( 'trackbacks', array( 'tb_page' => $id ), __METHOD__ );
|
||||
|
||||
# Delete outgoing links
|
||||
$dbw->delete( 'pagelinks', array( 'pl_from' => $id ) );
|
||||
$dbw->delete( 'imagelinks', array( 'il_from' => $id ) );
|
||||
$dbw->delete( 'categorylinks', array( 'cl_from' => $id ) );
|
||||
$dbw->delete( 'templatelinks', array( 'tl_from' => $id ) );
|
||||
$dbw->delete( 'externallinks', array( 'el_from' => $id ) );
|
||||
$dbw->delete( 'langlinks', array( 'll_from' => $id ) );
|
||||
$dbw->delete( 'redirect', array( 'rd_from' => $id ) );
|
||||
}
|
||||
|
||||
# If using cleanup triggers, we can skip some manual deletes
|
||||
if ( !$dbw->cleanupTriggers() ) {
|
||||
# Clean up recentchanges entries...
|
||||
$dbw->delete( 'recentchanges',
|
||||
array( 'rc_type != ' . RC_LOG,
|
||||
'rc_namespace' => $this->mTitle->getNamespace(),
|
||||
'rc_title' => $this->mTitle->getDBkey() ),
|
||||
__METHOD__ );
|
||||
$dbw->delete( 'recentchanges',
|
||||
array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ),
|
||||
__METHOD__ );
|
||||
}
|
||||
|
||||
# Clear caches
|
||||
Article::onArticleDelete( $this->mTitle );
|
||||
|
||||
# Clear the cached article id so the interface doesn't act like we exist
|
||||
$this->mTitle->resetArticleID( 0 );
|
||||
|
||||
# Log the deletion, if the page was suppressed, log it at Oversight instead
|
||||
$logtype = $suppress ? 'suppress' : 'delete';
|
||||
$log = new LogPage( $logtype );
|
||||
|
||||
# Make sure logging got through
|
||||
$log->addEntry( 'delete', $this->mTitle, $reason, array() );
|
||||
|
||||
if ( $commit ) {
|
||||
$dbw->commit();
|
||||
}
|
||||
|
||||
wfRunHooks( 'ArticleDeleteComplete', array( &$this, &$wgUser, $reason, $id ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ global $wgAutoloadLocalClasses;
|
|||
|
||||
$wgAutoloadLocalClasses = array(
|
||||
# Includes
|
||||
'Action' => 'includes/Action.php',
|
||||
'AjaxDispatcher' => 'includes/AjaxDispatcher.php',
|
||||
'AjaxResponse' => 'includes/AjaxResponse.php',
|
||||
'AlphabeticPager' => 'includes/Pager.php',
|
||||
|
|
@ -52,6 +51,7 @@ $wgAutoloadLocalClasses = array(
|
|||
'ConfEditorToken' => 'includes/ConfEditor.php',
|
||||
'ConstantDependency' => 'includes/CacheDependency.php',
|
||||
'CreativeCommonsRdf' => 'includes/Metadata.php',
|
||||
'Credits' => 'includes/Credits.php',
|
||||
'CSSJanus' => 'includes/libs/CSSJanus.php',
|
||||
'CSSMin' => 'includes/libs/CSSMin.php',
|
||||
'DependencyWrapper' => 'includes/CacheDependency.php',
|
||||
|
|
@ -274,12 +274,6 @@ $wgAutoloadLocalClasses = array(
|
|||
'ZhClient' => 'includes/ZhClient.php',
|
||||
'ZipDirectoryReader' => 'includes/ZipDirectoryReader.php',
|
||||
|
||||
# includes/actions
|
||||
'CreditsAction' => 'includes/actions/CreditsAction.php',
|
||||
'DeleteAction' => 'includes/actions/DeleteAction.php',
|
||||
'UnwatchAction' => 'includes/actions/WatchAction.php',
|
||||
'WatchAction' => 'includes/actions/WatchAction.php',
|
||||
|
||||
# includes/api
|
||||
'ApiBase' => 'includes/api/ApiBase.php',
|
||||
'ApiBlock' => 'includes/api/ApiBlock.php',
|
||||
|
|
|
|||
|
|
@ -19,35 +19,34 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
* @file
|
||||
* @ingroup Actions
|
||||
* @author <evan@wikitravel.org>
|
||||
*/
|
||||
|
||||
class CreditsAction extends FormlessAction {
|
||||
|
||||
public function getName(){
|
||||
return 'credits';
|
||||
}
|
||||
|
||||
public function getRestriction(){
|
||||
return null;
|
||||
}
|
||||
|
||||
class Credits {
|
||||
/**
|
||||
* This is largely cadged from PageHistory::history
|
||||
* @param $article Article object
|
||||
*/
|
||||
public function onView() {
|
||||
public static function showPage( Article $article ) {
|
||||
global $wgOut;
|
||||
|
||||
wfProfileIn( __METHOD__ );
|
||||
|
||||
if ( $this->page->getID() == 0 ) {
|
||||
$wgOut->setPageTitle( $article->mTitle->getPrefixedText() );
|
||||
$wgOut->setSubtitle( wfMsg( 'creditspage' ) );
|
||||
$wgOut->setArticleFlag( false );
|
||||
$wgOut->setArticleRelated( true );
|
||||
$wgOut->setRobotPolicy( 'noindex,nofollow' );
|
||||
|
||||
if ( $article->mTitle->getArticleID() == 0 ) {
|
||||
$s = wfMsg( 'nocredits' );
|
||||
} else {
|
||||
$s = $this->getCredits( -1 );
|
||||
$s = self::getCredits( $article, -1 );
|
||||
}
|
||||
|
||||
wfProfileOut( __METHOD__ );
|
||||
$wgOut->addHTML( $s );
|
||||
|
||||
return $s;
|
||||
wfProfileOut( __METHOD__ );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -57,14 +56,14 @@ class CreditsAction extends FormlessAction {
|
|||
* @param $showIfMax Bool: whether to contributors if there more than $cnt
|
||||
* @return String: html
|
||||
*/
|
||||
protected function getCredits( $cnt, $showIfMax = true ) {
|
||||
public static function getCredits( Article $article, $cnt, $showIfMax = true ) {
|
||||
wfProfileIn( __METHOD__ );
|
||||
$s = '';
|
||||
|
||||
if ( isset( $cnt ) && $cnt != 0 ) {
|
||||
$s = self::getAuthor( $this->page );
|
||||
$s = self::getAuthor( $article );
|
||||
if ( $cnt > 1 || $cnt < 0 ) {
|
||||
$s .= ' ' . $this->getContributors( $cnt - 1, $showIfMax );
|
||||
$s .= ' ' . self::getContributors( $article, $cnt - 1, $showIfMax );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -99,16 +98,16 @@ class CreditsAction extends FormlessAction {
|
|||
* @param $showIfMax Bool: whether to contributors if there more than $cnt
|
||||
* @return String: html
|
||||
*/
|
||||
protected function getContributors( $cnt, $showIfMax ) {
|
||||
protected static function getContributors( Article $article, $cnt, $showIfMax ) {
|
||||
global $wgLang, $wgHiddenPrefs;
|
||||
|
||||
$contributors = $this->page->getContributors();
|
||||
$contributors = $article->getContributors();
|
||||
|
||||
$others_link = false;
|
||||
|
||||
# Hmm... too many to fit!
|
||||
if ( $cnt > 0 && $contributors->count() > $cnt ) {
|
||||
$others_link = $this->othersLink();
|
||||
$others_link = self::othersLink( $article );
|
||||
if ( !$showIfMax )
|
||||
return wfMsgExt( 'othercontribs', 'parsemag', $others_link, $contributors->count() );
|
||||
}
|
||||
|
|
@ -193,11 +192,12 @@ class CreditsAction extends FormlessAction {
|
|||
$real = false;
|
||||
}
|
||||
|
||||
$page = $user->isAnon()
|
||||
? SpecialPage::getTitleFor( 'Contributions', $user->getName() )
|
||||
: $user->getUserPage();
|
||||
$skin = $wgUser->getSkin();
|
||||
$page = $user->isAnon() ?
|
||||
SpecialPage::getTitleFor( 'Contributions', $user->getName() ) :
|
||||
$user->getUserPage();
|
||||
|
||||
return Linker::link( $page, htmlspecialchars( $real ? $real : $user->getName() ) );
|
||||
return $skin->link( $page, htmlspecialchars( $real ? $real : $user->getName() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -224,10 +224,11 @@ class CreditsAction extends FormlessAction {
|
|||
* @param $article Article object
|
||||
* @return String: html
|
||||
*/
|
||||
protected function othersLink() {
|
||||
protected static function othersLink( Article $article ) {
|
||||
global $wgUser;
|
||||
return Linker::link(
|
||||
$this->getTitle(),
|
||||
$skin = $wgUser->getSkin();
|
||||
return $skin->link(
|
||||
$article->getTitle(),
|
||||
wfMsgHtml( 'others' ),
|
||||
array(),
|
||||
array( 'action' => 'credits' ),
|
||||
|
|
@ -27,10 +27,12 @@ if( !defined( 'MEDIAWIKI' ) ) {
|
|||
}
|
||||
|
||||
# Create a site configuration object. Not used for much in a default install
|
||||
if ( !defined( 'MW_COMPILED' ) ) {
|
||||
require_once( "$IP/includes/SiteConfiguration.php" );
|
||||
if ( !defined( 'MW_PHP4' ) ) {
|
||||
if ( !defined( 'MW_COMPILED' ) ) {
|
||||
require_once( "$IP/includes/SiteConfiguration.php" );
|
||||
}
|
||||
$wgConf = new SiteConfiguration;
|
||||
}
|
||||
$wgConf = new SiteConfiguration;
|
||||
/** @endcond */
|
||||
|
||||
/** MediaWiki version number */
|
||||
|
|
@ -5021,38 +5023,6 @@ $wgMaxRedirectLinksRetrieved = 500;
|
|||
|
||||
/** @} */ # end special pages }
|
||||
|
||||
/*************************************************************************//**
|
||||
* @name Actions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 'foo' => false The action is disabled; show an error message
|
||||
* Unsetting core actions will probably cause things to complain loudly.
|
||||
*/
|
||||
$wgActions = array(
|
||||
'credits' => true,
|
||||
'delete' => true,
|
||||
'unwatch' => true,
|
||||
'watch' => true,
|
||||
);
|
||||
|
||||
/**
|
||||
* Array of disabled article actions, e.g. view, edit, dublincore, delete, etc.
|
||||
* @deprecated since 1.18; just set $wgActions['action'] = false instead
|
||||
*/
|
||||
$wgDisabledActions = array();
|
||||
|
||||
/**
|
||||
* Allow the "info" action, very inefficient at the moment
|
||||
*/
|
||||
$wgAllowPageInfo = false;
|
||||
|
||||
/** @} */ # end actions }
|
||||
|
||||
/*************************************************************************//**
|
||||
* @name Robot (search engine crawler) policy
|
||||
* See also $wgNoFollowLinks.
|
||||
|
|
@ -5318,9 +5288,17 @@ $wgUpdateRowsPerQuery = 100;
|
|||
* @{
|
||||
*/
|
||||
|
||||
/** Allow the "info" action, very inefficient at the moment */
|
||||
$wgAllowPageInfo = false;
|
||||
|
||||
/** Name of the external diff engine to use */
|
||||
$wgExternalDiffEngine = false;
|
||||
|
||||
/**
|
||||
* Array of disabled article actions, e.g. view, edit, dublincore, delete, etc.
|
||||
*/
|
||||
$wgDisabledActions = array();
|
||||
|
||||
/**
|
||||
* Disable redirects to special pages and interwiki redirects, which use a 302
|
||||
* and have no "redirected from" link. Note this is only for articles with #Redirect
|
||||
|
|
|
|||
|
|
@ -1153,9 +1153,9 @@ class EditPage {
|
|||
$dbw = wfGetDB( DB_MASTER );
|
||||
$dbw->begin();
|
||||
if ( $this->watchthis ) {
|
||||
Action::factory( 'watch', $this->mArticle )->execute();
|
||||
$this->mArticle->doWatch();
|
||||
} else {
|
||||
Action::factory( 'watch', $this->mArticle )->execute();
|
||||
$this->mArticle->doUnwatch();
|
||||
}
|
||||
$dbw->commit();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,9 +125,9 @@ class FileDeleteForm {
|
|||
if( $article->doDeleteArticle( $reason, $suppress, $id, false ) ) {
|
||||
global $wgRequest;
|
||||
if( $wgRequest->getCheck( 'wpWatch' ) && $wgUser->isLoggedIn() ) {
|
||||
Action::factory( 'watch', $article )->execute();
|
||||
$article->doWatch();
|
||||
} elseif( $title->userIsWatching() ) {
|
||||
Action::factory( 'unwatch', $article )->execute();
|
||||
$article->doUnwatch();
|
||||
}
|
||||
$status = $file->delete( $reason, $suppress );
|
||||
if( $status->ok ) {
|
||||
|
|
|
|||
|
|
@ -317,9 +317,9 @@ class ProtectionForm {
|
|||
}
|
||||
|
||||
if( $wgRequest->getCheck( 'mwProtectWatch' ) && $wgUser->isLoggedIn() ) {
|
||||
Action::factory( 'watch', $this->mArticle )->execute();
|
||||
$this->mArticle->doWatch();
|
||||
} elseif( $this->mTitle->userIsWatching() ) {
|
||||
Action::factory( 'unwatch', $this->mArticle )->execute();
|
||||
$this->mArticle->doUnwatch();
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,14 +270,6 @@ if ( !$wgEnotifMinorEdits ) {
|
|||
$wgHiddenPrefs[] = 'enotifminoredits';
|
||||
}
|
||||
|
||||
# $wgDisabledActions is deprecated as of 1.18
|
||||
foreach( $wgDisabledActions as $action ){
|
||||
$wgActions[$action] = false;
|
||||
}
|
||||
if( !$wgAllowPageInfo ){
|
||||
$wgActions['info'] = false;
|
||||
}
|
||||
|
||||
if ( !$wgHtml5Version && $wgHtml5 && $wgAllowRdfaAttributes ) {
|
||||
# see http://www.w3.org/TR/rdfa-in-html/#document-conformance
|
||||
if ( $wgMimeType == 'application/xhtml+xml' ) {
|
||||
|
|
|
|||
|
|
@ -471,16 +471,9 @@ class MediaWiki {
|
|||
return;
|
||||
}
|
||||
|
||||
$act = $this->getAction();
|
||||
$action = $this->getAction();
|
||||
|
||||
$action = Action::factory( $this->getAction(), $article );
|
||||
if( $action instanceof Action ){
|
||||
$action->show();
|
||||
wfProfileOut( __METHOD__ );
|
||||
return;
|
||||
}
|
||||
|
||||
switch( $act ) {
|
||||
switch( $action ) {
|
||||
case 'view':
|
||||
$this->context->output->setSquidMaxage( $this->getVal( 'SquidMaxage' ) );
|
||||
$article->view();
|
||||
|
|
@ -491,6 +484,9 @@ class MediaWiki {
|
|||
$raw->view();
|
||||
wfProfileOut( __METHOD__ . '-raw' );
|
||||
break;
|
||||
case 'watch':
|
||||
case 'unwatch':
|
||||
case 'delete':
|
||||
case 'revert':
|
||||
case 'rollback':
|
||||
case 'protect':
|
||||
|
|
@ -500,7 +496,7 @@ class MediaWiki {
|
|||
case 'render':
|
||||
case 'deletetrackback':
|
||||
case 'purge':
|
||||
$article->$act();
|
||||
$article->$action();
|
||||
break;
|
||||
case 'print':
|
||||
$article->view();
|
||||
|
|
@ -521,6 +517,9 @@ class MediaWiki {
|
|||
$rdf->show();
|
||||
}
|
||||
break;
|
||||
case 'credits':
|
||||
Credits::showPage( $article );
|
||||
break;
|
||||
case 'submit':
|
||||
if ( session_id() == '' ) {
|
||||
// Send a cookie so anons get talk message notifications
|
||||
|
|
@ -533,7 +532,7 @@ class MediaWiki {
|
|||
$external = $this->context->request->getVal( 'externaledit' );
|
||||
$section = $this->context->request->getVal( 'section' );
|
||||
$oldid = $this->context->request->getVal( 'oldid' );
|
||||
if ( !$this->getVal( 'UseExternalEditor' ) || $act == 'submit' || $internal ||
|
||||
if ( !$this->getVal( 'UseExternalEditor' ) || $action == 'submit' || $internal ||
|
||||
$section || $oldid || ( !$this->context->user->getOption( 'externaleditor' ) && !$external ) ) {
|
||||
$editor = new EditPage( $article );
|
||||
$editor->submit();
|
||||
|
|
@ -562,7 +561,7 @@ class MediaWiki {
|
|||
$special->execute( '' );
|
||||
break;
|
||||
default:
|
||||
if ( wfRunHooks( 'UnknownAction', array( $act, $article ) ) ) {
|
||||
if ( wfRunHooks( 'UnknownAction', array( $action, $article ) ) ) {
|
||||
$this->context->output->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,476 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Performs the watch and 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
|
||||
*/
|
||||
|
||||
class DeleteAction extends Action {
|
||||
|
||||
public function getName(){
|
||||
return 'delete';
|
||||
}
|
||||
|
||||
public function getRestriction(){
|
||||
return 'delete';
|
||||
}
|
||||
|
||||
protected function getDescription(){
|
||||
return wfMsg( 'delete-confirm', $this->getTitle()->getPrefixedText() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the deletion can be executed. In addition to checking the user permissions,
|
||||
* check that the page is not too big and has not already been deleted.
|
||||
* @throws ErrorPageError
|
||||
* @see Action::checkCanExecute
|
||||
*/
|
||||
protected function checkCanExecute( User $user ){
|
||||
|
||||
// Check that the article hasn't already been deleted
|
||||
$dbw = wfGetDB( DB_MASTER );
|
||||
$conds = $this->getTitle()->pageCond();
|
||||
$latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
|
||||
if ( $latest === false ) {
|
||||
// Get the deletion log
|
||||
$log = '';
|
||||
LogEventsList::showLogExtract(
|
||||
$log,
|
||||
'delete',
|
||||
$this->getTitle()->getPrefixedText()
|
||||
);
|
||||
|
||||
$msg = new Message( 'cannotdelete' );
|
||||
$msg->params( $this->getTitle()->getPrefixedText() ); // This parameter is parsed
|
||||
$msg->rawParams( $log ); // This is not
|
||||
|
||||
throw new ErrorPageError( 'internalerror', $msg );
|
||||
}
|
||||
|
||||
// Limit deletions of big pages
|
||||
$bigHistory = $this->isBigDeletion();
|
||||
if ( $bigHistory && !$user->isAllowed( 'bigdelete' ) ) {
|
||||
global $wgDeleteRevisionsLimit;
|
||||
throw new ErrorPageError(
|
||||
'internalerror',
|
||||
'delete-toobig',
|
||||
$this->getContext()->lang->formatNum( $wgDeleteRevisionsLimit )
|
||||
);
|
||||
}
|
||||
|
||||
return parent::checkCanExecute( $user );
|
||||
}
|
||||
|
||||
protected function getFormFields(){
|
||||
// TODO: add more useful things here?
|
||||
$infoText = Html::rawElement(
|
||||
'strong',
|
||||
array(),
|
||||
Linker::link( $this->getTitle(), $this->getTitle()->getText() )
|
||||
);
|
||||
|
||||
$arr = array(
|
||||
'Page' => array(
|
||||
'type' => 'info',
|
||||
'raw' => true,
|
||||
'default' => $infoText,
|
||||
),
|
||||
'Reason' => array(
|
||||
'type' => 'selectandother',
|
||||
'label-message' => 'deletecomment',
|
||||
'options-message' => 'deletereason-dropdown',
|
||||
'size' => '60',
|
||||
'maxlength' => '255',
|
||||
'default' => self::getAutoReason( $this->page),
|
||||
),
|
||||
);
|
||||
|
||||
if( $this->getUser()->isLoggedIn() ){
|
||||
$arr['Watch'] = array(
|
||||
'type' => 'check',
|
||||
'label-message' => 'watchthis',
|
||||
'default' => $this->getUser()->getBoolOption( 'watchdeletion' ) || $this->getTitle()->userIsWatching()
|
||||
);
|
||||
}
|
||||
|
||||
if( $this->getUser()->isAllowed( 'suppressrevision' ) ){
|
||||
$arr['Suppress'] = array(
|
||||
'type' => 'check',
|
||||
'label-message' => 'revdelete-suppress',
|
||||
'default' => false,
|
||||
);
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Text to go at the top of the form, before the opening fieldset
|
||||
* @see Action::preText()
|
||||
* @return String
|
||||
*/
|
||||
protected function preText() {
|
||||
|
||||
// If the page has a history, insert a warning
|
||||
if ( $this->page->estimateRevisionCount() ) {
|
||||
global $wgLang;
|
||||
|
||||
$link = Linker::link(
|
||||
$this->getTitle(),
|
||||
wfMsgHtml( 'history' ),
|
||||
array( 'rel' => 'archives' ),
|
||||
array( 'action' => 'history' )
|
||||
);
|
||||
|
||||
return Html::rawElement(
|
||||
'strong',
|
||||
array( 'class' => 'mw-delete-warning-revisions' ),
|
||||
wfMessage(
|
||||
'historywarning',
|
||||
$wgLang->formatNum( $this->page->estimateRevisionCount() )
|
||||
)->rawParams( $link )->parse()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Text to go at the bottom of the form, below the closing fieldset
|
||||
* @see Action::postText()
|
||||
* @return string
|
||||
*/
|
||||
protected function postText(){
|
||||
$s = '';
|
||||
LogEventsList::showLogExtract(
|
||||
$s,
|
||||
'delete',
|
||||
$this->getTitle()->getPrefixedText()
|
||||
);
|
||||
return Html::element( 'h2', array(), LogPage::logName( 'delete' ) ) . $s;
|
||||
}
|
||||
|
||||
protected function alterForm( HTMLForm &$form ){
|
||||
$form->setWrapperLegend( wfMsgExt( 'delete-legend', array( 'parsemag', 'escapenoentities' ) ) );
|
||||
|
||||
if ( $this->getUser()->isAllowed( 'editinterface' ) ) {
|
||||
$link = Linker::link(
|
||||
Title::makeTitle( NS_MEDIAWIKI, 'Deletereason-dropdown' ),
|
||||
wfMsgHtml( 'delete-edit-reasonlist' ),
|
||||
array(),
|
||||
array( 'action' => 'edit' )
|
||||
);
|
||||
$form->addHeaderText( '<p class="mw-delete-editreasons">' . $link . '</p>' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called on form submission. Privilege checks and validation have already been
|
||||
* completed by this point; we just need to jump out to the heavy-lifting function,
|
||||
* which is implemented as a static method so it can be called from other places
|
||||
* TODO: make those other places call $action->execute() properly
|
||||
* @see Action::onSubmit()
|
||||
* @param $data Array
|
||||
* @return Array|Bool
|
||||
*/
|
||||
public function onSubmit( $data ){
|
||||
$status = self::doDeleteArticle( $this->page, $this->getContext(), $data, true );
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function onSuccess(){
|
||||
// Watch or unwatch, if requested
|
||||
if( $this->getRequest()->getCheck( 'wpWatch' ) && $this->getUser()->isLoggedIn() ) {
|
||||
Action::factory( 'watch', $this->page )->execute();
|
||||
} elseif ( $this->getTitle()->userIsWatching() ) {
|
||||
Action::factory( 'unwatch', $this->page )->execute();
|
||||
}
|
||||
|
||||
$this->getOutput()->setPagetitle( wfMsg( 'actioncomplete' ) );
|
||||
$this->getOutput()->addWikiMsg(
|
||||
'deletedtext',
|
||||
$this->getTitle()->getPrefixedText(),
|
||||
'[[Special:Log/delete|' . wfMsgNoTrans( 'deletionlog' ) . ']]'
|
||||
);
|
||||
$this->getOutput()->returnToMain( false );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool whether or not the page surpasses $wgDeleteRevisionsLimit revisions
|
||||
*/
|
||||
protected function isBigDeletion() {
|
||||
global $wgDeleteRevisionsLimit;
|
||||
return $wgDeleteRevisionsLimit && $this->page->estimateRevisionCount() > $wgDeleteRevisionsLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Back-end article deletion
|
||||
* Deletes the article with database consistency, writes logs, purges caches
|
||||
*
|
||||
* @param $commit boolean defaults to true, triggers transaction end
|
||||
* @return Bool|Array true if successful, error array on failure
|
||||
*/
|
||||
public static function doDeleteArticle( Article $page, RequestContext $context, array $data, $commit = true ) {
|
||||
global $wgDeferredUpdateList, $wgUseTrackbacks;
|
||||
|
||||
wfDebug( __METHOD__ . "\n" );
|
||||
|
||||
// The normal syntax from HTMLSelectAndOtherField is for the reason to be in the form
|
||||
// 'Reason' => array( <full reason>, <dropdown>, <custom> ), but it's reasonable for other
|
||||
// functions to just pass 'Reason' => <reason>
|
||||
$data['Reason'] = (array)$data['Reason'];
|
||||
|
||||
$error = null;
|
||||
if ( !wfRunHooks( 'ArticleDelete', array( &$page, &$context->user, &$data['Reason'][0], &$error ) ) ) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$title = $page->getTitle();
|
||||
$id = $page->getID( Title::GAID_FOR_UPDATE );
|
||||
|
||||
if ( $title->getDBkey() === '' || $id == 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$updates = new SiteStatsUpdate( 0, 1, - (int)$page->isCountable( $page->getRawText() ), -1 );
|
||||
array_push( $wgDeferredUpdateList, $updates );
|
||||
|
||||
// Bitfields to further suppress the content
|
||||
if ( isset( $data['Suppress'] ) && $data['Suppress'] ) {
|
||||
$bitfield = 0;
|
||||
// This should be 15...
|
||||
$bitfield |= Revision::DELETED_TEXT;
|
||||
$bitfield |= Revision::DELETED_COMMENT;
|
||||
$bitfield |= Revision::DELETED_USER;
|
||||
$bitfield |= Revision::DELETED_RESTRICTED;
|
||||
|
||||
$logtype = 'suppress';
|
||||
} else {
|
||||
// Otherwise, leave it unchanged
|
||||
$bitfield = 'rev_deleted';
|
||||
$logtype = 'delete';
|
||||
}
|
||||
|
||||
$dbw = wfGetDB( DB_MASTER );
|
||||
$dbw->begin();
|
||||
// For now, shunt the revision data into the archive table.
|
||||
// Text is *not* removed from the text table; bulk storage
|
||||
// is left intact to avoid breaking block-compression or
|
||||
// immutable storage schemes.
|
||||
//
|
||||
// For backwards compatibility, note that some older archive
|
||||
// table entries will have ar_text and ar_flags fields still.
|
||||
//
|
||||
// In the future, we may keep revisions and mark them with
|
||||
// the rev_deleted field, which is reserved for this purpose.
|
||||
$dbw->insertSelect(
|
||||
'archive',
|
||||
array( 'page', 'revision' ),
|
||||
array(
|
||||
'ar_namespace' => 'page_namespace',
|
||||
'ar_title' => 'page_title',
|
||||
'ar_comment' => 'rev_comment',
|
||||
'ar_user' => 'rev_user',
|
||||
'ar_user_text' => 'rev_user_text',
|
||||
'ar_timestamp' => 'rev_timestamp',
|
||||
'ar_minor_edit' => 'rev_minor_edit',
|
||||
'ar_rev_id' => 'rev_id',
|
||||
'ar_text_id' => 'rev_text_id',
|
||||
'ar_text' => "''", // Be explicit to appease
|
||||
'ar_flags' => "''", // MySQL's "strict mode"...
|
||||
'ar_len' => 'rev_len',
|
||||
'ar_page_id' => 'page_id',
|
||||
'ar_deleted' => $bitfield
|
||||
),
|
||||
array(
|
||||
'page_id' => $id,
|
||||
'page_id = rev_page'
|
||||
),
|
||||
__METHOD__
|
||||
);
|
||||
|
||||
// Delete restrictions for it
|
||||
$dbw->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ );
|
||||
|
||||
// Now that it's safely backed up, delete it
|
||||
$dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__ );
|
||||
|
||||
// getArticleId() uses slave, could be laggy
|
||||
if ( $dbw->affectedRows() == 0 ) {
|
||||
$dbw->rollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fix category table counts
|
||||
$res = $dbw->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ );
|
||||
$cats = array();
|
||||
foreach ( $res as $row ) {
|
||||
$cats[] = $row->cl_to;
|
||||
}
|
||||
$page->updateCategoryCounts( array(), $cats );
|
||||
|
||||
// If using cascading deletes, we can skip some explicit deletes
|
||||
if ( !$dbw->cascadingDeletes() ) {
|
||||
$dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
|
||||
|
||||
if ( $wgUseTrackbacks ){
|
||||
$dbw->delete( 'trackbacks', array( 'tb_page' => $id ), __METHOD__ );
|
||||
}
|
||||
|
||||
// Delete outgoing links
|
||||
$dbw->delete( 'pagelinks', array( 'pl_from' => $id ) );
|
||||
$dbw->delete( 'imagelinks', array( 'il_from' => $id ) );
|
||||
$dbw->delete( 'categorylinks', array( 'cl_from' => $id ) );
|
||||
$dbw->delete( 'templatelinks', array( 'tl_from' => $id ) );
|
||||
$dbw->delete( 'externallinks', array( 'el_from' => $id ) );
|
||||
$dbw->delete( 'langlinks', array( 'll_from' => $id ) );
|
||||
$dbw->delete( 'redirect', array( 'rd_from' => $id ) );
|
||||
}
|
||||
|
||||
// If using cleanup triggers, we can skip some manual deletes
|
||||
if ( !$dbw->cleanupTriggers() ) {
|
||||
// Clean up recentchanges entries...
|
||||
$dbw->delete( 'recentchanges',
|
||||
array(
|
||||
'rc_type != ' . RC_LOG,
|
||||
'rc_namespace' => $title->getNamespace(),
|
||||
'rc_title' => $title->getDBkey() ),
|
||||
__METHOD__
|
||||
);
|
||||
$dbw->delete(
|
||||
'recentchanges',
|
||||
array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ),
|
||||
__METHOD__
|
||||
);
|
||||
}
|
||||
|
||||
// Clear caches
|
||||
// TODO: should this be in here or left in Article?
|
||||
Article::onArticleDelete( $title );
|
||||
|
||||
// Clear the cached article id so the interface doesn't act like we exist
|
||||
$title->resetArticleID( 0 );
|
||||
|
||||
// Log the deletion, if the page was suppressed, log it at Oversight instead
|
||||
$log = new LogPage( $logtype );
|
||||
|
||||
// Make sure logging got through
|
||||
$log->addEntry( 'delete', $title, $data['Reason'][0], array() );
|
||||
|
||||
if ( $commit ) {
|
||||
$dbw->commit();
|
||||
}
|
||||
|
||||
wfRunHooks( 'ArticleDeleteComplete', array( &$page, &$context->user, $data['Reason'][0], $id ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-generates a deletion reason. Also sets $this->hasHistory if the page has old
|
||||
* revisions.
|
||||
*
|
||||
* @return mixed String containing default reason or empty string, or boolean false
|
||||
* if no revision was found
|
||||
*/
|
||||
public static function getAutoReason( Article $page ) {
|
||||
global $wgContLang;
|
||||
|
||||
$dbw = wfGetDB( DB_MASTER );
|
||||
// Get the last revision
|
||||
$rev = Revision::newFromTitle( $page->getTitle() );
|
||||
|
||||
if ( is_null( $rev ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the article's contents
|
||||
$contents = $rev->getText();
|
||||
$blank = false;
|
||||
|
||||
// If the page is blank, use the text from the previous revision,
|
||||
// which can only be blank if there's a move/import/protect dummy revision involved
|
||||
if ( $contents == '' ) {
|
||||
$prev = $rev->getPrevious();
|
||||
|
||||
if ( $prev ) {
|
||||
$contents = $prev->getText();
|
||||
$blank = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Find out if there was only one contributor
|
||||
// Only scan the last 20 revisions
|
||||
$res = $dbw->select( 'revision', 'rev_user_text',
|
||||
array(
|
||||
'rev_page' => $page->getID(),
|
||||
$dbw->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0'
|
||||
),
|
||||
__METHOD__,
|
||||
array( 'LIMIT' => 20 )
|
||||
);
|
||||
|
||||
if ( $res === false ) {
|
||||
// This page has no revisions, which is very weird
|
||||
return false;
|
||||
}
|
||||
|
||||
$row = $dbw->fetchObject( $res );
|
||||
|
||||
if ( $row ) { // $row is false if the only contributor is hidden
|
||||
$onlyAuthor = $row->rev_user_text;
|
||||
// Try to find a second contributor
|
||||
foreach ( $res as $row ) {
|
||||
if ( $row->rev_user_text != $onlyAuthor ) { // Bug 22999
|
||||
$onlyAuthor = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$onlyAuthor = false;
|
||||
}
|
||||
|
||||
// Generate the summary with a '$1' placeholder
|
||||
if ( $blank ) {
|
||||
// The current revision is blank and the one before is also
|
||||
// blank. It's just not our lucky day
|
||||
$reason = wfMessage( 'exbeforeblank', '$1' )->inContentLanguage()->text();
|
||||
} else {
|
||||
if ( $onlyAuthor ) {
|
||||
$reason = wfMessage( 'excontentauthor', '$1', $onlyAuthor )->inContentLanguage()->text();
|
||||
} else {
|
||||
$reason = wfMessage( 'excontent', '$1' )->inContentLanguage()->text();
|
||||
}
|
||||
}
|
||||
|
||||
if ( $reason == '-' ) {
|
||||
// Allow these UI messages to be blanked out cleanly
|
||||
return '';
|
||||
}
|
||||
|
||||
// Replace newlines with spaces to prevent uglyness
|
||||
$contents = preg_replace( "/[\n\r]/", ' ', $contents );
|
||||
// Calculate the maximum number of chars to get
|
||||
// Max content length = max comment length - length of the comment (excl. $1)
|
||||
$maxLength = 255 - ( strlen( $reason ) - 2 );
|
||||
$contents = $wgContLang->truncate( $contents, $maxLength );
|
||||
// Remove possible unfinished links
|
||||
$contents = preg_replace( '/\[\[([^\]]*)\]?$/', '$1', $contents );
|
||||
// Now replace the '$1' placeholder
|
||||
$reason = str_replace( '$1', $contents, $reason );
|
||||
|
||||
return $reason;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Performs the watch and 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
|
||||
*/
|
||||
|
||||
class WatchAction extends FormlessAction {
|
||||
|
||||
public function getName(){
|
||||
return 'watch';
|
||||
}
|
||||
|
||||
public function getRestriction(){
|
||||
return 'read';
|
||||
}
|
||||
|
||||
protected function getDescription(){
|
||||
return wfMsg( 'addedwatch' );
|
||||
}
|
||||
|
||||
protected function checkCanExecute( User $user ){
|
||||
if ( $user->isAnon() ) {
|
||||
throw new ErrorPageError( 'watchnologin', 'watchnologintext' );
|
||||
}
|
||||
return parent::checkCanExecute( $user );
|
||||
}
|
||||
|
||||
public function onView() {
|
||||
wfProfileIn( __METHOD__ );
|
||||
|
||||
$user = $this->getUser();
|
||||
if ( wfRunHooks( 'WatchArticle', array( &$user, &$this->page ) ) ) {
|
||||
$this->getUser()->addWatch( $this->getTitle() );
|
||||
wfRunHooks( 'WatchArticleComplete', array( &$user, &$this->page ) );
|
||||
}
|
||||
|
||||
wfProfileOut( __METHOD__ );
|
||||
|
||||
return wfMessage( 'addedwatchtext', $this->getTitle()->getPrefixedText() )->parse();
|
||||
}
|
||||
}
|
||||
|
||||
class UnwatchAction extends WatchAction {
|
||||
|
||||
public function getName(){
|
||||
return 'unwatch';
|
||||
}
|
||||
|
||||
protected function getDescription(){
|
||||
return wfMsg( 'removedwatch' );
|
||||
}
|
||||
|
||||
public function onView() {
|
||||
wfProfileIn( __METHOD__ );
|
||||
|
||||
$user = $this->getUser();
|
||||
if ( wfRunHooks( 'UnwatchArticle', array( &$user, &$this->page ) ) ) {
|
||||
$this->getUser()->removeWatch( $this->getTitle() );
|
||||
wfRunHooks( 'UnwatchArticleComplete', array( &$user, &$this->page ) );
|
||||
}
|
||||
|
||||
wfProfileOut( __METHOD__ );
|
||||
|
||||
return wfMessage( 'removedwatchtext', $this->getTitle()->getPrefixedText() )->parse();
|
||||
}
|
||||
}
|
||||
|
|
@ -645,9 +645,9 @@ abstract class ApiBase {
|
|||
|
||||
$articleObj = new Article( $titleObj );
|
||||
if ( $value ) {
|
||||
Action::factory( 'watch', $articleObj )->execute();
|
||||
$articleObj->doWatch();
|
||||
} else {
|
||||
Action::factory( 'unwatch', $articleObj )->execute();
|
||||
$articleObj->doUnwatch();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,6 +123,11 @@ class ApiDelete extends ApiBase {
|
|||
* @return Title::getUserPermissionsErrors()-like array
|
||||
*/
|
||||
public static function delete( &$article, $token, &$reason = null ) {
|
||||
global $wgUser;
|
||||
if ( $article->isBigDeletion() && !$wgUser->isAllowed( 'bigdelete' ) ) {
|
||||
global $wgDeleteRevisionsLimit;
|
||||
return array( array( 'delete-toobig', $wgDeleteRevisionsLimit ) );
|
||||
}
|
||||
$title = $article->getTitle();
|
||||
$errors = self::getPermissionsError( $title, $token );
|
||||
if ( count( $errors ) ) {
|
||||
|
|
@ -131,28 +136,21 @@ class ApiDelete extends ApiBase {
|
|||
|
||||
// Auto-generate a summary, if necessary
|
||||
if ( is_null( $reason ) ) {
|
||||
$reason = DeleteAction::getAutoReason( $article );
|
||||
// Need to pass a throwaway variable because generateReason expects
|
||||
// a reference
|
||||
$hasHistory = false;
|
||||
$reason = $article->generateReason( $hasHistory );
|
||||
if ( $reason === false ) {
|
||||
return array( array( 'cannotdelete' ) );
|
||||
}
|
||||
}
|
||||
|
||||
$action = Action::factory( 'delete', $article );
|
||||
$data = array(
|
||||
'Reason' => $reason,
|
||||
'Suppress' => false, // The thought of people doing this through the API is scary...
|
||||
);
|
||||
|
||||
try {
|
||||
$action->execute( $data, false );
|
||||
}
|
||||
catch ( ErrorPageError $e ){
|
||||
if( $e->msg == 'delete-toobig' ){
|
||||
global $wgDeleteRevisionsLimit;
|
||||
return array( array( 'delete-toobig', $wgDeleteRevisionsLimit ) );
|
||||
} else {
|
||||
array( array( 'cannotdelete', $article->mTitle->getPrefixedText() ) );
|
||||
}
|
||||
$error = '';
|
||||
// Luckily, Article.php provides a reusable delete function that does the hard work for us
|
||||
if ( $article->doDeleteArticle( $reason, false, 0, true, $error ) ) {
|
||||
return array();
|
||||
} else {
|
||||
return array( array( 'cannotdelete', $article->mTitle->getPrefixedText() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,11 +59,11 @@ class ApiWatch extends ApiBase {
|
|||
if ( $params['unwatch'] ) {
|
||||
$res['unwatched'] = '';
|
||||
$res['message'] = wfMsgExt( 'removedwatchtext', array( 'parse' ), $title->getPrefixedText() );
|
||||
$success = Action::factory( 'unwatch', $article )->execute();
|
||||
$success = $article->doUnwatch();
|
||||
} else {
|
||||
$res['watched'] = '';
|
||||
$res['message'] = wfMsgExt( 'addedwatchtext', array( 'parse' ), $title->getPrefixedText() );
|
||||
$success = Action::factory( 'watch', $article )->execute();
|
||||
$success = $article->doWatch();
|
||||
}
|
||||
if ( !$success ) {
|
||||
$this->dieUsageMsg( array( 'hookaborted' ) );
|
||||
|
|
|
|||
|
|
@ -359,11 +359,8 @@ class MovePageForm extends UnlistedSpecialPage {
|
|||
$article = new Article( $nt );
|
||||
|
||||
# Disallow deletions of big articles
|
||||
global $wgDeleteRevisionsLimit;
|
||||
if ( $wgDeleteRevisionsLimit
|
||||
&& $this->estimateRevisionCount() > $wgDeleteRevisionsLimit
|
||||
&& !$nt->userCan( 'bigdelete' ) )
|
||||
{
|
||||
$bigHistory = $article->isBigDeletion();
|
||||
if( $bigHistory && !$nt->userCan( 'bigdelete' ) ) {
|
||||
global $wgDeleteRevisionsLimit;
|
||||
$this->showForm( array('delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) );
|
||||
return;
|
||||
|
|
@ -376,10 +373,7 @@ class MovePageForm extends UnlistedSpecialPage {
|
|||
}
|
||||
|
||||
// This may output an error message and exit
|
||||
Action::factory( 'delete', $article )->execute(
|
||||
array( 'Reason' => wfMsgForContent( 'delete_and_move_reason' ) ),
|
||||
false // Do not capture exceptions
|
||||
);
|
||||
$article->doDelete( wfMsgForContent( 'delete_and_move_reason' ) );
|
||||
}
|
||||
|
||||
# don't allow moving to pages with # in
|
||||
|
|
|
|||
|
|
@ -998,9 +998,8 @@ Please report this to an [[Special:ListUsers/sysop|administrator]], making note
|
|||
'unexpected' => 'Unexpected value: "$1"="$2".',
|
||||
'formerror' => 'Error: could not submit form',
|
||||
'badarticleerror' => 'This action cannot be performed on this page.',
|
||||
'cannotdelete' => 'The page or file "$1" could not be deleted. It may have already been deleted by someone else. The deletion log is provided below for convenience.
|
||||
|
||||
$2',
|
||||
'cannotdelete' => 'The page or file "$1" could not be deleted.
|
||||
It may have already been deleted by someone else.',
|
||||
'badtitle' => 'Bad title',
|
||||
'badtitletext' => 'The requested page title was invalid, empty, or an incorrectly linked inter-language or inter-wiki title.
|
||||
It may contain one or more characters which cannot be used in titles.',
|
||||
|
|
@ -2784,7 +2783,7 @@ Feedback and further assistance:
|
|||
'delete-confirm' => 'Delete "$1"',
|
||||
'delete-backlink' => '← $1', # only translate this message to other languages if you have to change it
|
||||
'delete-legend' => 'Delete',
|
||||
'historywarning' => "'''Warning:''' The page you are about to delete has a $2 with approximately $1 {{PLURAL:$1|revision|revisions}}:",
|
||||
'historywarning' => "'''Warning:''' The page you are about to delete has a history with approximately $1 {{PLURAL:$1|revision|revisions}}:",
|
||||
'confirmdeletetext' => 'You are about to delete a page along with all of its history.
|
||||
Please confirm that you intend to do this, that you understand the consequences, and that you are doing this in accordance with [[{{MediaWiki:Policy-url}}|the policy]].',
|
||||
'actioncomplete' => 'Action complete',
|
||||
|
|
|
|||
Loading…
Reference in a new issue