MediaWiki
REL1_20
|
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 }