MediaWiki  REL1_21
Article.php
Go to the documentation of this file.
00001 <?php
00036 class Article implements Page {
00045         protected $mContext;
00046 
00051         protected $mPage;
00052 
00057         public $mParserOptions;
00058 
00063         var $mContent;                    // !< #BC cruft
00064 
00070         var $mContentObject;              // !<
00071 
00076         var $mContentLoaded = false;      // !<
00077 
00083         var $mOldId;                      // !<
00084 
00089         var $mRedirectedFrom = null;
00090 
00095         var $mRedirectUrl = false;        // !<
00096 
00101         var $mRevIdFetched = 0;           // !<
00102 
00107         var $mRevision = null;
00108 
00113         var $mParserOutput;
00114 
00122         public function __construct( Title $title, $oldId = null ) {
00123                 $this->mOldId = $oldId;
00124                 $this->mPage = $this->newPage( $title );
00125         }
00126 
00131         protected function newPage( Title $title ) {
00132                 return new WikiPage( $title );
00133         }
00134 
00140         public static function newFromID( $id ) {
00141                 $t = Title::newFromID( $id );
00142                 # @todo FIXME: Doesn't inherit right
00143                 return $t == null ? null : new self( $t );
00144                 # return $t == null ? null : new static( $t ); // PHP 5.3
00145         }
00146 
00154         public static function newFromTitle( $title, IContextSource $context ) {
00155                 if ( NS_MEDIA == $title->getNamespace() ) {
00156                         // FIXME: where should this go?
00157                         $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
00158                 }
00159 
00160                 $page = null;
00161                 wfRunHooks( 'ArticleFromTitle', array( &$title, &$page ) );
00162                 if ( !$page ) {
00163                         switch( $title->getNamespace() ) {
00164                                 case NS_FILE:
00165                                         $page = new ImagePage( $title );
00166                                         break;
00167                                 case NS_CATEGORY:
00168                                         $page = new CategoryPage( $title );
00169                                         break;
00170                                 default:
00171                                         $page = new Article( $title );
00172                         }
00173                 }
00174                 $page->setContext( $context );
00175 
00176                 return $page;
00177         }
00178 
00186         public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
00187                 $article = self::newFromTitle( $page->getTitle(), $context );
00188                 $article->mPage = $page; // override to keep process cached vars
00189                 return $article;
00190         }
00191 
00197         public function setRedirectedFrom( Title $from ) {
00198                 $this->mRedirectedFrom = $from;
00199         }
00200 
00206         public function getTitle() {
00207                 return $this->mPage->getTitle();
00208         }
00209 
00216         public function getPage() {
00217                 return $this->mPage;
00218         }
00219 
00223         public function clear() {
00224                 $this->mContentLoaded = false;
00225 
00226                 $this->mRedirectedFrom = null; # Title object if set
00227                 $this->mRevIdFetched = 0;
00228                 $this->mRedirectUrl = false;
00229 
00230                 $this->mPage->clear();
00231         }
00232 
00245         public function getContent() {
00246                 ContentHandler::deprecated( __METHOD__, '1.21' );
00247                 $content = $this->getContentObject();
00248                 return ContentHandler::getContentText( $content );
00249         }
00250 
00266         protected function getContentObject() {
00267                 wfProfileIn( __METHOD__ );
00268 
00269                 if ( $this->mPage->getID() === 0 ) {
00270                         # If this is a MediaWiki:x message, then load the messages
00271                         # and return the message value for x.
00272                         if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
00273                                 $text = $this->getTitle()->getDefaultMessageText();
00274                                 if ( $text === false ) {
00275                                         $text = '';
00276                                 }
00277 
00278                                 $content = ContentHandler::makeContent( $text, $this->getTitle() );
00279                         } else {
00280                                 $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon';
00281                                 $content = new MessageContent( $message, null, 'parsemag' );
00282                         }
00283                 } else {
00284                         $this->fetchContentObject();
00285                         $content = $this->mContentObject;
00286                 }
00287 
00288                 wfProfileOut( __METHOD__ );
00289                 return $content;
00290         }
00291 
00296         public function getOldID() {
00297                 if ( is_null( $this->mOldId ) ) {
00298                         $this->mOldId = $this->getOldIDFromRequest();
00299                 }
00300 
00301                 return $this->mOldId;
00302         }
00303 
00309         public function getOldIDFromRequest() {
00310                 $this->mRedirectUrl = false;
00311 
00312                 $request = $this->getContext()->getRequest();
00313                 $oldid = $request->getIntOrNull( 'oldid' );
00314 
00315                 if ( $oldid === null ) {
00316                         return 0;
00317                 }
00318 
00319                 if ( $oldid !== 0 ) {
00320                         # Load the given revision and check whether the page is another one.
00321                         # In that case, update this instance to reflect the change.
00322                         if ( $oldid === $this->mPage->getLatest() ) {
00323                                 $this->mRevision = $this->mPage->getRevision();
00324                         } else {
00325                                 $this->mRevision = Revision::newFromId( $oldid );
00326                                 if ( $this->mRevision !== null ) {
00327                                         // Revision title doesn't match the page title given?
00328                                         if ( $this->mPage->getID() != $this->mRevision->getPage() ) {
00329                                                 $function = array( get_class( $this->mPage ), 'newFromID' );
00330                                                 $this->mPage = call_user_func( $function, $this->mRevision->getPage() );
00331                                         }
00332                                 }
00333                         }
00334                 }
00335 
00336                 if ( $request->getVal( 'direction' ) == 'next' ) {
00337                         $nextid = $this->getTitle()->getNextRevisionID( $oldid );
00338                         if ( $nextid ) {
00339                                 $oldid = $nextid;
00340                                 $this->mRevision = null;
00341                         } else {
00342                                 $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
00343                         }
00344                 } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
00345                         $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
00346                         if ( $previd ) {
00347                                 $oldid = $previd;
00348                                 $this->mRevision = null;
00349                         }
00350                 }
00351 
00352                 return $oldid;
00353         }
00354 
00360         function loadContent() {
00361                 wfDeprecated( __METHOD__, '1.19' );
00362                 $this->fetchContent();
00363         }
00364 
00377         function fetchContent() { #BC cruft!
00378                 ContentHandler::deprecated( __METHOD__, '1.21' );
00379 
00380                 if ( $this->mContentLoaded && $this->mContent ) {
00381                         return $this->mContent;
00382                 }
00383 
00384                 wfProfileIn( __METHOD__ );
00385 
00386                 $content = $this->fetchContentObject();
00387 
00388                 $this->mContent = ContentHandler::getContentText( $content ); #@todo: get rid of mContent everywhere!
00389                 ContentHandler::runLegacyHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) );
00390 
00391                 wfProfileOut( __METHOD__ );
00392 
00393                 return $this->mContent;
00394         }
00395 
00407         protected function fetchContentObject() {
00408                 if ( $this->mContentLoaded ) {
00409                         return $this->mContentObject;
00410                 }
00411 
00412                 wfProfileIn( __METHOD__ );
00413 
00414                 $this->mContentLoaded = true;
00415                 $this->mContent = null;
00416 
00417                 $oldid = $this->getOldID();
00418 
00419                 # Pre-fill content with error message so that if something
00420                 # fails we'll have something telling us what we intended.
00421                 //XXX: this isn't page content but a UI message. horrible.
00422                 $this->mContentObject = new MessageContent( 'missing-revision', array( $oldid ), array() );
00423 
00424                 if ( $oldid ) {
00425                         # $this->mRevision might already be fetched by getOldIDFromRequest()
00426                         if ( !$this->mRevision ) {
00427                                 $this->mRevision = Revision::newFromId( $oldid );
00428                                 if ( !$this->mRevision ) {
00429                                         wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
00430                                         wfProfileOut( __METHOD__ );
00431                                         return false;
00432                                 }
00433                         }
00434                 } else {
00435                         if ( !$this->mPage->getLatest() ) {
00436                                 wfDebug( __METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() . "\n" );
00437                                 wfProfileOut( __METHOD__ );
00438                                 return false;
00439                         }
00440 
00441                         $this->mRevision = $this->mPage->getRevision();
00442 
00443                         if ( !$this->mRevision ) {
00444                                 wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" );
00445                                 wfProfileOut( __METHOD__ );
00446                                 return false;
00447                         }
00448                 }
00449 
00450                 // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
00451                 // We should instead work with the Revision object when we need it...
00452                 $this->mContentObject = $this->mRevision->getContent( Revision::FOR_THIS_USER, $this->getContext()->getUser() ); // Loads if user is allowed
00453                 $this->mRevIdFetched = $this->mRevision->getId();
00454 
00455                 wfRunHooks( 'ArticleAfterFetchContentObject', array( &$this, &$this->mContentObject ) );
00456 
00457                 wfProfileOut( __METHOD__ );
00458 
00459                 return $this->mContentObject;
00460         }
00461 
00466         public function forUpdate() {
00467                 wfDeprecated( __METHOD__, '1.18' );
00468         }
00469 
00475         public function isCurrent() {
00476                 # If no oldid, this is the current version.
00477                 if ( $this->getOldID() == 0 ) {
00478                         return true;
00479                 }
00480 
00481                 return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent();
00482         }
00483 
00491         public function getRevisionFetched() {
00492                 $this->fetchContentObject();
00493 
00494                 return $this->mRevision;
00495         }
00496 
00502         public function getRevIdFetched() {
00503                 if ( $this->mRevIdFetched ) {
00504                         return $this->mRevIdFetched;
00505                 } else {
00506                         return $this->mPage->getLatest();
00507                 }
00508         }
00509 
00514         public function view() {
00515                 global $wgUseFileCache, $wgUseETag, $wgDebugToolbar;
00516 
00517                 wfProfileIn( __METHOD__ );
00518 
00519                 # Get variables from query string
00520                 # As side effect this will load the revision and update the title
00521                 # in a revision ID is passed in the request, so this should remain
00522                 # the first call of this method even if $oldid is used way below.
00523                 $oldid = $this->getOldID();
00524 
00525                 $user = $this->getContext()->getUser();
00526                 # Another whitelist check in case getOldID() is altering the title
00527                 $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user );
00528                 if ( count( $permErrors ) ) {
00529                         wfDebug( __METHOD__ . ": denied on secondary read check\n" );
00530                         wfProfileOut( __METHOD__ );
00531                         throw new PermissionsError( 'read', $permErrors );
00532                 }
00533 
00534                 $outputPage = $this->getContext()->getOutput();
00535                 # getOldID() may as well want us to redirect somewhere else
00536                 if ( $this->mRedirectUrl ) {
00537                         $outputPage->redirect( $this->mRedirectUrl );
00538                         wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
00539                         wfProfileOut( __METHOD__ );
00540 
00541                         return;
00542                 }
00543 
00544                 # If we got diff in the query, we want to see a diff page instead of the article.
00545                 if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) {
00546                         wfDebug( __METHOD__ . ": showing diff page\n" );
00547                         $this->showDiffPage();
00548                         wfProfileOut( __METHOD__ );
00549 
00550                         return;
00551                 }
00552 
00553                 # Set page title (may be overridden by DISPLAYTITLE)
00554                 $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() );
00555 
00556                 $outputPage->setArticleFlag( true );
00557                 # Allow frames by default
00558                 $outputPage->allowClickjacking();
00559 
00560                 $parserCache = ParserCache::singleton();
00561 
00562                 $parserOptions = $this->getParserOptions();
00563                 # Render printable version, use printable version cache
00564                 if ( $outputPage->isPrintable() ) {
00565                         $parserOptions->setIsPrintable( true );
00566                         $parserOptions->setEditSection( false );
00567                 } elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit', $user ) ) {
00568                         $parserOptions->setEditSection( false );
00569                 }
00570 
00571                 # Try client and file cache
00572                 if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) {
00573                         if ( $wgUseETag ) {
00574                                 $outputPage->setETag( $parserCache->getETag( $this, $parserOptions ) );
00575                         }
00576 
00577                         # Is it client cached?
00578                         if ( $outputPage->checkLastModified( $this->mPage->getTouched() ) ) {
00579                                 wfDebug( __METHOD__ . ": done 304\n" );
00580                                 wfProfileOut( __METHOD__ );
00581 
00582                                 return;
00583                         # Try file cache
00584                         } elseif ( $wgUseFileCache && $this->tryFileCache() ) {
00585                                 wfDebug( __METHOD__ . ": done file cache\n" );
00586                                 # tell wgOut that output is taken care of
00587                                 $outputPage->disable();
00588                                 $this->mPage->doViewUpdates( $user );
00589                                 wfProfileOut( __METHOD__ );
00590 
00591                                 return;
00592                         }
00593                 }
00594 
00595                 # Should the parser cache be used?
00596                 $useParserCache = $this->mPage->isParserCacheUsed( $parserOptions, $oldid );
00597                 wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
00598                 if ( $user->getStubThreshold() ) {
00599                         wfIncrStats( 'pcache_miss_stub' );
00600                 }
00601 
00602                 $this->showRedirectedFromHeader();
00603                 $this->showNamespaceHeader();
00604 
00605                 # Iterate through the possible ways of constructing the output text.
00606                 # Keep going until $outputDone is set, or we run out of things to do.
00607                 $pass = 0;
00608                 $outputDone = false;
00609                 $this->mParserOutput = false;
00610 
00611                 while ( !$outputDone && ++$pass ) {
00612                         switch( $pass ) {
00613                                 case 1:
00614                                         wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$useParserCache ) );
00615                                         break;
00616                                 case 2:
00617                                         # Early abort if the page doesn't exist
00618                                         if ( !$this->mPage->exists() ) {
00619                                                 wfDebug( __METHOD__ . ": showing missing article\n" );
00620                                                 $this->showMissingArticle();
00621                                                 wfProfileOut( __METHOD__ );
00622                                                 return;
00623                                         }
00624 
00625                                         # Try the parser cache
00626                                         if ( $useParserCache ) {
00627                                                 $this->mParserOutput = $parserCache->get( $this, $parserOptions );
00628 
00629                                                 if ( $this->mParserOutput !== false ) {
00630                                                         if ( $oldid ) {
00631                                                                 wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" );
00632                                                                 $this->setOldSubtitle( $oldid );
00633                                                         } else {
00634                                                                 wfDebug( __METHOD__ . ": showing parser cache contents\n" );
00635                                                         }
00636                                                         $outputPage->addParserOutput( $this->mParserOutput );
00637                                                         # Ensure that UI elements requiring revision ID have
00638                                                         # the correct version information.
00639                                                         $outputPage->setRevisionId( $this->mPage->getLatest() );
00640                                                         # Preload timestamp to avoid a DB hit
00641                                                         $cachedTimestamp = $this->mParserOutput->getTimestamp();
00642                                                         if ( $cachedTimestamp !== null ) {
00643                                                                 $outputPage->setRevisionTimestamp( $cachedTimestamp );
00644                                                                 $this->mPage->setTimestamp( $cachedTimestamp );
00645                                                         }
00646                                                         $outputDone = true;
00647                                                 }
00648                                         }
00649                                         break;
00650                                 case 3:
00651                                         # This will set $this->mRevision if needed
00652                                         $this->fetchContentObject();
00653 
00654                                         # Are we looking at an old revision
00655                                         if ( $oldid && $this->mRevision ) {
00656                                                 $this->setOldSubtitle( $oldid );
00657 
00658                                                 if ( !$this->showDeletedRevisionHeader() ) {
00659                                                         wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
00660                                                         wfProfileOut( __METHOD__ );
00661                                                         return;
00662                                                 }
00663                                         }
00664 
00665                                         # Ensure that UI elements requiring revision ID have
00666                                         # the correct version information.
00667                                         $outputPage->setRevisionId( $this->getRevIdFetched() );
00668                                         # Preload timestamp to avoid a DB hit
00669                                         $outputPage->setRevisionTimestamp( $this->getTimestamp() );
00670 
00671                                         # Pages containing custom CSS or JavaScript get special treatment
00672                                         if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
00673                                                 wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
00674                                                 $this->showCssOrJsPage();
00675                                                 $outputDone = true;
00676                                         } elseif( !wfRunHooks( 'ArticleContentViewCustom',
00677                                                         array( $this->fetchContentObject(), $this->getTitle(), $outputPage ) ) ) {
00678 
00679                                                 # Allow extensions do their own custom view for certain pages
00680                                                 $outputDone = true;
00681                                         } elseif( !ContentHandler::runLegacyHooks( 'ArticleViewCustom',
00682                                                         array( $this->fetchContentObject(), $this->getTitle(), $outputPage ) ) ) {
00683 
00684                                                 # Allow extensions do their own custom view for certain pages
00685                                                 $outputDone = true;
00686                                         } else {
00687                                                 $content = $this->getContentObject();
00688                                                 $rt = $content ? $content->getRedirectChain() : null;
00689                                                 if ( $rt ) {
00690                                                         wfDebug( __METHOD__ . ": showing redirect=no page\n" );
00691                                                         # Viewing a redirect page (e.g. with parameter redirect=no)
00692                                                         $outputPage->addHTML( $this->viewRedirect( $rt ) );
00693                                                         # Parse just to get categories, displaytitle, etc.
00694                                                         $this->mParserOutput = $content->getParserOutput( $this->getTitle(), $oldid, $parserOptions, false );
00695                                                         $outputPage->addParserOutputNoText( $this->mParserOutput );
00696                                                         $outputDone = true;
00697                                                 }
00698                                         }
00699                                         break;
00700                                 case 4:
00701                                         # Run the parse, protected by a pool counter
00702                                         wfDebug( __METHOD__ . ": doing uncached parse\n" );
00703 
00704                                         $poolArticleView = new PoolWorkArticleView( $this->getPage(), $parserOptions,
00705                                                 $this->getRevIdFetched(), $useParserCache, $this->getContentObject() );
00706 
00707                                         if ( !$poolArticleView->execute() ) {
00708                                                 $error = $poolArticleView->getError();
00709                                                 if ( $error ) {
00710                                                         $outputPage->clearHTML(); // for release() errors
00711                                                         $outputPage->enableClientCache( false );
00712                                                         $outputPage->setRobotPolicy( 'noindex,nofollow' );
00713 
00714                                                         $errortext = $error->getWikiText( false, 'view-pool-error' );
00715                                                         $outputPage->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
00716                                                 }
00717                                                 # Connection or timeout error
00718                                                 wfProfileOut( __METHOD__ );
00719                                                 return;
00720                                         }
00721 
00722                                         $this->mParserOutput = $poolArticleView->getParserOutput();
00723                                         $outputPage->addParserOutput( $this->mParserOutput );
00724 
00725                                         # Don't cache a dirty ParserOutput object
00726                                         if ( $poolArticleView->getIsDirty() ) {
00727                                                 $outputPage->setSquidMaxage( 0 );
00728                                                 $outputPage->addHTML( "<!-- parser cache is expired, sending anyway due to pool overload-->\n" );
00729                                         }
00730 
00731                                         $outputDone = true;
00732                                         break;
00733                                 # Should be unreachable, but just in case...
00734                                 default:
00735                                         break 2;
00736                         }
00737                 }
00738 
00739                 # Get the ParserOutput actually *displayed* here.
00740                 # Note that $this->mParserOutput is the *current* version output.
00741                 $pOutput = ( $outputDone instanceof ParserOutput )
00742                         ? $outputDone // object fetched by hook
00743                         : $this->mParserOutput;
00744 
00745                 # Adjust title for main page & pages with displaytitle
00746                 if ( $pOutput ) {
00747                         $this->adjustDisplayTitle( $pOutput );
00748                 }
00749 
00750                 # For the main page, overwrite the <title> element with the con-
00751                 # tents of 'pagetitle-view-mainpage' instead of the default (if
00752                 # that's not empty).
00753                 # This message always exists because it is in the i18n files
00754                 if ( $this->getTitle()->isMainPage() ) {
00755                         $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
00756                         if ( !$msg->isDisabled() ) {
00757                                 $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
00758                         }
00759                 }
00760 
00761                 # Check for any __NOINDEX__ tags on the page using $pOutput
00762                 $policy = $this->getRobotPolicy( 'view', $pOutput );
00763                 $outputPage->setIndexPolicy( $policy['index'] );
00764                 $outputPage->setFollowPolicy( $policy['follow'] );
00765 
00766                 $this->showViewFooter();
00767                 $this->mPage->doViewUpdates( $user );
00768 
00769                 $outputPage->addModules( 'mediawiki.action.view.postEdit' );
00770 
00771                 wfProfileOut( __METHOD__ );
00772         }
00773 
00778         public function adjustDisplayTitle( ParserOutput $pOutput ) {
00779                 # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
00780                 $titleText = $pOutput->getTitleText();
00781                 if ( strval( $titleText ) !== '' ) {
00782                         $this->getContext()->getOutput()->setPageTitle( $titleText );
00783                 }
00784         }
00785 
00792         public function showDiffPage() {
00793                 $request = $this->getContext()->getRequest();
00794                 $user = $this->getContext()->getUser();
00795                 $diff = $request->getVal( 'diff' );
00796                 $rcid = $request->getVal( 'rcid' );
00797                 $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) );
00798                 $purge = $request->getVal( 'action' ) == 'purge';
00799                 $unhide = $request->getInt( 'unhide' ) == 1;
00800                 $oldid = $this->getOldID();
00801 
00802                 $rev = $this->getRevisionFetched();
00803 
00804                 if ( !$rev ) {
00805                         $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) );
00806                         $this->getContext()->getOutput()->addWikiMsg( 'difference-missing-revision', $oldid, 1 );
00807                         return;
00808                 }
00809 
00810                 $contentHandler = $rev->getContentHandler();
00811                 $de = $contentHandler->createDifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide );
00812 
00813                 // DifferenceEngine directly fetched the revision:
00814                 $this->mRevIdFetched = $de->mNewid;
00815                 $de->showDiffPage( $diffOnly );
00816 
00817                 if ( $diff == 0 || $diff == $this->mPage->getLatest() ) {
00818                         # Run view updates for current revision only
00819                         $this->mPage->doViewUpdates( $user );
00820                 }
00821         }
00822 
00832         protected function showCssOrJsPage( $showCacheHint = true ) {
00833                 $outputPage = $this->getContext()->getOutput();
00834 
00835                 if ( $showCacheHint ) {
00836                         $dir = $this->getContext()->getLanguage()->getDir();
00837                         $lang = $this->getContext()->getLanguage()->getCode();
00838 
00839                         $outputPage->wrapWikiMsg( "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
00840                                 'clearyourcache' );
00841                 }
00842 
00843                 $this->fetchContentObject();
00844 
00845                 if ( $this->mContentObject ) {
00846                         // Give hooks a chance to customise the output
00847                         if ( ContentHandler::runLegacyHooks( 'ShowRawCssJs', array( $this->mContentObject, $this->getTitle(), $outputPage ) ) ) {
00848                                 $po = $this->mContentObject->getParserOutput( $this->getTitle() );
00849                                 $outputPage->addHTML( $po->getText() );
00850                         }
00851                 }
00852         }
00853 
00861         public function getRobotPolicy( $action, $pOutput ) {
00862                 global $wgArticleRobotPolicies, $wgNamespaceRobotPolicies, $wgDefaultRobotPolicy;
00863 
00864                 $ns = $this->getTitle()->getNamespace();
00865 
00866                 # Don't index user and user talk pages for blocked users (bug 11443)
00867                 if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
00868                         $specificTarget = null;
00869                         $vagueTarget = null;
00870                         $titleText = $this->getTitle()->getText();
00871                         if ( IP::isValid( $titleText ) ) {
00872                                 $vagueTarget = $titleText;
00873                         } else {
00874                                 $specificTarget = $titleText;
00875                         }
00876                         if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) {
00877                                 return array(
00878                                         'index'  => 'noindex',
00879                                         'follow' => 'nofollow'
00880                                 );
00881                         }
00882                 }
00883 
00884                 if ( $this->mPage->getID() === 0 || $this->getOldID() ) {
00885                         # Non-articles (special pages etc), and old revisions
00886                         return array(
00887                                 'index'  => 'noindex',
00888                                 'follow' => 'nofollow'
00889                         );
00890                 } elseif ( $this->getContext()->getOutput()->isPrintable() ) {
00891                         # Discourage indexing of printable versions, but encourage following
00892                         return array(
00893                                 'index'  => 'noindex',
00894                                 'follow' => 'follow'
00895                         );
00896                 } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
00897                         # For ?curid=x urls, disallow indexing
00898                         return array(
00899                                 'index'  => 'noindex',
00900                                 'follow' => 'follow'
00901                         );
00902                 }
00903 
00904                 # Otherwise, construct the policy based on the various config variables.
00905                 $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
00906 
00907                 if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
00908                         # Honour customised robot policies for this namespace
00909                         $policy = array_merge(
00910                                 $policy,
00911                                 self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
00912                         );
00913                 }
00914                 if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) {
00915                         # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
00916                         # a final sanity check that we have really got the parser output.
00917                         $policy = array_merge(
00918                                 $policy,
00919                                 array( 'index' => $pOutput->getIndexPolicy() )
00920                         );
00921                 }
00922 
00923                 if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
00924                         # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
00925                         $policy = array_merge(
00926                                 $policy,
00927                                 self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
00928                         );
00929                 }
00930 
00931                 return $policy;
00932         }
00933 
00941         public static function formatRobotPolicy( $policy ) {
00942                 if ( is_array( $policy ) ) {
00943                         return $policy;
00944                 } elseif ( !$policy ) {
00945                         return array();
00946                 }
00947 
00948                 $policy = explode( ',', $policy );
00949                 $policy = array_map( 'trim', $policy );
00950 
00951                 $arr = array();
00952                 foreach ( $policy as $var ) {
00953                         if ( in_array( $var, array( 'index', 'noindex' ) ) ) {
00954                                 $arr['index'] = $var;
00955                         } elseif ( in_array( $var, array( 'follow', 'nofollow' ) ) ) {
00956                                 $arr['follow'] = $var;
00957                         }
00958                 }
00959 
00960                 return $arr;
00961         }
00962 
00970         public function showRedirectedFromHeader() {
00971                 global $wgRedirectSources;
00972                 $outputPage = $this->getContext()->getOutput();
00973 
00974                 $rdfrom = $this->getContext()->getRequest()->getVal( 'rdfrom' );
00975 
00976                 if ( isset( $this->mRedirectedFrom ) ) {
00977                         // This is an internally redirected page view.
00978                         // We'll need a backlink to the source page for navigation.
00979                         if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) {
00980                                 $redir = Linker::linkKnown(
00981                                         $this->mRedirectedFrom,
00982                                         null,
00983                                         array(),
00984                                         array( 'redirect' => 'no' )
00985                                 );
00986 
00987                                 $outputPage->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) );
00988 
00989                                 // Set the fragment if one was specified in the redirect
00990                                 if ( strval( $this->getTitle()->getFragment() ) != '' ) {
00991                                         $fragment = Xml::escapeJsString( $this->getTitle()->getFragmentForURL() );
00992                                         $outputPage->addInlineScript( "redirectToFragment(\"$fragment\");" );
00993                                 }
00994 
00995                                 // Add a <link rel="canonical"> tag
00996                                 $outputPage->setCanonicalUrl( $this->getTitle()->getLocalURL() );
00997 
00998                                 // Tell the output object that the user arrived at this article through a redirect
00999                                 $outputPage->setRedirectedFrom( $this->mRedirectedFrom );
01000 
01001                                 return true;
01002                         }
01003                 } elseif ( $rdfrom ) {
01004                         // This is an externally redirected view, from some other wiki.
01005                         // If it was reported from a trusted site, supply a backlink.
01006                         if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
01007                                 $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
01008                                 $outputPage->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) );
01009 
01010                                 return true;
01011                         }
01012                 }
01013 
01014                 return false;
01015         }
01016 
01021         public function showNamespaceHeader() {
01022                 if ( $this->getTitle()->isTalkPage() ) {
01023                         if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
01024                                 $this->getContext()->getOutput()->wrapWikiMsg( "<div class=\"mw-talkpageheader\">\n$1\n</div>", array( 'talkpageheader' ) );
01025                         }
01026                 }
01027         }
01028 
01032         public function showViewFooter() {
01033                 # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
01034                 if ( $this->getTitle()->getNamespace() == NS_USER_TALK && IP::isValid( $this->getTitle()->getText() ) ) {
01035                         $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
01036                 }
01037 
01038                 # If we have been passed an &rcid= parameter, we want to give the user a
01039                 # chance to mark this new article as patrolled.
01040                 $this->showPatrolFooter();
01041 
01042                 wfRunHooks( 'ArticleViewFooter', array( $this ) );
01043 
01044         }
01045 
01053         public function showPatrolFooter() {
01054                 $request = $this->getContext()->getRequest();
01055                 $outputPage = $this->getContext()->getOutput();
01056                 $user = $this->getContext()->getUser();
01057                 $rcid = $request->getVal( 'rcid' );
01058 
01059                 if ( !$rcid || !$this->getTitle()->quickUserCan( 'patrol', $user ) ) {
01060                         return;
01061                 }
01062 
01063                 $token = $user->getEditToken( $rcid );
01064 
01065                 $outputPage->preventClickjacking();
01066                 $outputPage->addModules( 'mediawiki.page.patrol.ajax' );
01067 
01068                 $link = Linker::linkKnown(
01069                         $this->getTitle(),
01070                         wfMessage( 'markaspatrolledtext' )->escaped(),
01071                         array(),
01072                         array(
01073                                 'action' => 'markpatrolled',
01074                                 'rcid' => $rcid,
01075                                 'token' => $token,
01076                         )
01077                 );
01078 
01079                 $outputPage->addHTML(
01080                         "<div class='patrollink'>" .
01081                                 wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
01082                         '</div>'
01083                 );
01084         }
01085 
01090         public function showMissingArticle() {
01091                 global $wgSend404Code;
01092                 $outputPage = $this->getContext()->getOutput();
01093                 $validUserPage = false;
01094 
01095                 # Show info in user (talk) namespace. Does the user exist? Is he blocked?
01096                 if ( $this->getTitle()->getNamespace() == NS_USER || $this->getTitle()->getNamespace() == NS_USER_TALK ) {
01097                         $parts = explode( '/', $this->getTitle()->getText() );
01098                         $rootPart = $parts[0];
01099                         $user = User::newFromName( $rootPart, false /* allow IP users*/ );
01100                         $ip = User::isIP( $rootPart );
01101 
01102                         if ( !($user && $user->isLoggedIn()) && !$ip ) { # User does not exist
01103                                 $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
01104                                         array( 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ) );
01105                         } elseif ( $user->isBlocked() ) { # Show log extract if the user is currently blocked
01106                                 LogEventsList::showLogExtract(
01107                                         $outputPage,
01108                                         'block',
01109                                         $user->getUserPage(),
01110                                         '',
01111                                         array(
01112                                                 'lim' => 1,
01113                                                 'showIfEmpty' => false,
01114                                                 'msgKey' => array(
01115                                                         'blocked-notice-logextract',
01116                                                         $user->getName() # Support GENDER in notice
01117                                                 )
01118                                         )
01119                                 );
01120                                 $validUserPage = true;
01121                         } else {
01122                                 $validUserPage = true;
01123                         }
01124                 }
01125 
01126                 wfRunHooks( 'ShowMissingArticle', array( $this ) );
01127 
01128                 # Show delete and move logs
01129                 LogEventsList::showLogExtract( $outputPage, array( 'delete', 'move' ), $this->getTitle(), '',
01130                         array( 'lim' => 10,
01131                                 'conds' => array( "log_action != 'revision'" ),
01132                                 'showIfEmpty' => false,
01133                                 'msgKey' => array( 'moveddeleted-notice' ) )
01134                 );
01135 
01136                 if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
01137                         // If there's no backing content, send a 404 Not Found
01138                         // for better machine handling of broken links.
01139                         $this->getContext()->getRequest()->response()->header( "HTTP/1.1 404 Not Found" );
01140                 }
01141 
01142                 $hookResult = wfRunHooks( 'BeforeDisplayNoArticleText', array( $this ) );
01143 
01144                 if ( ! $hookResult ) {
01145                         return;
01146                 }
01147 
01148                 # Show error message
01149                 $oldid = $this->getOldID();
01150                 if ( $oldid ) {
01151                         $text = wfMessage( 'missing-revision', $oldid )->plain();
01152                 } elseif ( $this->getTitle()->getNamespace() === NS_MEDIAWIKI ) {
01153                         // Use the default message text
01154                         $text = $this->getTitle()->getDefaultMessageText();
01155                 } elseif ( $this->getTitle()->quickUserCan( 'create', $this->getContext()->getUser() )
01156                         && $this->getTitle()->quickUserCan( 'edit', $this->getContext()->getUser() )
01157                 ) {
01158                         $text = wfMessage( 'noarticletext' )->plain();
01159                 } else {
01160                         $text = wfMessage( 'noarticletext-nopermission' )->plain();
01161                 }
01162                 $text = "<div class='noarticletext'>\n$text\n</div>";
01163 
01164                 $outputPage->addWikiText( $text );
01165         }
01166 
01173         public function showDeletedRevisionHeader() {
01174                 if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
01175                         // Not deleted
01176                         return true;
01177                 }
01178 
01179                 $outputPage = $this->getContext()->getOutput();
01180                 $user = $this->getContext()->getUser();
01181                 // If the user is not allowed to see it...
01182                 if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
01183                         $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
01184                                 'rev-deleted-text-permission' );
01185 
01186                         return false;
01187                 // If the user needs to confirm that they want to see it...
01188                 } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
01189                         # Give explanation and add a link to view the revision...
01190                         $oldid = intval( $this->getOldID() );
01191                         $link = $this->getTitle()->getFullUrl( "oldid={$oldid}&unhide=1" );
01192                         $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
01193                                 'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
01194                         $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
01195                                 array( $msg, $link ) );
01196 
01197                         return false;
01198                 // We are allowed to see...
01199                 } else {
01200                         $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
01201                                 'rev-suppressed-text-view' : 'rev-deleted-text-view';
01202                         $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
01203 
01204                         return true;
01205                 }
01206         }
01207 
01216         public function setOldSubtitle( $oldid = 0 ) {
01217                 if ( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) {
01218                         return;
01219                 }
01220 
01221                 $unhide = $this->getContext()->getRequest()->getInt( 'unhide' ) == 1;
01222 
01223                 # Cascade unhide param in links for easy deletion browsing
01224                 $extraParams = array();
01225                 if ( $unhide ) {
01226                         $extraParams['unhide'] = 1;
01227                 }
01228 
01229                 if ( $this->mRevision && $this->mRevision->getId() === $oldid ) {
01230                         $revision = $this->mRevision;
01231                 } else {
01232                         $revision = Revision::newFromId( $oldid );
01233                 }
01234 
01235                 $timestamp = $revision->getTimestamp();
01236 
01237                 $current = ( $oldid == $this->mPage->getLatest() );
01238                 $language = $this->getContext()->getLanguage();
01239                 $user = $this->getContext()->getUser();
01240 
01241                 $td = $language->userTimeAndDate( $timestamp, $user );
01242                 $tddate = $language->userDate( $timestamp, $user );
01243                 $tdtime = $language->userTime( $timestamp, $user );
01244 
01245                 # Show user links if allowed to see them. If hidden, then show them only if requested...
01246                 $userlinks = Linker::revUserTools( $revision, !$unhide );
01247 
01248                 $infomsg = $current && !wfMessage( 'revision-info-current' )->isDisabled()
01249                         ? 'revision-info-current'
01250                         : 'revision-info';
01251 
01252                 $outputPage = $this->getContext()->getOutput();
01253                 $outputPage->addSubtitle( "<div id=\"mw-{$infomsg}\">" . wfMessage( $infomsg,
01254                         $td )->rawParams( $userlinks )->params( $revision->getID(), $tddate,
01255                         $tdtime, $revision->getUser() )->parse() . "</div>" );
01256 
01257                 $lnk = $current
01258                         ? wfMessage( 'currentrevisionlink' )->escaped()
01259                         : Linker::linkKnown(
01260                                 $this->getTitle(),
01261                                 wfMessage( 'currentrevisionlink' )->escaped(),
01262                                 array(),
01263                                 $extraParams
01264                         );
01265                 $curdiff = $current
01266                         ? wfMessage( 'diff' )->escaped()
01267                         : Linker::linkKnown(
01268                                 $this->getTitle(),
01269                                 wfMessage( 'diff' )->escaped(),
01270                                 array(),
01271                                 array(
01272                                         'diff' => 'cur',
01273                                         'oldid' => $oldid
01274                                 ) + $extraParams
01275                         );
01276                 $prev = $this->getTitle()->getPreviousRevisionID( $oldid );
01277                 $prevlink = $prev
01278                         ? Linker::linkKnown(
01279                                 $this->getTitle(),
01280                                 wfMessage( 'previousrevision' )->escaped(),
01281                                 array(),
01282                                 array(
01283                                         'direction' => 'prev',
01284                                         'oldid' => $oldid
01285                                 ) + $extraParams
01286                         )
01287                         : wfMessage( 'previousrevision' )->escaped();
01288                 $prevdiff = $prev
01289                         ? Linker::linkKnown(
01290                                 $this->getTitle(),
01291                                 wfMessage( 'diff' )->escaped(),
01292                                 array(),
01293                                 array(
01294                                         'diff' => 'prev',
01295                                         'oldid' => $oldid
01296                                 ) + $extraParams
01297                         )
01298                         : wfMessage( 'diff' )->escaped();
01299                 $nextlink = $current
01300                         ? wfMessage( 'nextrevision' )->escaped()
01301                         : Linker::linkKnown(
01302                                 $this->getTitle(),
01303                                 wfMessage( 'nextrevision' )->escaped(),
01304                                 array(),
01305                                 array(
01306                                         'direction' => 'next',
01307                                         'oldid' => $oldid
01308                                 ) + $extraParams
01309                         );
01310                 $nextdiff = $current
01311                         ? wfMessage( 'diff' )->escaped()
01312                         : Linker::linkKnown(
01313                                 $this->getTitle(),
01314                                 wfMessage( 'diff' )->escaped(),
01315                                 array(),
01316                                 array(
01317                                         'diff' => 'next',
01318                                         'oldid' => $oldid
01319                                 ) + $extraParams
01320                         );
01321 
01322                 $cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() );
01323                 if ( $cdel !== '' ) {
01324                         $cdel .= ' ';
01325                 }
01326 
01327                 $outputPage->addSubtitle( "<div id=\"mw-revision-nav\">" . $cdel .
01328                         wfMessage( 'revision-nav' )->rawParams(
01329                                 $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff
01330                         )->escaped() . "</div>" );
01331         }
01332 
01341         public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
01342                 global $wgStylePath;
01343 
01344                 if ( !is_array( $target ) ) {
01345                         $target = array( $target );
01346                 }
01347 
01348                 $lang = $this->getTitle()->getPageLanguage();
01349                 $imageDir = $lang->getDir();
01350 
01351                 if ( $appendSubtitle ) {
01352                         $out = $this->getContext()->getOutput();
01353                         $out->addSubtitle( wfMessage( 'redirectpagesub' )->escaped() );
01354                 }
01355 
01356                 // the loop prepends the arrow image before the link, so the first case needs to be outside
01357 
01361                 $title = array_shift( $target );
01362 
01363                 if ( $forceKnown ) {
01364                         $link = Linker::linkKnown( $title, htmlspecialchars( $title->getFullText() ) );
01365                 } else {
01366                         $link = Linker::link( $title, htmlspecialchars( $title->getFullText() ) );
01367                 }
01368 
01369                 $nextRedirect = $wgStylePath . '/common/images/nextredirect' . $imageDir . '.png';
01370                 $alt = $lang->isRTL() ? '←' : '→';
01371                 // Automatically append redirect=no to each link, since most of them are redirect pages themselves.
01372                 foreach ( $target as $rt ) {
01373                         $link .= Html::element( 'img', array( 'src' => $nextRedirect, 'alt' => $alt ) );
01374                         if ( $forceKnown ) {
01375                                 $link .= Linker::linkKnown( $rt, htmlspecialchars( $rt->getFullText(), array(), array( 'redirect' => 'no' ) ) );
01376                         } else {
01377                                 $link .= Linker::link( $rt, htmlspecialchars( $rt->getFullText() ), array(), array( 'redirect' => 'no' ) );
01378                         }
01379                 }
01380 
01381                 $imageUrl = $wgStylePath . '/common/images/redirect' . $imageDir . '.png';
01382                 return '<div class="redirectMsg">' .
01383                         Html::element( 'img', array( 'src' => $imageUrl, 'alt' => '#REDIRECT' ) ) .
01384                         '<span class="redirectText">' . $link . '</span></div>';
01385         }
01386 
01390         public function render() {
01391                 $this->getContext()->getOutput()->setArticleBodyOnly( true );
01392                 $this->view();
01393         }
01394 
01398         public function protect() {
01399                 $form = new ProtectionForm( $this );
01400                 $form->execute();
01401         }
01402 
01406         public function unprotect() {
01407                 $this->protect();
01408         }
01409 
01413         public function delete() {
01414                 # This code desperately needs to be totally rewritten
01415 
01416                 $title = $this->getTitle();
01417                 $user = $this->getContext()->getUser();
01418 
01419                 # Check permissions
01420                 $permission_errors = $title->getUserPermissionsErrors( 'delete', $user );
01421                 if ( count( $permission_errors ) ) {
01422                         throw new PermissionsError( 'delete', $permission_errors );
01423                 }
01424 
01425                 # Read-only check...
01426                 if ( wfReadOnly() ) {
01427                         throw new ReadOnlyError;
01428                 }
01429 
01430                 # Better double-check that it hasn't been deleted yet!
01431                 $this->mPage->loadPageData( 'fromdbmaster' );
01432                 if ( !$this->mPage->exists() ) {
01433                         $deleteLogPage = new LogPage( 'delete' );
01434                         $outputPage = $this->getContext()->getOutput();
01435                         $outputPage->setPageTitle( wfMessage( 'cannotdelete-title', $title->getPrefixedText() ) );
01436                         $outputPage->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>",
01437                                         array( 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) )
01438                                 );
01439                         $outputPage->addHTML(
01440                                 Xml::element( 'h2', null, $deleteLogPage->getName()->text() )
01441                         );
01442                         LogEventsList::showLogExtract(
01443                                 $outputPage,
01444                                 'delete',
01445                                 $title
01446                         );
01447 
01448                         return;
01449                 }
01450 
01451                 $request = $this->getContext()->getRequest();
01452                 $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' );
01453                 $deleteReason = $request->getText( 'wpReason' );
01454 
01455                 if ( $deleteReasonList == 'other' ) {
01456                         $reason = $deleteReason;
01457                 } elseif ( $deleteReason != '' ) {
01458                         // Entry from drop down menu + additional comment
01459                         $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text();
01460                         $reason = $deleteReasonList . $colonseparator . $deleteReason;
01461                 } else {
01462                         $reason = $deleteReasonList;
01463                 }
01464 
01465                 if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ),
01466                         array( 'delete', $this->getTitle()->getPrefixedText() ) ) )
01467                 {
01468                         # Flag to hide all contents of the archived revisions
01469                         $suppress = $request->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' );
01470 
01471                         $this->doDelete( $reason, $suppress );
01472 
01473                         if ( $user->isLoggedIn() && $request->getCheck( 'wpWatch' ) != $user->isWatched( $title ) ) {
01474                                 if ( $request->getCheck( 'wpWatch' ) ) {
01475                                         WatchAction::doWatch( $title, $user );
01476                                 } else {
01477                                         WatchAction::doUnwatch( $title, $user );
01478                                 }
01479                         }
01480 
01481                         return;
01482                 }
01483 
01484                 // Generate deletion reason
01485                 $hasHistory = false;
01486                 if ( !$reason ) {
01487                         try {
01488                                 $reason = $this->generateReason( $hasHistory );
01489                         } catch ( MWException $e ) {
01490                                 # if a page is horribly broken, we still want to be able to delete it. so be lenient about errors here.
01491                                 wfDebug( "Error while building auto delete summary: $e" );
01492                                 $reason = '';
01493                         }
01494                 }
01495 
01496                 // If the page has a history, insert a warning
01497                 if ( $hasHistory ) {
01498                         $revisions = $this->mTitle->estimateRevisionCount();
01499                         // @todo FIXME: i18n issue/patchwork message
01500                         $this->getContext()->getOutput()->addHTML( '<strong class="mw-delete-warning-revisions">' .
01501                                 wfMessage( 'historywarning' )->numParams( $revisions )->parse() .
01502                                 wfMessage( 'word-separator' )->plain() . Linker::linkKnown( $title,
01503                                         wfMessage( 'history' )->escaped(),
01504                                         array( 'rel' => 'archives' ),
01505                                         array( 'action' => 'history' ) ) .
01506                                 '</strong>'
01507                         );
01508 
01509                         if ( $this->mTitle->isBigDeletion() ) {
01510                                 global $wgDeleteRevisionsLimit;
01511                                 $this->getContext()->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
01512                                         array( 'delete-warning-toobig', $this->getContext()->getLanguage()->formatNum( $wgDeleteRevisionsLimit ) ) );
01513                         }
01514                 }
01515 
01516                 $this->confirmDelete( $reason );
01517         }
01518 
01524         public function confirmDelete( $reason ) {
01525                 wfDebug( "Article::confirmDelete\n" );
01526 
01527                 $outputPage = $this->getContext()->getOutput();
01528                 $outputPage->setPageTitle( wfMessage( 'delete-confirm', $this->getTitle()->getPrefixedText() ) );
01529                 $outputPage->addBacklinkSubtitle( $this->getTitle() );
01530                 $outputPage->setRobotPolicy( 'noindex,nofollow' );
01531                 $outputPage->addWikiMsg( 'confirmdeletetext' );
01532 
01533                 wfRunHooks( 'ArticleConfirmDelete', array( $this, $outputPage, &$reason ) );
01534 
01535                 $user = $this->getContext()->getUser();
01536 
01537                 if ( $user->isAllowed( 'suppressrevision' ) ) {
01538                         $suppress = "<tr id=\"wpDeleteSuppressRow\">
01539                                         <td></td>
01540                                         <td class='mw-input'><strong>" .
01541                                                 Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(),
01542                                                         'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '4' ) ) .
01543                                         "</strong></td>
01544                                 </tr>";
01545                 } else {
01546                         $suppress = '';
01547                 }
01548                 $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $this->getTitle() );
01549 
01550                 $form = Xml::openElement( 'form', array( 'method' => 'post',
01551                         'action' => $this->getTitle()->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) .
01552                         Xml::openElement( 'fieldset', array( 'id' => 'mw-delete-table' ) ) .
01553                         Xml::tags( 'legend', null, wfMessage( 'delete-legend' )->escaped() ) .
01554                         Xml::openElement( 'table', array( 'id' => 'mw-deleteconfirm-table' ) ) .
01555                         "<tr id=\"wpDeleteReasonListRow\">
01556                                 <td class='mw-label'>" .
01557                                         Xml::label( wfMessage( 'deletecomment' )->text(), 'wpDeleteReasonList' ) .
01558                                 "</td>
01559                                 <td class='mw-input'>" .
01560                                         Xml::listDropDown( 'wpDeleteReasonList',
01561                                                 wfMessage( 'deletereason-dropdown' )->inContentLanguage()->text(),
01562                                                 wfMessage( 'deletereasonotherlist' )->inContentLanguage()->text(), '', 'wpReasonDropDown', 1 ) .
01563                                 "</td>
01564                         </tr>
01565                         <tr id=\"wpDeleteReasonRow\">
01566                                 <td class='mw-label'>" .
01567                                         Xml::label( wfMessage( 'deleteotherreason' )->text(), 'wpReason' ) .
01568                                 "</td>
01569                                 <td class='mw-input'>" .
01570                                 Html::input( 'wpReason', $reason, 'text', array(
01571                                         'size' => '60',
01572                                         'maxlength' => '255',
01573                                         'tabindex' => '2',
01574                                         'id' => 'wpReason',
01575                                         'autofocus'
01576                                 ) ) .
01577                                 "</td>
01578                         </tr>";
01579 
01580                 # Disallow watching if user is not logged in
01581                 if ( $user->isLoggedIn() ) {
01582                         $form .= "
01583                         <tr>
01584                                 <td></td>
01585                                 <td class='mw-input'>" .
01586                                         Xml::checkLabel( wfMessage( 'watchthis' )->text(),
01587                                                 'wpWatch', 'wpWatch', $checkWatch, array( 'tabindex' => '3' ) ) .
01588                                 "</td>
01589                         </tr>";
01590                 }
01591 
01592                 $form .= "
01593                         $suppress
01594                         <tr>
01595                                 <td></td>
01596                                 <td class='mw-submit'>" .
01597                                         Xml::submitButton( wfMessage( 'deletepage' )->text(),
01598                                                 array( 'name' => 'wpConfirmB', 'id' => 'wpConfirmB', 'tabindex' => '5' ) ) .
01599                                 "</td>
01600                         </tr>" .
01601                         Xml::closeElement( 'table' ) .
01602                         Xml::closeElement( 'fieldset' ) .
01603                         Html::hidden( 'wpEditToken', $user->getEditToken( array( 'delete', $this->getTitle()->getPrefixedText() ) ) ) .
01604                         Xml::closeElement( 'form' );
01605 
01606                         if ( $user->isAllowed( 'editinterface' ) ) {
01607                                 $title = Title::makeTitle( NS_MEDIAWIKI, 'Deletereason-dropdown' );
01608                                 $link = Linker::link(
01609                                         $title,
01610                                         wfMessage( 'delete-edit-reasonlist' )->escaped(),
01611                                         array(),
01612                                         array( 'action' => 'edit' )
01613                                 );
01614                                 $form .= '<p class="mw-delete-editreasons">' . $link . '</p>';
01615                         }
01616 
01617                 $outputPage->addHTML( $form );
01618 
01619                 $deleteLogPage = new LogPage( 'delete' );
01620                 $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
01621                 LogEventsList::showLogExtract( $outputPage, 'delete',
01622                         $this->getTitle()
01623                 );
01624         }
01625 
01631         public function doDelete( $reason, $suppress = false ) {
01632                 $error = '';
01633                 $outputPage = $this->getContext()->getOutput();
01634                 $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error );
01635                 if ( $status->isGood() ) {
01636                         $deleted = $this->getTitle()->getPrefixedText();
01637 
01638                         $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) );
01639                         $outputPage->setRobotPolicy( 'noindex,nofollow' );
01640 
01641                         $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]';
01642 
01643                         $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
01644                         $outputPage->returnToMain( false );
01645                 } else {
01646                         $outputPage->setPageTitle( wfMessage( 'cannotdelete-title', $this->getTitle()->getPrefixedText() ) );
01647                         if ( $error == '' ) {
01648                                 $outputPage->addWikiText(
01649                                         "<div class=\"error mw-error-cannotdelete\">\n" . $status->getWikiText() . "\n</div>"
01650                                 );
01651                                 $deleteLogPage = new LogPage( 'delete' );
01652                                 $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) );
01653 
01654                                 LogEventsList::showLogExtract(
01655                                         $outputPage,
01656                                         'delete',
01657                                         $this->getTitle()
01658                                 );
01659                         } else {
01660                                 $outputPage->addHTML( $error );
01661                         }
01662                 }
01663         }
01664 
01665         /* Caching functions */
01666 
01674         protected function tryFileCache() {
01675                 static $called = false;
01676 
01677                 if ( $called ) {
01678                         wfDebug( "Article::tryFileCache(): called twice!?\n" );
01679                         return false;
01680                 }
01681 
01682                 $called = true;
01683                 if ( $this->isFileCacheable() ) {
01684                         $cache = HTMLFileCache::newFromTitle( $this->getTitle(), 'view' );
01685                         if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) {
01686                                 wfDebug( "Article::tryFileCache(): about to load file\n" );
01687                                 $cache->loadFromFileCache( $this->getContext() );
01688                                 return true;
01689                         } else {
01690                                 wfDebug( "Article::tryFileCache(): starting buffer\n" );
01691                                 ob_start( array( &$cache, 'saveToFileCache' ) );
01692                         }
01693                 } else {
01694                         wfDebug( "Article::tryFileCache(): not cacheable\n" );
01695                 }
01696 
01697                 return false;
01698         }
01699 
01704         public function isFileCacheable() {
01705                 $cacheable = false;
01706 
01707                 if ( HTMLFileCache::useFileCache( $this->getContext() ) ) {
01708                         $cacheable = $this->mPage->getID()
01709                                 && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
01710                         // Extension may have reason to disable file caching on some pages.
01711                         if ( $cacheable ) {
01712                                 $cacheable = wfRunHooks( 'IsFileCacheable', array( &$this ) );
01713                         }
01714                 }
01715 
01716                 return $cacheable;
01717         }
01718 
01732         public function getParserOutput( $oldid = null, User $user = null ) {
01733                 //XXX: bypasses mParserOptions and thus setParserOptions()
01734 
01735                 if ( $user === null ) {
01736                         $parserOptions = $this->getParserOptions();
01737                 } else {
01738                         $parserOptions = $this->mPage->makeParserOptions( $user );
01739                 }
01740 
01741                 return $this->mPage->getParserOutput( $parserOptions, $oldid );
01742         }
01743 
01750         public function setParserOptions( ParserOptions $options ) {
01751                 if ( $this->mParserOptions ) {
01752                         throw new MWException( "can't change parser options after they have already been set" );
01753                 }
01754 
01755                 // clone, so if $options is modified later, it doesn't confuse the parser cache.
01756                 $this->mParserOptions = clone $options;
01757         }
01758 
01763         public function getParserOptions() {
01764                 if ( !$this->mParserOptions ) {
01765                         $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() );
01766                 }
01767                 // Clone to allow modifications of the return value without affecting cache
01768                 return clone $this->mParserOptions;
01769         }
01770 
01777         public function setContext( $context ) {
01778                 $this->mContext = $context;
01779         }
01780 
01787         public function getContext() {
01788                 if ( $this->mContext instanceof IContextSource ) {
01789                         return $this->mContext;
01790                 } else {
01791                         wfDebug( __METHOD__ . " called and \$mContext is null. Return RequestContext::getMain(); for sanity\n" );
01792                         return RequestContext::getMain();
01793                 }
01794         }
01795 
01800         public function info() {
01801                 wfDeprecated( __METHOD__, '1.19' );
01802                 Action::factory( 'info', $this )->show();
01803         }
01804 
01809         public function markpatrolled() {
01810                 wfDeprecated( __METHOD__, '1.18' );
01811                 Action::factory( 'markpatrolled', $this )->show();
01812         }
01813 
01819         public function purge() {
01820                 return Action::factory( 'purge', $this )->show();
01821         }
01822 
01827         public function revert() {
01828                 wfDeprecated( __METHOD__, '1.19' );
01829                 Action::factory( 'revert', $this )->show();
01830         }
01831 
01836         public function rollback() {
01837                 wfDeprecated( __METHOD__, '1.19' );
01838                 Action::factory( 'rollback', $this )->show();
01839         }
01840 
01846         public function watch() {
01847                 wfDeprecated( __METHOD__, '1.18' );
01848                 Action::factory( 'watch', $this )->show();
01849         }
01850 
01859         public function doWatch() {
01860                 wfDeprecated( __METHOD__, '1.18' );
01861                 return WatchAction::doWatch( $this->getTitle(), $this->getContext()->getUser() );
01862         }
01863 
01869         public function unwatch() {
01870                 wfDeprecated( __METHOD__, '1.18' );
01871                 Action::factory( 'unwatch', $this )->show();
01872         }
01873 
01879         public function doUnwatch() {
01880                 wfDeprecated( __METHOD__, '1.18' );
01881                 return WatchAction::doUnwatch( $this->getTitle(), $this->getContext()->getUser() );
01882         }
01883 
01893         public function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) {
01894                 wfDeprecated( __METHOD__, '1.18' );
01895                 if ( $noRedir ) {
01896                         $query = 'redirect=no';
01897                         if ( $extraQuery ) {
01898                                 $query .= "&$extraQuery";
01899                         }
01900                 } else {
01901                         $query = $extraQuery;
01902                 }
01903 
01904                 $this->getContext()->getOutput()->redirect( $this->getTitle()->getFullURL( $query ) . $sectionAnchor );
01905         }
01906 
01913         public function __get( $fname ) {
01914                 if ( property_exists( $this->mPage, $fname ) ) {
01915                         #wfWarn( "Access to raw $fname field " . __CLASS__ );
01916                         return $this->mPage->$fname;
01917                 }
01918                 trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
01919         }
01920 
01928         public function __set( $fname, $fvalue ) {
01929                 if ( property_exists( $this->mPage, $fname ) ) {
01930                         #wfWarn( "Access to raw $fname field of " . __CLASS__ );
01931                         $this->mPage->$fname = $fvalue;
01932                 // Note: extensions may want to toss on new fields
01933                 } elseif ( !in_array( $fname, array( 'mContext', 'mPage' ) ) ) {
01934                         $this->mPage->$fname = $fvalue;
01935                 } else {
01936                         trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE );
01937                 }
01938         }
01939 
01948         public function __call( $fname, $args ) {
01949                 if ( is_callable( array( $this->mPage, $fname ) ) ) {
01950                         #wfWarn( "Call to " . __CLASS__ . "::$fname; please use WikiPage instead" );
01951                         return call_user_func_array( array( $this->mPage, $fname ), $args );
01952                 }
01953                 trigger_error( 'Inaccessible function via __call(): ' . $fname, E_USER_ERROR );
01954         }
01955 
01956         // ****** B/C functions to work-around PHP silliness with __call and references ****** //
01957 
01966         public function doUpdateRestrictions( array $limit, array $expiry, &$cascade, $reason, User $user ) {
01967                 return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
01968         }
01969 
01977         public function updateRestrictions( $limit = array(), $reason = '', &$cascade = 0, $expiry = array() ) {
01978                 return $this->mPage->doUpdateRestrictions(
01979                         $limit,
01980                         $expiry,
01981                         $cascade,
01982                         $reason,
01983                         $this->getContext()->getUser()
01984                 );
01985         }
01986 
01995         public function doDeleteArticle( $reason, $suppress = false, $id = 0, $commit = true, &$error = '' ) {
01996                 return $this->mPage->doDeleteArticle( $reason, $suppress, $id, $commit, $error );
01997         }
01998 
02008         public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
02009                 $user = is_null( $user ) ? $this->getContext()->getUser() : $user;
02010                 return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
02011         }
02012 
02021         public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
02022                 $guser = is_null( $guser ) ? $this->getContext()->getUser() : $guser;
02023                 return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
02024         }
02025 
02030         public function generateReason( &$hasHistory ) {
02031                 $title = $this->mPage->getTitle();
02032                 $handler = ContentHandler::getForTitle( $title );
02033                 return $handler->getAutoDeleteReason( $title, $hasHistory );
02034         }
02035 
02036         // ****** B/C functions for static methods ( __callStatic is PHP>=5.3 ) ****** //
02037 
02041         public static function selectFields() {
02042                 return WikiPage::selectFields();
02043         }
02044 
02048         public static function onArticleCreate( $title ) {
02049                 WikiPage::onArticleCreate( $title );
02050         }
02051 
02055         public static function onArticleDelete( $title ) {
02056                 WikiPage::onArticleDelete( $title );
02057         }
02058 
02062         public static function onArticleEdit( $title ) {
02063                 WikiPage::onArticleEdit( $title );
02064         }
02065 
02073         public static function getAutosummary( $oldtext, $newtext, $flags ) {
02074                 return WikiPage::getAutosummary( $oldtext, $newtext, $flags );
02075         }
02076         // ******
02077 }