MediaWiki  REL1_20
Article.php
Go to the documentation of this file.
00001 <?php
00036 class Article extends Page {
00045         protected $mContext;
00046 
00051         protected $mPage;
00052 
00057         public $mParserOptions;
00058 
00063         var $mContent;                    // !<
00064 
00069         var $mContentLoaded = false;      // !<
00070 
00076         var $mOldId;                      // !<
00077 
00082         var $mRedirectedFrom = null;
00083 
00088         var $mRedirectUrl = false;        // !<
00089 
00094         var $mRevIdFetched = 0;           // !<
00095 
00100         var $mRevision = null;
00101 
00106         var $mParserOutput;
00107 
00115         public function __construct( Title $title, $oldId = null ) {
00116                 $this->mOldId = $oldId;
00117                 $this->mPage = $this->newPage( $title );
00118         }
00119 
00124         protected function newPage( Title $title ) {
00125                 return new WikiPage( $title );
00126         }
00127 
00133         public static function newFromID( $id ) {
00134                 $t = Title::newFromID( $id );
00135                 # @todo FIXME: Doesn't inherit right
00136                 return $t == null ? null : new self( $t );
00137                 # return $t == null ? null : new static( $t ); // PHP 5.3
00138         }
00139 
00147         public static function newFromTitle( $title, IContextSource $context ) {
00148                 if ( NS_MEDIA == $title->getNamespace() ) {
00149                         // FIXME: where should this go?
00150                         $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
00151                 }
00152 
00153                 $page = null;
00154                 wfRunHooks( 'ArticleFromTitle', array( &$title, &$page ) );
00155                 if ( !$page ) {
00156                         switch( $title->getNamespace() ) {
00157                                 case NS_FILE:
00158                                         $page = new ImagePage( $title );
00159                                         break;
00160                                 case NS_CATEGORY:
00161                                         $page = new CategoryPage( $title );
00162                                         break;
00163                                 default:
00164                                         $page = new Article( $title );
00165                         }
00166                 }
00167                 $page->setContext( $context );
00168 
00169                 return $page;
00170         }
00171 
00179         public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
00180                 $article = self::newFromTitle( $page->getTitle(), $context );
00181                 $article->mPage = $page; // override to keep process cached vars
00182                 return $article;
00183         }
00184 
00190         public function setRedirectedFrom( Title $from ) {
00191                 $this->mRedirectedFrom = $from;
00192         }
00193 
00199         public function getTitle() {
00200                 return $this->mPage->getTitle();
00201         }
00202 
00209         public function getPage() {
00210                 return $this->mPage;
00211         }
00212 
00216         public function clear() {
00217                 $this->mContentLoaded = false;
00218 
00219                 $this->mRedirectedFrom = null; # Title object if set
00220                 $this->mRevIdFetched = 0;
00221                 $this->mRedirectUrl = false;
00222 
00223                 $this->mPage->clear();
00224         }
00225 
00236         public function getContent() {
00237                 wfProfileIn( __METHOD__ );
00238 
00239                 if ( $this->mPage->getID() === 0 ) {
00240                         # If this is a MediaWiki:x message, then load the messages
00241                         # and return the message value for x.
00242                         if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
00243                                 $text = $this->getTitle()->getDefaultMessageText();
00244                                 if ( $text === false ) {
00245                                         $text = '';
00246                                 }
00247                         } else {
00248                                 $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
00249                                 $text = wfMessage( $message )->text();
00250                         }
00251                         wfProfileOut( __METHOD__ );
00252 
00253                         return $text;
00254                 } else {
00255                         $this->fetchContent();
00256                         wfProfileOut( __METHOD__ );
00257 
00258                         return $this->mContent;
00259                 }
00260         }
00261 
00266         public function getOldID() {
00267                 if ( is_null( $this->mOldId ) ) {
00268                         $this->mOldId = $this->getOldIDFromRequest();
00269                 }
00270 
00271                 return $this->mOldId;
00272         }
00273 
00279         public function getOldIDFromRequest() {
00280                 $this->mRedirectUrl = false;
00281 
00282                 $request = $this->getContext()->getRequest();
00283                 $oldid = $request->getIntOrNull( 'oldid' );
00284 
00285                 if ( $oldid === null ) {
00286                         return 0;
00287                 }
00288 
00289                 if ( $oldid !== 0 ) {
00290                         # Load the given revision and check whether the page is another one.
00291                         # In that case, update this instance to reflect the change.
00292                         if ( $oldid === $this->mPage->getLatest() ) {
00293                                 $this->mRevision = $this->mPage->getRevision();
00294                         } else {
00295                                 $this->mRevision = Revision::newFromId( $oldid );
00296                                 if ( $this->mRevision !== null ) {
00297                                         // Revision title doesn't match the page title given?
00298                                         if ( $this->mPage->getID() != $this->mRevision->getPage() ) {
00299                                                 $function = array( get_class( $this->mPage ), 'newFromID' );
00300                                                 $this->mPage = call_user_func( $function, $this->mRevision->getPage() );
00301                                         }
00302                                 }
00303                         }
00304                 }
00305 
00306                 if ( $request->getVal( 'direction' ) == 'next' ) {
00307                         $nextid = $this->getTitle()->getNextRevisionID( $oldid );
00308                         if ( $nextid ) {
00309                                 $oldid = $nextid;
00310                                 $this->mRevision = null;
00311                         } else {
00312                                 $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
00313                         }
00314                 } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
00315                         $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
00316                         if ( $previd ) {
00317                                 $oldid = $previd;
00318                                 $this->mRevision = null;
00319                         }
00320                 }
00321 
00322                 return $oldid;
00323         }
00324 
00330         function loadContent() {
00331                 wfDeprecated( __METHOD__, '1.19' );
00332                 $this->fetchContent();
00333         }
00334 
00341         function fetchContent() {
00342                 if ( $this->mContentLoaded ) {
00343                         return $this->mContent;
00344                 }
00345 
00346                 wfProfileIn( __METHOD__ );
00347 
00348                 $this->mContentLoaded = true;
00349 
00350                 $oldid = $this->getOldID();
00351 
00352                 # Pre-fill content with error message so that if something
00353                 # fails we'll have something telling us what we intended.
00354                 $this->mContent = wfMessage( 'missing-revision', $oldid )->plain();
00355 
00356                 if ( $oldid ) {
00357                         # $this->mRevision might already be fetched by getOldIDFromRequest()
00358                         if ( !$this->mRevision ) {
00359                                 $this->mRevision = Revision::newFromId( $oldid );
00360                                 if ( !$this->mRevision ) {
00361                                         wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
00362                                         wfProfileOut( __METHOD__ );
00363                                         return false;
00364                                 }
00365                         }
00366                 } else {
00367                         if ( !$this->mPage->getLatest() ) {
00368                                 wfDebug( __METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() . "\n" );
00369                                 wfProfileOut( __METHOD__ );
00370                                 return false;
00371                         }
00372 
00373                         $this->mRevision = $this->mPage->getRevision();
00374                         if ( !$this->mRevision ) {
00375                                 wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" );
00376                                 wfProfileOut( __METHOD__ );
00377                                 return false;
00378                         }
00379                 }
00380 
00381                 // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
00382                 // We should instead work with the Revision object when we need it...
00383                 $this->mContent = $this->mRevision->getText( Revision::FOR_THIS_USER ); // Loads if user is allowed
00384                 $this->mRevIdFetched = $this->mRevision->getId();
00385 
00386                 wfRunHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) );
00387 
00388                 wfProfileOut( __METHOD__ );
00389 
00390                 return $this->mContent;
00391         }
00392 
00397         public function forUpdate() {
00398                 wfDeprecated( __METHOD__, '1.18' );
00399         }
00400 
00406         public function isCurrent() {
00407                 # If no oldid, this is the current version.
00408                 if ( $this->getOldID() == 0 ) {
00409                         return true;
00410                 }
00411 
00412                 return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent();
00413         }
00414 
00422         public function getRevisionFetched() {
00423                 $this->fetchContent();
00424 
00425                 return $this->mRevision;
00426         }
00427 
00433         public function getRevIdFetched() {
00434                 if ( $this->mRevIdFetched ) {
00435                         return $this->mRevIdFetched;
00436                 } else {
00437                         return $this->mPage->getLatest();
00438                 }
00439         }
00440 
00445         public function view() {
00446                 global $wgParser, $wgUseFileCache, $wgUseETag, $wgDebugToolbar;
00447 
00448                 wfProfileIn( __METHOD__ );
00449 
00450                 # Get variables from query string
00451                 # As side effect this will load the revision and update the title
00452                 # in a revision ID is passed in the request, so this should remain
00453                 # the first call of this method even if $oldid is used way below.
00454                 $oldid = $this->getOldID();
00455 
00456                 $user = $this->getContext()->getUser();
00457                 # Another whitelist check in case getOldID() is altering the title
00458                 $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user );
00459                 if ( count( $permErrors ) ) {
00460                         wfDebug( __METHOD__ . ": denied on secondary read check\n" );
00461                         wfProfileOut( __METHOD__ );
00462                         throw new PermissionsError( 'read', $permErrors );
00463                 }
00464 
00465                 $outputPage = $this->getContext()->getOutput();
00466                 # getOldID() may as well want us to redirect somewhere else
00467                 if ( $this->mRedirectUrl ) {
00468                         $outputPage->redirect( $this->mRedirectUrl );
00469                         wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
00470                         wfProfileOut( __METHOD__ );
00471 
00472                         return;
00473                 }
00474 
00475                 # If we got diff in the query, we want to see a diff page instead of the article.
00476                 if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
00477                         wfDebug( __METHOD__ . ": showing diff page\n" );
00478                         $this->showDiffPage();
00479                         wfProfileOut( __METHOD__ );
00480 
00481                         return;
00482                 }
00483 
00484                 # Set page title (may be overridden by DISPLAYTITLE)
00485                 $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
00486 
00487                 $outputPage->setArticleFlag( true );
00488                 # Allow frames by default
00489                 $outputPage->allowClickjacking();
00490 
00491                 $parserCache = ParserCache::singleton();
00492 
00493                 $parserOptions = $this->getParserOptions();
00494                 # Render printable version, use printable version cache
00495                 if ( $outputPage->isPrintable() ) {
00496                         $parserOptions->setIsPrintable( true );
00497                         $parserOptions->setEditSection( false );
00498                 } elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit', $user ) ) {
00499                         $parserOptions->setEditSection( false );
00500                 }
00501 
00502                 # Try client and file cache
00503                 if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
00504                         if ( $wgUseETag ) {
00505                                 $outputPage->setETag( $parserCache->getETag( $this, $parserOptions ) );
00506                         }
00507 
00508                         # Is it client cached?
00509                         if ( $outputPage->checkLastModified( $this->mPage->getTouched() ) ) {
00510                                 wfDebug( __METHOD__ . ": done 304\n" );
00511                                 wfProfileOut( __METHOD__ );
00512 
00513                                 return;
00514                         # Try file cache
00515                         } elseif ( $wgUseFileCache && $this->tryFileCache() ) {
00516                                 wfDebug( __METHOD__ . ": done file cache\n" );
00517                                 # tell wgOut that output is taken care of
00518                                 $outputPage->disable();
00519                                 $this->mPage->doViewUpdates( $user );
00520                                 wfProfileOut( __METHOD__ );
00521 
00522                                 return;
00523                         }
00524                 }
00525 
00526                 # Should the parser cache be used?
00527                 $useParserCache = $this->mPage->isParserCacheUsed( $parserOptions, $oldid );
00528                 wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
00529                 if ( $user->getStubThreshold() ) {
00530                         wfIncrStats( 'pcache_miss_stub' );
00531                 }
00532 
00533                 $this->showRedirectedFromHeader();
00534                 $this->showNamespaceHeader();
00535 
00536                 # Iterate through the possible ways of constructing the output text.
00537                 # Keep going until $outputDone is set, or we run out of things to do.
00538                 $pass = 0;
00539                 $outputDone = false;
00540                 $this->mParserOutput = false;
00541 
00542                 while ( !$outputDone && ++$pass ) {
00543                         switch( $pass ) {
00544                                 case 1:
00545                                         wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$useParserCache ) );
00546                                         break;
00547                                 case 2:
00548                                         # Early abort if the page doesn't exist
00549                                         if ( !$this->mPage->exists() ) {
00550                                                 wfDebug( __METHOD__ . ": showing missing article\n" );
00551                                                 $this->showMissingArticle();
00552                                                 wfProfileOut( __METHOD__ );
00553                                                 return;
00554                                         }
00555 
00556                                         # Try the parser cache
00557                                         if ( $useParserCache ) {
00558                                                 $this->mParserOutput = $parserCache->get( $this, $parserOptions );
00559 
00560                                                 if ( $this->mParserOutput !== false ) {
00561                                                         if ( $oldid ) {
00562                                                                 wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
00563                                                                 $this->setOldSubtitle( $oldid );
00564                                                         } else {
00565                                                                 wfDebug( __METHOD__ . ": showing parser cache contents\n" );
00566                                                         }
00567                                                         $outputPage->addParserOutput( $this->mParserOutput );
00568                                                         # Ensure that UI elements requiring revision ID have
00569                                                         # the correct version information.
00570                                                         $outputPage->setRevisionId( $this->mPage->getLatest() );
00571                                                         # Preload timestamp to avoid a DB hit
00572                                                         $cachedTimestamp = $this->mParserOutput->getTimestamp();
00573                                                         if ( $cachedTimestamp !== null ) {
00574                                                                 $outputPage->setRevisionTimestamp( $cachedTimestamp );
00575                                                                 $this->mPage->setTimestamp( $cachedTimestamp );
00576                                                         }
00577                                                         $outputDone = true;
00578                                                 }
00579                                         }
00580                                         break;
00581                                 case 3:
00582                                         # This will set $this->mRevision if needed
00583                                         $this->fetchContent();
00584 
00585                                         # Are we looking at an old revision
00586                                         if ( $oldid && $this->mRevision ) {
00587                                                 $this->setOldSubtitle( $oldid );
00588 
00589                                                 if ( !$this->showDeletedRevisionHeader() ) {
00590                                                         wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
00591                                                         wfProfileOut( __METHOD__ );
00592                                                         return;
00593                                                 }
00594                                         }
00595 
00596                                         # Ensure that UI elements requiring revision ID have
00597                                         # the correct version information.
00598                                         $outputPage->setRevisionId( $this->getRevIdFetched() );
00599                                         # Preload timestamp to avoid a DB hit
00600                                         $outputPage->setRevisionTimestamp( $this->getTimestamp() );
00601 
00602                                         # Pages containing custom CSS or JavaScript get special treatment
00603                                         if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
00604                                                 wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
00605                                                 $this->showCssOrJsPage();
00606                                                 $outputDone = true;
00607                                         } elseif( !wfRunHooks( 'ArticleViewCustom', array( $this->mContent, $this->getTitle(), $outputPage ) ) ) {
00608                                                 # Allow extensions do their own custom view for certain pages
00609                                                 $outputDone = true;
00610                                         } else {
00611                                                 $text = $this->getContent();
00612                                                 $rt = Title::newFromRedirectArray( $text );
00613                                                 if ( $rt ) {
00614                                                         wfDebug( __METHOD__ . ": showing redirect=no page\n" );
00615                                                         # Viewing a redirect page (e.g. with parameter redirect=no)
00616                                                         $outputPage->addHTML( $this->viewRedirect( $rt ) );
00617                                                         # Parse just to get categories, displaytitle, etc.
00618                                                         $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(), $parserOptions );
00619                                                         $outputPage->addParserOutputNoText( $this->mParserOutput );
00620                                                         $outputDone = true;
00621                                                 }
00622                                         }
00623                                         break;
00624                                 case 4:
00625                                         # Run the parse, protected by a pool counter
00626                                         wfDebug( __METHOD__ . ": doing uncached parse\n" );
00627 
00628                                         $poolArticleView = new PoolWorkArticleView( $this, $parserOptions,
00629                                                 $this->getRevIdFetched(), $useParserCache, $this->getContent() );
00630 
00631                                         if ( !$poolArticleView->execute() ) {
00632                                                 $error = $poolArticleView->getError();
00633                                                 if ( $error ) {
00634                                                         $outputPage->clearHTML(); // for release() errors
00635                                                         $outputPage->enableClientCache( false );
00636                                                         $outputPage->setRobotPolicy( 'noindex,nofollow' );
00637 
00638                                                         $errortext = $error->getWikiText( false, 'view-pool-error' );
00639                                                         $outputPage->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
00640                                                 }
00641                                                 # Connection or timeout error
00642                                                 wfProfileOut( __METHOD__ );
00643                                                 return;
00644                                         }
00645 
00646                                         $this->mParserOutput = $poolArticleView->getParserOutput();
00647                                         $outputPage->addParserOutput( $this->mParserOutput );
00648 
00649                                         # Don't cache a dirty ParserOutput object
00650                                         if ( $poolArticleView->getIsDirty() ) {
00651                                                 $outputPage->setSquidMaxage( 0 );
00652                                                 $outputPage->addHTML( "<!-- parser cache is expired, sending anyway due to pool overload-->\n" );
00653                                         }
00654 
00655                                         $outputDone = true;
00656                                         break;
00657                                 # Should be unreachable, but just in case...
00658                                 default:
00659                                         break 2;
00660                         }
00661                 }
00662 
00663                 # Get the ParserOutput actually *displayed* here.
00664                 # Note that $this->mParserOutput is the *current* version output.
00665                 $pOutput = ( $outputDone instanceof ParserOutput )
00666                         ? $outputDone // object fetched by hook
00667                         : $this->mParserOutput;
00668 
00669                 # Adjust title for main page & pages with displaytitle
00670                 if ( $pOutput ) {
00671                         $this->adjustDisplayTitle( $pOutput );
00672                 }
00673 
00674                 # For the main page, overwrite the <title> element with the con-
00675                 # tents of 'pagetitle-view-mainpage' instead of the default (if
00676                 # that's not empty).
00677                 # This message always exists because it is in the i18n files
00678                 if ( $this->getTitle()->isMainPage() ) {
00679                         $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
00680                         if ( !$msg->isDisabled() ) {
00681                                 $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
00682                         }
00683                 }
00684 
00685                 # Check for any __NOINDEX__ tags on the page using $pOutput
00686                 $policy = $this->getRobotPolicy( 'view', $pOutput );
00687                 $outputPage->setIndexPolicy( $policy['index'] );
00688                 $outputPage->setFollowPolicy( $policy['follow'] );
00689 
00690                 $this->showViewFooter();
00691                 $this->mPage->doViewUpdates( $user );
00692 
00693                 wfProfileOut( __METHOD__ );
00694         }
00695 
00700         public function adjustDisplayTitle( ParserOutput $pOutput ) {
00701                 # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
00702                 $titleText = $pOutput->getTitleText();
00703                 if ( strval( $titleText ) !== '' ) {
00704                         $this->getContext()->getOutput()->setPageTitle( $titleText );
00705                 }
00706         }
00707 
00712         public function showDiffPage() {
00713                 $request = $this->getContext()->getRequest();
00714                 $user = $this->getContext()->getUser();
00715                 $diff = $request->getVal( 'diff' );
00716                 $rcid = $request->getVal( 'rcid' );
00717                 $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
00718                 $purge = $request->getVal( 'action' ) == 'purge';
00719                 $unhide = $request->getInt( 'unhide' ) == 1;
00720                 $oldid = $this->getOldID();
00721 
00722                 $de = new DifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide );
00723                 // DifferenceEngine directly fetched the revision:
00724                 $this->mRevIdFetched = $de->mNewid;
00725                 $de->showDiffPage( $diffOnly );
00726 
00727                 if ( $diff == 0 || $diff == $this->mPage->getLatest() ) {
00728                         # Run view updates for current revision only
00729                         $this->mPage->doViewUpdates( $user );
00730                 }
00731         }
00732 
00740         protected function showCssOrJsPage() {
00741                 $dir = $this->getContext()->getLanguage()->getDir();
00742                 $lang = $this->getContext()->getLanguage()->getCode();
00743 
00744                 $outputPage = $this->getContext()->getOutput();
00745                 $outputPage->wrapWikiMsg( "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
00746                         'clearyourcache' );
00747 
00748                 // Give hooks a chance to customise the output
00749                 if ( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->getTitle(), $outputPage ) ) ) {
00750                         // Wrap the whole lot in a <pre> and don't parse
00751                         $m = array();
00752                         preg_match( '!\.(css|js)$!u', $this->getTitle()->getText(), $m );
00753                         $outputPage->addHTML( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" );
00754                         $outputPage->addHTML( htmlspecialchars( $this->mContent ) );
00755                         $outputPage->addHTML( "\n</pre>\n" );
00756                 }
00757         }
00758 
00766         public function getRobotPolicy( $action, $pOutput ) {
00767                 global $wgArticleRobotPolicies, $wgNamespaceRobotPolicies, $wgDefaultRobotPolicy;
00768 
00769                 $ns = $this->getTitle()->getNamespace();
00770 
00771                 if ( $ns == NS_USER || $ns == NS_USER_TALK ) {
00772                         # Don't index user and user talk pages for blocked users (bug 11443)
00773                         if ( !$this->getTitle()->isSubpage() ) {
00774                                 if ( Block::newFromTarget( null, $this->getTitle()->getText() ) instanceof Block ) {
00775                                         return array(
00776                                                 'index'  => 'noindex',
00777                                                 'follow' => 'nofollow'
00778                                         );
00779                                 }
00780                         }
00781                 }
00782 
00783                 if ( $this->mPage->getID() === 0 || $this->getOldID() ) {
00784                         # Non-articles (special pages etc), and old revisions
00785                         return array(
00786                                 'index'  => 'noindex',
00787                                 'follow' => 'nofollow'
00788                         );
00789                 } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
00790                         # Discourage indexing of printable versions, but encourage following
00791                         return array(
00792                                 'index'  => 'noindex',
00793                                 'follow' => 'follow'
00794                         );
00795                 } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
00796                         # For ?curid=x urls, disallow indexing
00797                         return array(
00798                                 'index'  => 'noindex',
00799                                 'follow' => 'follow'
00800                         );
00801                 }
00802 
00803                 # Otherwise, construct the policy based on the various config variables.
00804                 $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
00805 
00806                 if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
00807                         # Honour customised robot policies for this namespace
00808                         $policy = array_merge(
00809                                 $policy,
00810                                 self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
00811                         );
00812                 }
00813                 if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
00814                         # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
00815                         # a final sanity check that we have really got the parser output.
00816                         $policy = array_merge(
00817                                 $policy,
00818                                 array( 'index' => $pOutput->getIndexPolicy() )
00819                         );
00820                 }
00821 
00822                 if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
00823                         # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
00824                         $policy = array_merge(
00825                                 $policy,
00826                                 self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
00827                         );
00828                 }
00829 
00830                 return $policy;
00831         }
00832 
00840         public static function formatRobotPolicy( $policy ) {
00841                 if ( is_array( $policy ) ) {
00842                         return $policy;
00843                 } elseif ( !$policy ) {
00844                         return array();
00845                 }
00846 
00847                 $policy = explode( ',', $policy );
00848                 $policy = array_map( 'trim', $policy );
00849 
00850                 $arr = array();
00851                 foreach ( $policy as $var ) {
00852                         if ( in_array( $var, array( 'index', 'noindex' ) ) ) {
00853                                 $arr['index'] = $var;
00854                         } elseif ( in_array( $var, array( 'follow', 'nofollow' ) ) ) {
00855                                 $arr['follow'] = $var;
00856                         }
00857                 }
00858 
00859                 return $arr;
00860         }
00861 
00869         public function showRedirectedFromHeader() {
00870                 global $wgRedirectSources;
00871                 $outputPage = $this->getContext()->getOutput();
00872 
00873                 $rdfrom = $this->getContext()->getRequest()->getVal( 'rdfrom' );
00874 
00875                 if ( isset( $this->mRedirectedFrom ) ) {
00876                         // This is an internally redirected page view.
00877                         // We'll need a backlink to the source page for navigation.
00878                         if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) {
00879                                 $redir = Linker::linkKnown(
00880                                         $this->mRedirectedFrom,
00881                                         null,
00882                                         array(),
00883                                         array( 'redirect' => 'no' )
00884                                 );
00885 
00886                                 $outputPage->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) );
00887 
00888                                 // Set the fragment if one was specified in the redirect
00889                                 if ( strval( $this->getTitle()->getFragment() ) != '' ) {
00890                                         $fragment = Xml::escapeJsString( $this->getTitle()->getFragmentForURL() );
00891                                         $outputPage->addInlineScript( "redirectToFragment(\"$fragment\");" );
00892                                 }
00893 
00894                                 // Add a <link rel="canonical"> tag
00895                                 $outputPage->addLink( array( 'rel' => 'canonical',
00896                                         'href' => $this->getTitle()->getLocalURL() )
00897                                 );
00898 
00899                                 // Tell the output object that the user arrived at this article through a redirect
00900                                 $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
00901 
00902                                 return true;
00903                         }
00904                 } elseif ( $rdfrom ) {
00905                         // This is an externally redirected view, from some other wiki.
00906                         // If it was reported from a trusted site, supply a backlink.
00907                         if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
00908                                 $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
00909                                 $outputPage->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) );
00910 
00911                                 return true;
00912                         }
00913                 }
00914 
00915                 return false;
00916         }
00917 
00922         public function showNamespaceHeader() {
00923                 if ( $this->getTitle()->isTalkPage() ) {
00924                         if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
00925                                 $this->getContext()->getOutput()->wrapWikiMsg( "<div class=\"mw-talkpageheader\">\n$1\n</div>", array( 'talkpageheader' ) );
00926                         }
00927                 }
00928         }
00929 
00933         public function showViewFooter() {
00934                 # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
00935                 if ( $this->getTitle()->getNamespace() == NS_USER_TALK && IP::isValid( $this->getTitle()->getText() ) ) {
00936                         $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
00937                 }
00938 
00939                 # If we have been passed an &rcid= parameter, we want to give the user a
00940                 # chance to mark this new article as patrolled.
00941                 $this->showPatrolFooter();
00942 
00943                 wfRunHooks( 'ArticleViewFooter', array( $this ) );
00944 
00945         }
00946 
00952         public function showPatrolFooter() {
00953                 $request = $this->getContext()->getRequest();
00954                 $outputPage = $this->getContext()->getOutput();
00955                 $user = $this->getContext()->getUser();
00956                 $rcid = $request->getVal( 'rcid' );
00957 
00958                 if ( !$rcid || !$this->getTitle()->quickUserCan( 'patrol', $user ) ) {
00959                         return;
00960                 }
00961 
00962                 $token = $user->getEditToken( $rcid );
00963                 $outputPage->preventClickjacking();
00964 
00965                 $link = Linker::linkKnown(
00966                         $this->getTitle(),
00967                         wfMessage( 'markaspatrolledtext' )->escaped(),
00968                         array(),
00969                         array(
00970                                 'action' => 'markpatrolled',
00971                                 'rcid' => $rcid,
00972                                 'token' => $token,
00973                         )
00974                 );
00975 
00976                 $outputPage->addHTML(
00977                         "<div class='patrollink'>" .
00978                                 wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
00979                         '</div>'
00980                 );
00981         }
00982 
00987         public function showMissingArticle() {
00988                 global $wgSend404Code;
00989                 $outputPage = $this->getContext()->getOutput();
00990 
00991                 # Show info in user (talk) namespace. Does the user exist? Is he blocked?
00992                 if ( $this->getTitle()->getNamespace() == NS_USER || $this->getTitle()->getNamespace() == NS_USER_TALK ) {
00993                         $parts = explode( '/', $this->getTitle()->getText() );
00994                         $rootPart = $parts[0];
00995                         $user = User::newFromName( $rootPart, false /* allow IP users*/ );
00996                         $ip = User::isIP( $rootPart );
00997 
00998                         if ( !($user && $user->isLoggedIn()) && !$ip ) { # User does not exist
00999                                 $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
01000                                         array( 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ) );
01001                         } elseif ( $user->isBlocked() ) { # Show log extract if the user is currently blocked
01002                                 LogEventsList::showLogExtract(
01003                                         $outputPage,
01004                                         'block',
01005                                         $user->getUserPage(),
01006                                         '',
01007                                         array(
01008                                                 'lim' => 1,
01009                                                 'showIfEmpty' => false,
01010                                                 'msgKey' => array(
01011                                                         'blocked-notice-logextract',
01012                                                         $user->getName() # Support GENDER in notice
01013                                                 )
01014                                         )
01015                                 );
01016                         }
01017                 }
01018 
01019                 wfRunHooks( 'ShowMissingArticle', array( $this ) );
01020 
01021                 # Show delete and move logs
01022                 LogEventsList::showLogExtract( $outputPage, array( 'delete', 'move' ), $this->getTitle(), '',
01023                         array(  'lim' => 10,
01024                                 'conds' => array( "log_action != 'revision'" ),
01025                                 'showIfEmpty' => false,
01026                                 'msgKey' => array( 'moveddeleted-notice' ) )
01027                 );
01028 
01029                 if ( !$this->mPage->hasViewableContent() && $wgSend404Code ) {
01030                         // If there's no backing content, send a 404 Not Found
01031                         // for better machine handling of broken links.
01032                         $this->getContext()->getRequest()->response()->header( "HTTP/1.1 404 Not Found" );
01033                 }
01034 
01035                 $hookResult = wfRunHooks( 'BeforeDisplayNoArticleText', array( $this ) );
01036 
01037                 if ( ! $hookResult ) {
01038                         return;
01039                 }
01040 
01041                 # Show error message
01042                 $oldid = $this->getOldID();
01043                 if ( $oldid ) {
01044                         $text = wfMessage( 'missing-revision', $oldid )->plain();
01045                 } elseif ( $this->getTitle()->getNamespace() === NS_MEDIAWIKI ) {
01046                         // Use the default message text
01047                         $text = $this->getTitle()->getDefaultMessageText();
01048                 } elseif ( $this->getTitle()->quickUserCan( 'create', $this->getContext()->getUser() )
01049                         && $this->getTitle()->quickUserCan( 'edit', $this->getContext()->getUser() )
01050                 ) {
01051                         $text = wfMessage( 'noarticletext' )->plain();
01052                 } else {
01053                         $text = wfMessage( 'noarticletext-nopermission' )->plain();
01054                 }
01055                 $text = "<div class='noarticletext'>\n$text\n</div>";
01056 
01057                 $outputPage->addWikiText( $text );
01058         }
01059 
01066         public function showDeletedRevisionHeader() {
01067                 if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
01068                         // Not deleted
01069                         return true;
01070                 }
01071 
01072                 $outputPage = $this->getContext()->getOutput();
01073                 $user = $this->getContext()->getUser();
01074                 // If the user is not allowed to see it...
01075                 if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
01076                         $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
01077                                 'rev-deleted-text-permission' );
01078 
01079                         return false;
01080                 // If the user needs to confirm that they want to see it...
01081                 } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
01082                         # Give explanation and add a link to view the revision...
01083                         $oldid = intval( $this->getOldID() );
01084                         $link = $this->getTitle()->getFullUrl( "oldid={$oldid}&unhide=1" );
01085                         $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
01086                                 'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
01087                         $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
01088                                 array( $msg, $link ) );
01089 
01090                         return false;
01091                 // We are allowed to see...
01092                 } else {
01093                         $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
01094                                 'rev-suppressed-text-view' : 'rev-deleted-text-view';
01095                         $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
01096 
01097                         return true;
01098                 }
01099         }
01100 
01109         public function setOldSubtitle( $oldid = 0 ) {
01110                 if ( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) {
01111                         return;
01112                 }
01113 
01114                 $unhide = $this->getContext()->getRequest()->getInt( 'unhide' ) == 1;
01115 
01116                 # Cascade unhide param in links for easy deletion browsing
01117                 $extraParams = array();
01118                 if ( $unhide ) {
01119                         $extraParams['unhide'] = 1;
01120                 }
01121 
01122                 if ( $this->mRevision && $this->mRevision->getId() === $oldid ) {
01123                         $revision = $this->mRevision;
01124                 } else {
01125                         $revision = Revision::newFromId( $oldid );
01126                 }
01127 
01128                 $timestamp = $revision->getTimestamp();
01129 
01130                 $current = ( $oldid == $this->mPage->getLatest() );
01131                 $language = $this->getContext()->getLanguage();
01132                 $user = $this->getContext()->getUser();
01133 
01134                 $td = $language->userTimeAndDate( $timestamp, $user );
01135                 $tddate = $language->userDate( $timestamp, $user );
01136                 $tdtime = $language->userTime( $timestamp, $user );
01137 
01138                 # Show user links if allowed to see them. If hidden, then show them only if requested...
01139                 $userlinks = Linker::revUserTools( $revision, !$unhide );
01140 
01141                 $infomsg = $current && !wfMessage( 'revision-info-current' )->isDisabled()
01142                         ? 'revision-info-current'
01143                         : 'revision-info';
01144 
01145                 $outputPage = $this->getContext()->getOutput();
01146                 $outputPage->addSubtitle( "<div id=\"mw-{$infomsg}\">" . wfMessage( $infomsg,
01147                         $td )->rawParams( $userlinks )->params( $revision->getID(), $tddate,
01148                         $tdtime, $revision->getUser() )->parse() . "</div>" );
01149 
01150                 $lnk = $current
01151                         ? wfMessage( 'currentrevisionlink' )->escaped()
01152                         : Linker::linkKnown(
01153                                 $this->getTitle(),
01154                                 wfMessage( 'currentrevisionlink' )->escaped(),
01155                                 array(),
01156                                 $extraParams
01157                         );
01158                 $curdiff = $current
01159                         ? wfMessage( 'diff' )->escaped()
01160                         : Linker::linkKnown(
01161                                 $this->getTitle(),
01162                                 wfMessage( 'diff' )->escaped(),
01163                                 array(),
01164                                 array(
01165                                         'diff' => 'cur',
01166                                         'oldid' => $oldid
01167                                 ) + $extraParams
01168                         );
01169                 $prev = $this->getTitle()->getPreviousRevisionID( $oldid ) ;
01170                 $prevlink = $prev
01171                         ? Linker::linkKnown(
01172                                 $this->getTitle(),
01173                                 wfMessage( 'previousrevision' )->escaped(),
01174                                 array(),
01175                                 array(
01176                                         'direction' => 'prev',
01177                                         'oldid' => $oldid
01178                                 ) + $extraParams
01179                         )
01180                         : wfMessage( 'previousrevision' )->escaped();
01181                 $prevdiff = $prev
01182                         ? Linker::linkKnown(
01183                                 $this->getTitle(),
01184                                 wfMessage( 'diff' )->escaped(),
01185                                 array(),
01186                                 array(
01187                                         'diff' => 'prev',
01188                                         'oldid' => $oldid
01189                                 ) + $extraParams
01190                         )
01191                         : wfMessage( 'diff' )->escaped();
01192                 $nextlink = $current
01193                         ? wfMessage( 'nextrevision' )->escaped()
01194                         : Linker::linkKnown(
01195                                 $this->getTitle(),
01196                                 wfMessage( 'nextrevision' )->escaped(),
01197                                 array(),
01198                                 array(
01199                                         'direction' => 'next',
01200                                         'oldid' => $oldid
01201                                 ) + $extraParams
01202                         );
01203                 $nextdiff = $current
01204                         ? wfMessage( 'diff' )->escaped()
01205                         : Linker::linkKnown(
01206                                 $this->getTitle(),
01207                                 wfMessage( 'diff' )->escaped(),
01208                                 array(),
01209                                 array(
01210                                         'diff' => 'next',
01211                                         'oldid' => $oldid
01212                                 ) + $extraParams
01213                         );
01214 
01215                 $cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
01216                 if ( $cdel !== '' ) {
01217                         $cdel .= ' ';
01218                 }
01219 
01220                 $outputPage->addSubtitle( "<div id=\"mw-revision-nav\">" . $cdel .
01221                         wfMessage( 'revision-nav' )->rawParams(
01222                                 $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
01223                         )->escaped() . "</div>" );
01224         }
01225 
01234         public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
01235                 global $wgStylePath;
01236 
01237                 if ( !is_array( $target ) ) {
01238                         $target = array( $target );
01239                 }
01240 
01241                 $lang = $this->getTitle()->getPageLanguage();
01242                 $imageDir = $lang->getDir();
01243 
01244                 if ( $appendSubtitle ) {
01245                         $out = $this->getContext()->getOutput();
01246                         $out->addSubtitle( wfMessage( 'redirectpagesub' )->escaped() );
01247                 }
01248 
01249                 // the loop prepends the arrow image before the link, so the first case needs to be outside
01250 
01254                 $title = array_shift( $target );
01255 
01256                 if ( $forceKnown ) {
01257                         $link = Linker::linkKnown( $title, htmlspecialchars( $title->getFullText() ) );
01258                 } else {
01259                         $link = Linker::link( $title, htmlspecialchars( $title->getFullText() ) );
01260                 }
01261 
01262                 $nextRedirect = $wgStylePath . '/common/images/nextredirect' . $imageDir . '.png';
01263                 $alt = $lang->isRTL() ? '←' : '→';
01264                 // Automatically append redirect=no to each link, since most of them are redirect pages themselves.
01265                 foreach ( $target as $rt ) {
01266                         $link .= Html::element( 'img', array( 'src' => $nextRedirect, 'alt' => $alt ) );
01267                         if ( $forceKnown ) {
01268                                 $link .= Linker::linkKnown( $rt, htmlspecialchars( $rt->getFullText(), array(), array( 'redirect' => 'no' ) ) );
01269                         } else {
01270                                 $link .= Linker::link( $rt, htmlspecialchars( $rt->getFullText() ), array(), array( 'redirect' => 'no' ) );
01271                         }
01272                 }
01273 
01274                 $imageUrl = $wgStylePath . '/common/images/redirect' . $imageDir . '.png';
01275                 return '<div class="redirectMsg">' .
01276                         Html::element( 'img', array( 'src' => $imageUrl, 'alt' => '#REDIRECT' ) ) .
01277                         '<span class="redirectText">' . $link . '</span></div>';
01278         }
01279 
01283         public function render() {
01284                 $this->getContext()->getOutput()->setArticleBodyOnly( true );
01285                 $this->view();
01286         }
01287 
01291         public function protect() {
01292                 $form = new ProtectionForm( $this );
01293                 $form->execute();
01294         }
01295 
01299         public function unprotect() {
01300                 $this->protect();
01301         }
01302 
01306         public function delete() {
01307                 # This code desperately needs to be totally rewritten
01308 
01309                 $title = $this->getTitle();
01310                 $user = $this->getContext()->getUser();
01311 
01312                 # Check permissions
01313                 $permission_errors = $title->getUserPermissionsErrors( 'delete', $user );
01314                 if ( count( $permission_errors ) ) {
01315                         throw new PermissionsError( 'delete', $permission_errors );
01316                 }
01317 
01318                 # Read-only check...
01319                 if ( wfReadOnly() ) {
01320                         throw new ReadOnlyError;
01321                 }
01322 
01323                 # Better double-check that it hasn't been deleted yet!
01324                 $this->mPage->loadPageData( 'fromdbmaster' );
01325                 if ( !$this->mPage->exists() ) {
01326                         $deleteLogPage = new LogPage( 'delete' );
01327                         $outputPage = $this->getContext()->getOutput();
01328                         $outputPage->setPageTitle( wfMessage( 'cannotdelete-title', $title->getPrefixedText() ) );
01329                         $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
01330                                         array( 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) )
01331                                 );
01332                         $outputPage->addHTML(
01333                                 Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
01334                         );
01335                         LogEventsList::showLogExtract(
01336                                 $outputPage,
01337                                 'delete',
01338                                 $title
01339                         );
01340 
01341                         return;
01342                 }
01343 
01344                 $request = $this->getContext()->getRequest();
01345                 $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
01346                 $deleteReason = $request->getText( 'wpReason' );
01347 
01348                 if ( $deleteReasonList == 'other' ) {
01349                         $reason = $deleteReason;
01350                 } elseif ( $deleteReason != '' ) {
01351                         // Entry from drop down menu + additional comment
01352                         $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
01353                         $reason = $deleteReasonList . $colonseparator . $deleteReason;
01354                 } else {
01355                         $reason = $deleteReasonList;
01356                 }
01357 
01358                 if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
01359                         array( 'delete', $this->getTitle()->getPrefixedText() ) ) )
01360                 {
01361                         # Flag to hide all contents of the archived revisions
01362                         $suppress = $request->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
01363 
01364                         $this->doDelete( $reason, $suppress );
01365 
01366                         if ( $user->isLoggedIn() && $request->getCheck( 'wpWatch' ) != $user->isWatched( $title ) ) {
01367                                 if ( $request->getCheck( 'wpWatch' ) ) {
01368                                         WatchAction::doWatch( $title, $user );
01369                                 } else {
01370                                         WatchAction::doUnwatch( $title, $user );
01371                                 }
01372                         }
01373 
01374                         return;
01375                 }
01376 
01377                 // Generate deletion reason
01378                 $hasHistory = false;
01379                 if ( !$reason ) {
01380                         $reason = $this->generateReason( $hasHistory );
01381                 }
01382 
01383                 // If the page has a history, insert a warning
01384                 if ( $hasHistory ) {
01385                         $revisions = $this->mTitle->estimateRevisionCount();
01386                         // @todo FIXME: i18n issue/patchwork message
01387                         $this->getContext()->getOutput()->addHTML( '<strong class="mw-delete-warning-revisions">' .
01388                                 wfMessage( 'historywarning' )->numParams( $revisions )->parse() .
01389                                 wfMessage( 'word-separator' )->plain() . Linker::linkKnown( $title,
01390                                         wfMessage( 'history' )->escaped(),
01391                                         array( 'rel' => 'archives' ),
01392                                         array( 'action' => 'history' ) ) .
01393                                 '</strong>'
01394                         );
01395 
01396                         if ( $this->mTitle->isBigDeletion() ) {
01397                                 global $wgDeleteRevisionsLimit;
01398                                 $this->getContext()->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
01399                                         array( 'delete-warning-toobig', $this->getContext()->getLanguage()->formatNum( $wgDeleteRevisionsLimit ) ) );
01400                         }
01401                 }
01402 
01403                 $this->confirmDelete( $reason );
01404         }
01405 
01411         public function confirmDelete( $reason ) {
01412                 wfDebug( "Article::confirmDelete\n" );
01413 
01414                 $outputPage = $this->getContext()->getOutput();
01415                 $outputPage->setPageTitle( wfMessage( 'delete-confirm', $this->getTitle()->getPrefixedText() ) );
01416                 $outputPage->addBacklinkSubtitle( $this->getTitle() );
01417                 $outputPage->setRobotPolicy( 'noindex,nofollow' );
01418                 $outputPage->addWikiMsg( 'confirmdeletetext' );
01419 
01420                 wfRunHooks( 'ArticleConfirmDelete', array( $this, $outputPage, &$reason ) );
01421 
01422                 $user = $this->getContext()->getUser();
01423 
01424                 if ( $user->isAllowed( 'suppressrevision' ) ) {
01425                         $suppress = "<tr id=\"wpDeleteSuppressRow\">
01426                                         <td></td>
01427                                         <td class='mw-input'><strong>" .
01428                                                 Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
01429                                                         'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '4' ) ) .
01430                                         "</strong></td>
01431                                 </tr>";
01432                 } else {
01433                         $suppress = '';
01434                 }
01435                 $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $this->getTitle() );
01436 
01437                 $form = Xml::openElement( 'form', array( 'method' => 'post',
01438                         'action' => $this->getTitle()->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) .
01439                         Xml::openElement( 'fieldset', array( 'id' => 'mw-delete-table' ) ) .
01440                         Xml::tags( 'legend', null, wfMessage( 'delete-legend' )->escaped() ) .
01441                         Xml::openElement( 'table', array( 'id' => 'mw-deleteconfirm-table' ) ) .
01442                         "<tr id=\"wpDeleteReasonListRow\">
01443                                 <td class='mw-label'>" .
01444                                         Xml::label( wfMessage( 'deletecomment' )->text(), 'wpDeleteReasonList' ) .
01445                                 "</td>
01446                                 <td class='mw-input'>" .
01447                                         Xml::listDropDown( 'wpDeleteReasonList',
01448                                                 wfMessage( 'deletereason-dropdown' )->inContentLanguage()->text(),
01449                                                 wfMessage( 'deletereasonotherlist' )->inContentLanguage()->text(), '', 'wpReasonDropDown', 1 ) .
01450                                 "</td>
01451                         </tr>
01452                         <tr id=\"wpDeleteReasonRow\">
01453                                 <td class='mw-label'>" .
01454                                         Xml::label( wfMessage( 'deleteotherreason' )->text(), 'wpReason' ) .
01455                                 "</td>
01456                                 <td class='mw-input'>" .
01457                                 Html::input( 'wpReason', $reason, 'text', array(
01458                                         'size' => '60',
01459                                         'maxlength' => '255',
01460                                         'tabindex' => '2',
01461                                         'id' => 'wpReason',
01462                                         'autofocus'
01463                                 ) ) .
01464                                 "</td>
01465                         </tr>";
01466 
01467                 # Disallow watching if user is not logged in
01468                 if ( $user->isLoggedIn() ) {
01469                         $form .= "
01470                         <tr>
01471                                 <td></td>
01472                                 <td class='mw-input'>" .
01473                                         Xml::checkLabel( wfMessage( 'watchthis' )->text(),
01474                                                 'wpWatch', 'wpWatch', $checkWatch, array( 'tabindex' => '3' ) ) .
01475                                 "</td>
01476                         </tr>";
01477                 }
01478 
01479                 $form .= "
01480                         $suppress
01481                         <tr>
01482                                 <td></td>
01483                                 <td class='mw-submit'>" .
01484                                         Xml::submitButton( wfMessage( 'deletepage' )->text(),
01485                                                 array( 'name' => 'wpConfirmB', 'id' => 'wpConfirmB', 'tabindex' => '5' ) ) .
01486                                 "</td>
01487                         </tr>" .
01488                         Xml::closeElement( 'table' ) .
01489                         Xml::closeElement( 'fieldset' ) .
01490                         Html::hidden( 'wpEditToken', $user->getEditToken( array( 'delete', $this->getTitle()->getPrefixedText() ) ) ) .
01491                         Xml::closeElement( 'form' );
01492 
01493                         if ( $user->isAllowed( 'editinterface' ) ) {
01494                                 $title = Title::makeTitle( NS_MEDIAWIKI, 'Deletereason-dropdown' );
01495                                 $link = Linker::link(
01496                                         $title,
01497                                         wfMessage( 'delete-edit-reasonlist' )->escaped(),
01498                                         array(),
01499                                         array( 'action' => 'edit' )
01500                                 );
01501                                 $form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
01502                         }
01503 
01504                 $outputPage->addHTML( $form );
01505 
01506                 $deleteLogPage = new LogPage( 'delete' );
01507                 $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
01508                 LogEventsList::showLogExtract( $outputPage, 'delete',
01509                         $this->getTitle()
01510                 );
01511         }
01512 
01518         public function doDelete( $reason, $suppress = false ) {
01519                 $error = '';
01520                 $outputPage = $this->getContext()->getOutput();
01521                 $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error );
01522                 if ( $status->isGood() ) {
01523                         $deleted = $this->getTitle()->getPrefixedText();
01524 
01525                         $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
01526                         $outputPage->setRobotPolicy( 'noindex,nofollow' );
01527 
01528                         $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
01529 
01530                         $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
01531                         $outputPage->returnToMain( false );
01532                 } else {
01533                         $outputPage->setPageTitle( wfMessage( 'cannotdelete-title', $this->getTitle()->getPrefixedText() ) );
01534                         if ( $error == '' ) {
01535                                 $outputPage->addWikiText(
01536                                         "<div class=\"error mw-error-cannotdelete\">\n" . $status->getWikiText() . "\n</div>"
01537                                 );
01538                                 $deleteLogPage = new LogPage( 'delete' );
01539                                 $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
01540 
01541                                 LogEventsList::showLogExtract(
01542                                         $outputPage,
01543                                         'delete',
01544                                         $this->getTitle()
01545                                 );
01546                         } else {
01547                                 $outputPage->addHTML( $error );
01548                         }
01549                 }
01550         }
01551 
01552         /* Caching functions */
01553 
01561         protected function tryFileCache() {
01562                 static $called = false;
01563 
01564                 if ( $called ) {
01565                         wfDebug( "Article::tryFileCache(): called twice!?\n" );
01566                         return false;
01567                 }
01568 
01569                 $called = true;
01570                 if ( $this->isFileCacheable() ) {
01571                         $cache = HTMLFileCache::newFromTitle( $this->getTitle(), 'view' );
01572                         if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
01573                                 wfDebug( "Article::tryFileCache(): about to load file\n" );
01574                                 $cache->loadFromFileCache( $this->getContext() );
01575                                 return true;
01576                         } else {
01577                                 wfDebug( "Article::tryFileCache(): starting buffer\n" );
01578                                 ob_start( array( &$cache, 'saveToFileCache' ) );
01579                         }
01580                 } else {
01581                         wfDebug( "Article::tryFileCache(): not cacheable\n" );
01582                 }
01583 
01584                 return false;
01585         }
01586 
01591         public function isFileCacheable() {
01592                 $cacheable = false;
01593 
01594                 if ( HTMLFileCache::useFileCache( $this->getContext() ) ) {
01595                         $cacheable = $this->mPage->getID()
01596                                 && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
01597                         // Extension may have reason to disable file caching on some pages.
01598                         if ( $cacheable ) {
01599                                 $cacheable = wfRunHooks( 'IsFileCacheable', array( &$this ) );
01600                         }
01601                 }
01602 
01603                 return $cacheable;
01604         }
01605 
01619         public function getParserOutput( $oldid = null, User $user = null ) {
01620                 if ( $user === null ) {
01621                         $parserOptions = $this->getParserOptions();
01622                 } else {
01623                         $parserOptions = $this->mPage->makeParserOptions( $user );
01624                 }
01625 
01626                 return $this->mPage->getParserOutput( $parserOptions, $oldid );
01627         }
01628 
01633         public function getParserOptions() {
01634                 if ( !$this->mParserOptions ) {
01635                         $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
01636                 }
01637                 // Clone to allow modifications of the return value without affecting cache
01638                 return clone $this->mParserOptions;
01639         }
01640 
01647         public function setContext( $context ) {
01648                 $this->mContext = $context;
01649         }
01650 
01657         public function getContext() {
01658                 if ( $this->mContext instanceof IContextSource ) {
01659                         return $this->mContext;
01660                 } else {
01661                         wfDebug( __METHOD__ . " called and \$mContext is null. Return RequestContext::getMain(); for sanity\n" );
01662                         return RequestContext::getMain();
01663                 }
01664         }
01665 
01670         public function info() {
01671                 wfDeprecated( __METHOD__, '1.19' );
01672                 Action::factory( 'info', $this )->show();
01673         }
01674 
01679         public function markpatrolled() {
01680                 wfDeprecated( __METHOD__, '1.18' );
01681                 Action::factory( 'markpatrolled', $this )->show();
01682         }
01683 
01689         public function purge() {
01690                 return Action::factory( 'purge', $this )->show();
01691         }
01692 
01697         public function revert() {
01698                 wfDeprecated( __METHOD__, '1.19' );
01699                 Action::factory( 'revert', $this )->show();
01700         }
01701 
01706         public function rollback() {
01707                 wfDeprecated( __METHOD__, '1.19' );
01708                 Action::factory( 'rollback', $this )->show();
01709         }
01710 
01716         public function watch() {
01717                 wfDeprecated( __METHOD__, '1.18' );
01718                 Action::factory( 'watch', $this )->show();
01719         }
01720 
01729         public function doWatch() {
01730                 wfDeprecated( __METHOD__, '1.18' );
01731                 return WatchAction::doWatch( $this->getTitle(), $this->getContext()->getUser() );
01732         }
01733 
01739         public function unwatch() {
01740                 wfDeprecated( __METHOD__, '1.18' );
01741                 Action::factory( 'unwatch', $this )->show();
01742         }
01743 
01749         public function doUnwatch() {
01750                 wfDeprecated( __METHOD__, '1.18' );
01751                 return WatchAction::doUnwatch( $this->getTitle(), $this->getContext()->getUser() );
01752         }
01753 
01763         public function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) {
01764                 wfDeprecated( __METHOD__, '1.18' );
01765                 if ( $noRedir ) {
01766                         $query = 'redirect=no';
01767                         if ( $extraQuery )
01768                                 $query .= "&$extraQuery";
01769                 } else {
01770                         $query = $extraQuery;
01771                 }
01772 
01773                 $this->getContext()->getOutput()->redirect( $this->getTitle()->getFullURL( $query ) . $sectionAnchor );
01774         }
01775 
01782         public function __get( $fname ) {
01783                 if ( property_exists( $this->mPage, $fname ) ) {
01784                         #wfWarn( "Access to raw $fname field " . __CLASS__ );
01785                         return $this->mPage->$fname;
01786                 }
01787                 trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
01788         }
01789 
01797         public function __set( $fname, $fvalue ) {
01798                 if ( property_exists( $this->mPage, $fname ) ) {
01799                         #wfWarn( "Access to raw $fname field of " . __CLASS__ );
01800                         $this->mPage->$fname = $fvalue;
01801                 // Note: extensions may want to toss on new fields
01802                 } elseif ( !in_array( $fname, array( 'mContext', 'mPage' ) ) ) {
01803                         $this->mPage->$fname = $fvalue;
01804                 } else {
01805                         trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
01806                 }
01807         }
01808 
01817         public function __call( $fname, $args ) {
01818                 if ( is_callable( array( $this->mPage, $fname ) ) ) {
01819                         #wfWarn( "Call to " . __CLASS__ . "::$fname; please use WikiPage instead" );
01820                         return call_user_func_array( array( $this->mPage, $fname ), $args );
01821                 }
01822                 trigger_error( 'Inaccessible function via __call(): ' . $fname, E_USER_ERROR );
01823         }
01824 
01825         // ****** B/C functions to work-around PHP silliness with __call and references ****** //
01826 
01835         public function doUpdateRestrictions( array $limit, array $expiry, &$cascade, $reason, User $user ) {
01836                 return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
01837         }
01838 
01846         public function updateRestrictions( $limit = array(), $reason = '', &$cascade = 0, $expiry = array() ) {
01847                 return $this->mPage->updateRestrictions( $limit, $reason, $cascade, $expiry );
01848         }
01849 
01858         public function doDeleteArticle( $reason, $suppress = false, $id = 0, $commit = true, &$error = '' ) {
01859                 return $this->mPage->doDeleteArticle( $reason, $suppress, $id, $commit, $error );
01860         }
01861 
01871         public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
01872                 $user = is_null( $user ) ? $this->getContext()->getUser() : $user;
01873                 return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
01874         }
01875 
01884         public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
01885                 $guser = is_null( $guser ) ? $this->getContext()->getUser() : $guser;
01886                 return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
01887         }
01888 
01893         public function generateReason( &$hasHistory ) {
01894                 return $this->mPage->getAutoDeleteReason( $hasHistory );
01895         }
01896 
01897         // ****** B/C functions for static methods ( __callStatic is PHP>=5.3 ) ****** //
01898 
01902         public static function selectFields() {
01903                 return WikiPage::selectFields();
01904         }
01905 
01909         public static function onArticleCreate( $title ) {
01910                 WikiPage::onArticleCreate( $title );
01911         }
01912 
01916         public static function onArticleDelete( $title ) {
01917                 WikiPage::onArticleDelete( $title );
01918         }
01919 
01923         public static function onArticleEdit( $title ) {
01924                 WikiPage::onArticleEdit( $title );
01925         }
01926 
01933         public static function getAutosummary( $oldtext, $newtext, $flags ) {
01934                 return WikiPage::getAutosummary( $oldtext, $newtext, $flags );
01935         }
01936         // ******
01937 }