From 8f416baead93a48e5799e44b8bd2e2c4859f4e04 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Fri, 14 Sep 2007 13:18:58 +0200 Subject: auf Version 1.11 aktualisiert; Login-Bug behoben --- includes/Article.php | 345 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 231 insertions(+), 114 deletions(-) (limited to 'includes/Article.php') diff --git a/includes/Article.php b/includes/Article.php index 0130ceba..7ba55c54 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -36,6 +36,18 @@ class Article { var $mUserText; //!< /**@}}*/ + /** + * Constants used by internal components to get rollback results + */ + const SUCCESS = 0; // Operation successful + const PERM_DENIED = 1; // Permission denied + const BLOCKED = 2; // User has been blocked + const READONLY = 3; // Wiki is in read-only mode + const BAD_TOKEN = 4; // Invalid token specified + const BAD_TITLE = 5; // $this is not a valid Article + const ALREADY_ROLLED = 6; // Someone else already rolled this back. $from and $summary will be set + const ONLY_AUTHOR = 7; // User is the only author of the page + /** * Constructor and clear the article * @param $title Reference to a Title object. @@ -257,13 +269,16 @@ class Article { 'page_random', 'page_touched', 'page_latest', - 'page_len' ) ; - wfRunHooks( 'ArticlePageDataBefore', array( &$this , &$fields ) ) ; - $row = $dbr->selectRow( 'page', + 'page_len', + ); + wfRunHooks( 'ArticlePageDataBefore', array( &$this, &$fields ) ); + $row = $dbr->selectRow( + 'page', $fields, $conditions, - 'Article::pageData' ); - wfRunHooks( 'ArticlePageDataAfter', array( &$this , &$row ) ) ; + __METHOD__ + ); + wfRunHooks( 'ArticlePageDataAfter', array( &$this, &$row ) ); return $row ; } @@ -500,6 +515,10 @@ class Article { * @return bool */ function isCurrent() { + # If no oldid, this is the current version. + if ($this->getOldID() == 0) + return true; + return $this->exists() && isset( $this->mRevision ) && $this->mRevision->isCurrent(); @@ -604,7 +623,7 @@ class Article { function view() { global $wgUser, $wgOut, $wgRequest, $wgContLang; global $wgEnableParserCache, $wgStylePath, $wgUseRCPatrol, $wgParser; - global $wgUseTrackbacks, $wgNamespaceRobotPolicies; + global $wgUseTrackbacks, $wgNamespaceRobotPolicies, $wgArticleRobotPolicies; $sk = $wgUser->getSkin(); wfProfileIn( __METHOD__ ); @@ -632,6 +651,8 @@ class Article { # Discourage indexing of printable versions, but encourage following if( $wgOut->isPrintable() ) { $policy = 'noindex,follow'; + } elseif ( isset( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] ) ) { + $policy = $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()]; } elseif( isset( $wgNamespaceRobotPolicies[$ns] ) ) { # Honour customised robot policies for this namespace $policy = $wgNamespaceRobotPolicies[$ns]; @@ -678,10 +699,12 @@ class Article { } # Should the parser cache be used? - $pcache = $wgEnableParserCache && - intval( $wgUser->getOption( 'stubthreshold' ) ) == 0 && - $this->exists() && - empty( $oldid ); + $pcache = $wgEnableParserCache + && intval( $wgUser->getOption( 'stubthreshold' ) ) == 0 + && $this->exists() + && empty( $oldid ) + && !$this->mTitle->isCssOrJsPage() + && !$this->mTitle->isCssJsSubpage(); wfDebug( 'Article::view using parser cache: ' . ($pcache ? 'yes' : 'no' ) . "\n" ); if ( $wgUser->getOption( 'stubthreshold' ) ) { wfIncrStats( 'pcache_miss_stub' ); @@ -718,9 +741,12 @@ class Article { } $outputDone = false; - wfRunHooks( 'ArticleViewHeader', array( &$this ) ); + wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$pcache ) ); if ( $pcache ) { if ( $wgOut->tryParserCache( $this, $wgUser ) ) { + // Ensure that UI elements requiring revision ID have + // the correct version information. + $wgOut->setRevisionId( $this->mLatest ); $outputDone = true; } } @@ -768,15 +794,24 @@ class Article { } if( !$outputDone ) { $wgOut->setRevisionId( $this->getRevIdFetched() ); - # wrap user css and user js in pre and don't parse - # XXX: use $this->mTitle->usCssJsSubpage() when php is fixed/ a workaround is found - if ( - $ns == NS_USER && - preg_match('/\\/[\\w]+\\.(?:css|js)$/', $this->mTitle->getDBkey()) - ) { - $wgOut->addWikiText( wfMsg('clearyourcache')); - $wgOut->addHTML( '
'.htmlspecialchars($this->mContent)."\n
" ); - } else if ( $rt = Title::newFromRedirect( $text ) ) { + + // Pages containing custom CSS or JavaScript get special treatment + if( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) { + $wgOut->addHtml( wfMsgExt( 'clearyourcache', 'parse' ) ); + + // Give hooks a chance to customise the output + if( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->mTitle, $wgOut ) ) ) { + // Wrap the whole lot in a
 and don't parse
+					$m = array();
+					preg_match( '!\.(css|js)$!u', $this->mTitle->getText(), $m );
+					$wgOut->addHtml( "
\n" );
+					$wgOut->addHtml( htmlspecialchars( $this->mContent ) );
+					$wgOut->addHtml( "\n
\n" ); + } + + } + + elseif ( $rt = Title::newFromRedirect( $text ) ) { # Display redirect $imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr'; $imageUrl = $wgStylePath.'/common/images/redirect' . $imageDir . '.png'; @@ -869,8 +904,8 @@ class Article { $rmvtxt = ""; if ($wgUser->isAllowed( 'trackback' )) { $delurl = $this->mTitle->getFullURL("action=deletetrackback&tbid=" - . $o->tb_id . "&token=" . $wgUser->editToken()); - $rmvtxt = wfMsg('trackbackremove', $delurl); + . $o->tb_id . "&token=" . urlencode( $wgUser->editToken() ) ); + $rmvtxt = wfMsg( 'trackbackremove', htmlspecialchars( $delurl ) ); } $tbtext .= wfMsg(strlen($o->tb_ex) ? 'trackbackexcerpt' : 'trackback', $o->tb_title, @@ -1146,7 +1181,7 @@ class Article { if( $section == 'new' ) { # Inserting a new section - $subject = $summary ? "== {$summary} ==\n\n" : ''; + $subject = $summary ? wfMsgForContent('newsectionheaderdefaultlevel',$summary) . "\n\n" : ''; $text = strlen( trim( $oldtext ) ) > 0 ? "{$oldtext}\n\n{$subject}{$text}" : "{$subject}{$text}"; @@ -1172,7 +1207,7 @@ class Article { # If this is a comment, add the summary as headline if ( $comment && $summary != "" ) { - $text = "== {$summary} ==\n\n".$text; + $text = wfMsgForContent('newsectionheaderdefaultlevel',$summary) . "\n\n".$text; } $this->doEdit( $text, $summary, $flags ); @@ -1219,7 +1254,10 @@ class Article { } } - $this->doRedirect( $this->isRedirect( $text ), $sectionanchor ); + $extraq = ''; // Give extensions a chance to modify URL query on update + wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this, &$sectionanchor, &$extraq ) ); + + $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraq ); } return $good; } @@ -1359,6 +1397,7 @@ class Article { $dbw->commit(); } } else { + $revision = null; // Keep the same revision ID, but do some updates on it $revisionId = $this->getRevIdFetched(); // Update page_touched, this is usually implicit in the page update @@ -1426,19 +1465,18 @@ class Article { # Clear caches Article::onArticleCreate( $this->mTitle ); - wfRunHooks( 'ArticleInsertComplete', array( &$this, &$wgUser, $text, - $summary, $flags & EDIT_MINOR, - null, null, &$flags ) ); + wfRunHooks( 'ArticleInsertComplete', array( &$this, &$wgUser, $text, $summary, + $flags & EDIT_MINOR, null, null, &$flags, $revision ) ); } if ( $good && !( $flags & EDIT_DEFER_UPDATES ) ) { wfDoUpdates(); } - wfRunHooks( 'ArticleSaveComplete', - array( &$this, &$wgUser, $text, - $summary, $flags & EDIT_MINOR, - null, null, &$flags ) ); + if ( $good ) { + wfRunHooks( 'ArticleSaveComplete', array( &$this, &$wgUser, $text, $summary, + $flags & EDIT_MINOR, null, null, &$flags, $revision ) ); + } wfProfileOut( __METHOD__ ); return $good; @@ -1458,12 +1496,14 @@ class Article { * @param boolean $noRedir Add redirect=no * @param string $sectionAnchor section to redirect to, including "#" */ - function doRedirect( $noRedir = false, $sectionAnchor = '' ) { + function doRedirect( $noRedir = false, $sectionAnchor = '', $extraq = '' ) { global $wgOut; if ( $noRedir ) { $query = 'redirect=no'; + if( $extraq ) + $query .= "&$query"; } else { - $query = ''; + $query = $extraq; } $wgOut->redirect( $this->mTitle->getFullURL( $query ) . $sectionAnchor ); } @@ -1690,7 +1730,13 @@ class Article { } # Prepare a null revision to be added to the history - $comment = $wgContLang->ucfirst( wfMsgForContent( $protect ? 'protectedarticle' : 'unprotectedarticle', $this->mTitle->getPrefixedText() ) ); + $modified = $current != '' && $protect; + if ( $protect ) { + $comment_type = $modified ? 'modifiedarticleprotection' : 'protectedarticle'; + } else { + $comment_type = 'unprotectedarticle'; + } + $comment = $wgContLang->ucfirst( wfMsgForContent( $comment_type, $this->mTitle->getPrefixedText() ) ); foreach( $limit as $action => $restrictions ) { # Check if the group level required to edit also can protect pages @@ -1744,7 +1790,7 @@ class Article { $log = new LogPage( 'protect' ); if( $protect ) { - $log->addEntry( 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) ); + $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason . " [$updated]$cascade_description$expiry_description" ) ); } else { $log->addEntry( 'unprotect', $this->mTitle, $reason ); } @@ -1994,11 +2040,10 @@ class Article { /** - * Fetch deletion log + * Show relevant lines from the deletion log */ - function showLogExtract( &$out ) { - # Show relevant lines from the deletion log: - $out->addHTML( "

" . htmlspecialchars( LogPage::logName( 'delete' ) ) . "

\n" ); + function showLogExtract( $out ) { + $out->addHtml( '

' . htmlspecialchars( LogPage::logName( 'delete' ) ) . '

' ); $logViewer = new LogViewer( new LogReader( new FauxRequest( @@ -2080,7 +2125,8 @@ class Article { 'ar_text_id' => 'rev_text_id', 'ar_text' => '\'\'', // Be explicit to appease 'ar_flags' => '\'\'', // MySQL's "strict mode"... - 'ar_len' => 'rev_len' + 'ar_len' => 'rev_len', + 'ar_page_id' => 'page_id', ), array( 'page_id' => $id, 'page_id = rev_page' @@ -2132,60 +2178,52 @@ class Article { } /** - * Revert a modification - */ - function rollback() { - global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol; - + * Roll back the most recent consecutive set of edits to a page + * from the same user; fails if there are no eligible edits to + * roll back to, e.g. user is the sole contributor + * + * @param string $fromP - Name of the user whose edits to rollback. + * @param string $summary - Custom summary. Set to default summary if empty. + * @param string $token - Rollback token. + * @param bool $bot - If true, mark all reverted edits as bot. + * + * @param array $resultDetails contains result-specific dict of additional values + * ALREADY_ROLLED : 'current' (rev) + * SUCCESS : 'summary' (str), 'current' (rev), 'target' (rev) + * + * @return self::SUCCESS on succes, self::* on failure + */ + public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails ) { + global $wgUser, $wgUseRCPatrol; + $resultDetails = null; + if( $wgUser->isAllowed( 'rollback' ) ) { if( $wgUser->isBlocked() ) { - $wgOut->blockedPage(); - return; + return self::BLOCKED; } } else { - $wgOut->permissionRequired( 'rollback' ); - return; + return self::PERM_DENIED; } - + if ( wfReadOnly() ) { - $wgOut->readOnlyPage( $this->getContent() ); - return; - } - if( !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ), - array( $this->mTitle->getPrefixedText(), - $wgRequest->getVal( 'from' ) ) ) ) { - $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) ); - $wgOut->addWikiText( wfMsg( 'sessionfailure' ) ); - return; + return self::READONLY; } - $dbw = wfGetDB( DB_MASTER ); - - # Enhanced rollback, marks edits rc_bot=1 - $bot = $wgRequest->getBool( 'bot' ); + if( !$wgUser->matchEditToken( $token, array( $this->mTitle->getPrefixedText(), $fromP ) ) ) + return self::BAD_TOKEN; - # Replace all this user's current edits with the next one down + $dbw = wfGetDB( DB_MASTER ); # Get the last editor $current = Revision::newFromTitle( $this->mTitle ); if( is_null( $current ) ) { # Something wrong... no page? - $wgOut->addHTML( wfMsg( 'notanarticle' ) ); - return; + return self::BAD_TITLE; } - $from = str_replace( '_', ' ', $wgRequest->getVal( 'from' ) ); + $from = str_replace( '_', ' ', $fromP ); if( $from != $current->getUserText() ) { - $wgOut->setPageTitle( wfMsg('rollbackfailed') ); - $wgOut->addWikiText( wfMsg( 'alreadyrolled', - htmlspecialchars( $this->mTitle->getPrefixedText()), - htmlspecialchars( $from ), - htmlspecialchars( $current->getUserText() ) ) ); - if( $current->getComment() != '') { - $wgOut->addHTML( - wfMsg( 'editcomment', - $wgUser->getSkin()->formatComment( $current->getComment() ) ) ); - } - return; + $resultDetails = array( 'current' => $current ); + return self::ALREADY_ROLLED; } # Get the last edit not by this guy @@ -2203,11 +2241,9 @@ class Article { ); if( $s === false ) { # Something wrong - $wgOut->setPageTitle(wfMsg('rollbackfailed')); - $wgOut->addHTML( wfMsg( 'cantrollback' ) ); - return; + return self::ONLY_AUTHOR; } - + $set = array(); if ( $bot ) { # Mark all reverted edits as bot @@ -2220,27 +2256,100 @@ class Article { if ( $set ) { $dbw->update( 'recentchanges', $set, - array( /* WHERE */ - 'rc_cur_id' => $current->getPage(), - 'rc_user_text' => $current->getUserText(), - "rc_timestamp > '{$s->rev_timestamp}'", - ), __METHOD__ - ); + array( /* WHERE */ + 'rc_cur_id' => $current->getPage(), + 'rc_user_text' => $current->getUserText(), + "rc_timestamp > '{$s->rev_timestamp}'", + ), __METHOD__ + ); } # Get the edit summary $target = Revision::newFromId( $s->rev_id ); - $newComment = wfMsgForContent( 'revertpage', $target->getUserText(), $from ); - $newComment = $wgRequest->getText( 'summary', $newComment ); + if( empty( $summary ) ) + $summary = wfMsgForContent( 'revertpage', $target->getUserText(), $from ); + + # Save + $flags = EDIT_UPDATE | EDIT_MINOR; + if( $bot ) + $flags |= EDIT_FORCE_BOT; + $this->doEdit( $target->getText(), $summary, $flags ); + + $resultDetails = array( + 'summary' => $summary, + 'current' => $current, + 'target' => $target, + ); + return self::SUCCESS; + } - # Save it! - $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) ); - $wgOut->setRobotpolicy( 'noindex,nofollow' ); - $wgOut->addHTML( '

' . htmlspecialchars( $newComment ) . "

\n
\n" ); + /** + * User interface for rollback operations + */ + function rollback() { + global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol; - $this->updateArticle( $target->getText(), $newComment, 1, $this->mTitle->userIsWatching(), $bot ); + $details = null; + $result = $this->doRollback( + $wgRequest->getVal( 'from' ), + $wgRequest->getText( 'summary' ), + $wgRequest->getVal( 'token' ), + $wgRequest->getBool( 'bot' ), + $details + ); + + switch( $result ) { + case self::BLOCKED: + $wgOut->blockedPage(); + break; + case self::PERM_DENIED: + $wgOut->permissionRequired( 'rollback' ); + break; + case self::READONLY: + $wgOut->readOnlyPage( $this->getContent() ); + break; + case self::BAD_TOKEN: + $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) ); + $wgOut->addWikiText( wfMsg( 'sessionfailure' ) ); + break; + case self::BAD_TITLE: + $wgOut->addHtml( wfMsg( 'notanarticle' ) ); + break; + case self::ALREADY_ROLLED: + $current = $details['current']; + $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) ); + $wgOut->addWikiText( + wfMsg( 'alreadyrolled', + htmlspecialchars( $this->mTitle->getPrefixedText() ), + htmlspecialchars( $wgRequest->getVal( 'from' ) ), + htmlspecialchars( $current->getUserText() ) + ) + ); + if( $current->getComment() != '' ) { + $wgOut->addHtml( wfMsg( 'editcomment', + $wgUser->getSkin()->formatComment( $current->getComment() ) ) ); + } + break; + case self::ONLY_AUTHOR: + $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) ); + $wgOut->addHtml( wfMsg( 'cantrollback' ) ); + break; + case self::SUCCESS: + $current = $details['current']; + $target = $details['target']; + $wgOut->setPageTitle( wfMsg( 'actioncomplete' ) ); + $wgOut->setRobotPolicy( 'noindex,nofollow' ); + $old = $wgUser->getSkin()->userLink( $current->getUser(), $current->getUserText() ) + . $wgUser->getSkin()->userToolLinks( $current->getUser(), $current->getUserText() ); + $new = $wgUser->getSkin()->userLink( $target->getUser(), $target->getUserText() ) + . $wgUser->getSkin()->userToolLinks( $target->getUser(), $target->getUserText() ); + $wgOut->addHtml( wfMsgExt( 'rollback-success', array( 'parse', 'replaceafter' ), $old, $new ) ); + $wgOut->returnToMain( false, $this->mTitle ); + break; + default: + throw new MWException( __METHOD__ . ": Unknown return value `{$result}`" ); + } - $wgOut->returnToMain( false ); } @@ -2268,7 +2377,7 @@ class Article { /** * Do standard deferred updates after page edit. * Update links tables, site stats, search index and message cache. - * Every 1000th edit, prune the recent changes table. + * Every 100th edit, prune the recent changes table. * * @private * @param $text New text of the article @@ -2296,12 +2405,11 @@ class Article { $u = new LinksUpdate( $this->mTitle, $poutput ); $u->doUpdate(); - if ( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) { - wfSeedRandom(); - if ( 0 == mt_rand( 0, 999 ) ) { - # Periodically flush old entries from the recentchanges table. + if( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) { + if ( 0 == mt_rand( 0, 99 ) ) { + // Flush old entries from the `recentchanges` table; we do this on + // random requests so as to avoid an increase in writes for no good reason global $wgRCMaxAge; - $dbw = wfGetDB( DB_MASTER ); $cutoff = $dbw->timestamp( time() - $wgRCMaxAge ); $recentchanges = $dbw->tableName( 'recentchanges' ); @@ -2410,7 +2518,12 @@ class Article { $userlinks = $sk->userLink( $revision->getUser(), $revision->getUserText() ) . $sk->userToolLinks( $revision->getUser(), $revision->getUserText() ); - $r = "\n\t\t\t\t
" . wfMsg( 'revision-info', $td, $userlinks ) . "
\n" . + $m = wfMsg( 'revision-info-current' ); + $infomsg = $current && !wfEmptyMsg( 'revision-info-current', $m ) && $m != '-' + ? 'revision-info-current' + : 'revision-info'; + + $r = "\n\t\t\t\t
" . wfMsg( $infomsg, $td, $userlinks ) . "
\n" . "\n\t\t\t\t
" . wfMsg( 'revision-nav', $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "
\n\t\t\t"; $wgOut->setSubtitle( $r ); } @@ -2701,16 +2814,22 @@ class Article { $page = $this->mTitle->getSubjectPage(); $wgOut->setPagetitle( $page->getPrefixedText() ); - $wgOut->setSubtitle( wfMsg( 'infosubtitle' )); - - # first, see if the page exists at all. - $exists = $page->getArticleId() != 0; - if( !$exists ) { - if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) { - $wgOut->addHTML(wfMsgWeirdKey ( $this->mTitle->getText() ) ); + $wgOut->setPageTitleActionText( wfMsg( 'info_short' ) ); + $wgOut->setSubtitle( wfMsg( 'infosubtitle' ) ); + + if( !$this->mTitle->exists() ) { + $wgOut->addHtml( '
' ); + if( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) { + // This doesn't quite make sense; the user is asking for + // information about the _page_, not the message... -- RC + $wgOut->addHtml( htmlspecialchars( wfMsgWeirdKey( $this->mTitle->getText() ) ) ); } else { - $wgOut->addHTML(wfMsg( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon' ) ); + $msg = $wgUser->isLoggedIn() + ? 'noarticletext' + : 'noarticletextanon'; + $wgOut->addHtml( wfMsgExt( $msg, 'parse' ) ); } + $wgOut->addHtml( '
' ); } else { $dbr = wfGetDB( DB_SLAVE ); $wl_clause = array( @@ -2959,5 +3078,3 @@ class Article { } } - -?> -- cgit v1.2.3-54-g00ecf