MediaWiki
REL1_19
|
00001 <?php 00020 class Article extends Page { 00028 protected $mContext; 00029 00033 protected $mPage; 00034 00038 public $mParserOptions; 00039 00040 var $mContent; // !< 00041 var $mContentLoaded = false; // !< 00042 var $mOldId; // !< 00043 00047 var $mRedirectedFrom = null; 00048 00052 var $mRedirectUrl = false; // !< 00053 var $mRevIdFetched = 0; // !< 00054 00058 var $mRevision = null; 00059 00063 var $mParserOutput; 00064 00072 public function __construct( Title $title, $oldId = null ) { 00073 $this->mOldId = $oldId; 00074 $this->mPage = $this->newPage( $title ); 00075 } 00076 00081 protected function newPage( Title $title ) { 00082 return new WikiPage( $title ); 00083 } 00084 00090 public static function newFromID( $id ) { 00091 $t = Title::newFromID( $id ); 00092 # @todo FIXME: Doesn't inherit right 00093 return $t == null ? null : new self( $t ); 00094 # return $t == null ? null : new static( $t ); // PHP 5.3 00095 } 00096 00104 public static function newFromTitle( $title, IContextSource $context ) { 00105 if ( NS_MEDIA == $title->getNamespace() ) { 00106 // FIXME: where should this go? 00107 $title = Title::makeTitle( NS_FILE, $title->getDBkey() ); 00108 } 00109 00110 $page = null; 00111 wfRunHooks( 'ArticleFromTitle', array( &$title, &$page ) ); 00112 if ( !$page ) { 00113 switch( $title->getNamespace() ) { 00114 case NS_FILE: 00115 $page = new ImagePage( $title ); 00116 break; 00117 case NS_CATEGORY: 00118 $page = new CategoryPage( $title ); 00119 break; 00120 default: 00121 $page = new Article( $title ); 00122 } 00123 } 00124 $page->setContext( $context ); 00125 00126 return $page; 00127 } 00128 00136 public static function newFromWikiPage( WikiPage $page, IContextSource $context ) { 00137 $article = self::newFromTitle( $page->getTitle(), $context ); 00138 $article->mPage = $page; // override to keep process cached vars 00139 return $article; 00140 } 00141 00147 public function setRedirectedFrom( Title $from ) { 00148 $this->mRedirectedFrom = $from; 00149 } 00150 00156 public function getTitle() { 00157 return $this->mPage->getTitle(); 00158 } 00159 00166 public function getPage() { 00167 return $this->mPage; 00168 } 00169 00173 public function clear() { 00174 $this->mContentLoaded = false; 00175 00176 $this->mRedirectedFrom = null; # Title object if set 00177 $this->mRevIdFetched = 0; 00178 $this->mRedirectUrl = false; 00179 00180 $this->mPage->clear(); 00181 } 00182 00193 public function getContent() { 00194 global $wgUser; 00195 00196 wfProfileIn( __METHOD__ ); 00197 00198 if ( $this->mPage->getID() === 0 ) { 00199 # If this is a MediaWiki:x message, then load the messages 00200 # and return the message value for x. 00201 if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) { 00202 $text = $this->getTitle()->getDefaultMessageText(); 00203 if ( $text === false ) { 00204 $text = ''; 00205 } 00206 } else { 00207 $text = wfMsgExt( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon', 'parsemag' ); 00208 } 00209 wfProfileOut( __METHOD__ ); 00210 00211 return $text; 00212 } else { 00213 $this->fetchContent(); 00214 wfProfileOut( __METHOD__ ); 00215 00216 return $this->mContent; 00217 } 00218 } 00219 00224 public function getOldID() { 00225 if ( is_null( $this->mOldId ) ) { 00226 $this->mOldId = $this->getOldIDFromRequest(); 00227 } 00228 00229 return $this->mOldId; 00230 } 00231 00237 public function getOldIDFromRequest() { 00238 global $wgRequest; 00239 00240 $this->mRedirectUrl = false; 00241 00242 $oldid = $wgRequest->getIntOrNull( 'oldid' ); 00243 00244 if ( $oldid === null ) { 00245 return 0; 00246 } 00247 00248 if ( $oldid !== 0 ) { 00249 # Load the given revision and check whether the page is another one. 00250 # In that case, update this instance to reflect the change. 00251 $this->mRevision = Revision::newFromId( $oldid ); 00252 if ( $this->mRevision !== null ) { 00253 // Revision title doesn't match the page title given? 00254 if ( $this->mPage->getID() != $this->mRevision->getPage() ) { 00255 $function = array( get_class( $this->mPage ), 'newFromID' ); 00256 $this->mPage = call_user_func( $function, $this->mRevision->getPage() ); 00257 } 00258 } 00259 } 00260 00261 if ( $wgRequest->getVal( 'direction' ) == 'next' ) { 00262 $nextid = $this->getTitle()->getNextRevisionID( $oldid ); 00263 if ( $nextid ) { 00264 $oldid = $nextid; 00265 $this->mRevision = null; 00266 } else { 00267 $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' ); 00268 } 00269 } elseif ( $wgRequest->getVal( 'direction' ) == 'prev' ) { 00270 $previd = $this->getTitle()->getPreviousRevisionID( $oldid ); 00271 if ( $previd ) { 00272 $oldid = $previd; 00273 $this->mRevision = null; 00274 } 00275 } 00276 00277 return $oldid; 00278 } 00279 00285 function loadContent() { 00286 wfDeprecated( __METHOD__, '1.19' ); 00287 $this->fetchContent(); 00288 } 00289 00296 function fetchContent() { 00297 if ( $this->mContentLoaded ) { 00298 return $this->mContent; 00299 } 00300 00301 wfProfileIn( __METHOD__ ); 00302 00303 $this->mContentLoaded = true; 00304 00305 $oldid = $this->getOldID(); 00306 00307 # Pre-fill content with error message so that if something 00308 # fails we'll have something telling us what we intended. 00309 $t = $this->getTitle()->getPrefixedText(); 00310 $d = $oldid ? wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid ) : ''; 00311 $this->mContent = wfMsgNoTrans( 'missing-article', $t, $d ) ; 00312 00313 if ( $oldid ) { 00314 # $this->mRevision might already be fetched by getOldIDFromRequest() 00315 if ( !$this->mRevision ) { 00316 $this->mRevision = Revision::newFromId( $oldid ); 00317 if ( !$this->mRevision ) { 00318 wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" ); 00319 wfProfileOut( __METHOD__ ); 00320 return false; 00321 } 00322 } 00323 } else { 00324 if ( !$this->mPage->getLatest() ) { 00325 wfDebug( __METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() . "\n" ); 00326 wfProfileOut( __METHOD__ ); 00327 return false; 00328 } 00329 00330 $this->mRevision = $this->mPage->getRevision(); 00331 if ( !$this->mRevision ) { 00332 wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" ); 00333 wfProfileOut( __METHOD__ ); 00334 return false; 00335 } 00336 } 00337 00338 // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks. 00339 // We should instead work with the Revision object when we need it... 00340 $this->mContent = $this->mRevision->getText( Revision::FOR_THIS_USER ); // Loads if user is allowed 00341 $this->mRevIdFetched = $this->mRevision->getId(); 00342 00343 wfRunHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) ); 00344 00345 wfProfileOut( __METHOD__ ); 00346 00347 return $this->mContent; 00348 } 00349 00354 public function forUpdate() { 00355 wfDeprecated( __METHOD__, '1.18' ); 00356 } 00357 00363 public function isCurrent() { 00364 # If no oldid, this is the current version. 00365 if ( $this->getOldID() == 0 ) { 00366 return true; 00367 } 00368 00369 return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent(); 00370 } 00371 00379 public function getRevisionFetched() { 00380 $this->fetchContent(); 00381 00382 return $this->mRevision; 00383 } 00384 00390 public function getRevIdFetched() { 00391 if ( $this->mRevIdFetched ) { 00392 return $this->mRevIdFetched; 00393 } else { 00394 return $this->mPage->getLatest(); 00395 } 00396 } 00397 00402 public function view() { 00403 global $wgUser, $wgOut, $wgRequest, $wgParser; 00404 global $wgUseFileCache, $wgUseETag, $wgDebugToolbar; 00405 00406 wfProfileIn( __METHOD__ ); 00407 00408 # Get variables from query string 00409 # As side effect this will load the revision and update the title 00410 # in a revision ID is passed in the request, so this should remain 00411 # the first call of this method even if $oldid is used way below. 00412 $oldid = $this->getOldID(); 00413 00414 # Another whitelist check in case getOldID() is altering the title 00415 $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $wgUser ); 00416 if ( count( $permErrors ) ) { 00417 wfDebug( __METHOD__ . ": denied on secondary read check\n" ); 00418 wfProfileOut( __METHOD__ ); 00419 throw new PermissionsError( 'read', $permErrors ); 00420 } 00421 00422 # getOldID() may as well want us to redirect somewhere else 00423 if ( $this->mRedirectUrl ) { 00424 $wgOut->redirect( $this->mRedirectUrl ); 00425 wfDebug( __METHOD__ . ": redirecting due to oldid\n" ); 00426 wfProfileOut( __METHOD__ ); 00427 00428 return; 00429 } 00430 00431 # If we got diff in the query, we want to see a diff page instead of the article. 00432 if ( $wgRequest->getCheck( 'diff' ) ) { 00433 wfDebug( __METHOD__ . ": showing diff page\n" ); 00434 $this->showDiffPage(); 00435 wfProfileOut( __METHOD__ ); 00436 00437 return; 00438 } 00439 00440 # Set page title (may be overridden by DISPLAYTITLE) 00441 $wgOut->setPageTitle( $this->getTitle()->getPrefixedText() ); 00442 00443 $wgOut->setArticleFlag( true ); 00444 # Allow frames by default 00445 $wgOut->allowClickjacking(); 00446 00447 $parserCache = ParserCache::singleton(); 00448 00449 $parserOptions = $this->getParserOptions(); 00450 # Render printable version, use printable version cache 00451 if ( $wgOut->isPrintable() ) { 00452 $parserOptions->setIsPrintable( true ); 00453 $parserOptions->setEditSection( false ); 00454 } elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit' ) ) { 00455 $parserOptions->setEditSection( false ); 00456 } 00457 00458 # Try client and file cache 00459 if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) { 00460 if ( $wgUseETag ) { 00461 $wgOut->setETag( $parserCache->getETag( $this, $parserOptions ) ); 00462 } 00463 00464 # Is it client cached? 00465 if ( $wgOut->checkLastModified( $this->mPage->getTouched() ) ) { 00466 wfDebug( __METHOD__ . ": done 304\n" ); 00467 wfProfileOut( __METHOD__ ); 00468 00469 return; 00470 # Try file cache 00471 } elseif ( $wgUseFileCache && $this->tryFileCache() ) { 00472 wfDebug( __METHOD__ . ": done file cache\n" ); 00473 # tell wgOut that output is taken care of 00474 $wgOut->disable(); 00475 $this->mPage->doViewUpdates( $wgUser ); 00476 wfProfileOut( __METHOD__ ); 00477 00478 return; 00479 } 00480 } 00481 00482 # Should the parser cache be used? 00483 $useParserCache = $this->mPage->isParserCacheUsed( $parserOptions, $oldid ); 00484 wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" ); 00485 if ( $wgUser->getStubThreshold() ) { 00486 wfIncrStats( 'pcache_miss_stub' ); 00487 } 00488 00489 $this->showRedirectedFromHeader(); 00490 $this->showNamespaceHeader(); 00491 00492 # Iterate through the possible ways of constructing the output text. 00493 # Keep going until $outputDone is set, or we run out of things to do. 00494 $pass = 0; 00495 $outputDone = false; 00496 $this->mParserOutput = false; 00497 00498 while ( !$outputDone && ++$pass ) { 00499 switch( $pass ) { 00500 case 1: 00501 wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$useParserCache ) ); 00502 break; 00503 case 2: 00504 # Early abort if the page doesn't exist 00505 if ( !$this->mPage->exists() ) { 00506 wfDebug( __METHOD__ . ": showing missing article\n" ); 00507 $this->showMissingArticle(); 00508 wfProfileOut( __METHOD__ ); 00509 return; 00510 } 00511 00512 # Try the parser cache 00513 if ( $useParserCache ) { 00514 $this->mParserOutput = $parserCache->get( $this, $parserOptions ); 00515 00516 if ( $this->mParserOutput !== false ) { 00517 if ( $oldid ) { 00518 wfDebug( __METHOD__ . ": showing parser cache contents for current rev permalink\n" ); 00519 $this->setOldSubtitle( $oldid ); 00520 } else { 00521 wfDebug( __METHOD__ . ": showing parser cache contents\n" ); 00522 } 00523 $wgOut->addParserOutput( $this->mParserOutput ); 00524 # Ensure that UI elements requiring revision ID have 00525 # the correct version information. 00526 $wgOut->setRevisionId( $this->mPage->getLatest() ); 00527 # Preload timestamp to avoid a DB hit 00528 $cachedTimestamp = $this->mParserOutput->getTimestamp(); 00529 if ( $cachedTimestamp !== null ) { 00530 $wgOut->setRevisionTimestamp( $cachedTimestamp ); 00531 $this->mPage->setTimestamp( $cachedTimestamp ); 00532 } 00533 $outputDone = true; 00534 } 00535 } 00536 break; 00537 case 3: 00538 # This will set $this->mRevision if needed 00539 $this->fetchContent(); 00540 00541 # Are we looking at an old revision 00542 if ( $oldid && $this->mRevision ) { 00543 $this->setOldSubtitle( $oldid ); 00544 00545 if ( !$this->showDeletedRevisionHeader() ) { 00546 wfDebug( __METHOD__ . ": cannot view deleted revision\n" ); 00547 wfProfileOut( __METHOD__ ); 00548 return; 00549 } 00550 } 00551 00552 # Ensure that UI elements requiring revision ID have 00553 # the correct version information. 00554 $wgOut->setRevisionId( $this->getRevIdFetched() ); 00555 # Preload timestamp to avoid a DB hit 00556 $wgOut->setRevisionTimestamp( $this->getTimestamp() ); 00557 00558 # Pages containing custom CSS or JavaScript get special treatment 00559 if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) { 00560 wfDebug( __METHOD__ . ": showing CSS/JS source\n" ); 00561 $this->showCssOrJsPage(); 00562 $outputDone = true; 00563 } elseif( !wfRunHooks( 'ArticleViewCustom', array( $this->mContent, $this->getTitle(), $wgOut ) ) ) { 00564 # Allow extensions do their own custom view for certain pages 00565 $outputDone = true; 00566 } else { 00567 $text = $this->getContent(); 00568 $rt = Title::newFromRedirectArray( $text ); 00569 if ( $rt ) { 00570 wfDebug( __METHOD__ . ": showing redirect=no page\n" ); 00571 # Viewing a redirect page (e.g. with parameter redirect=no) 00572 $wgOut->addHTML( $this->viewRedirect( $rt ) ); 00573 # Parse just to get categories, displaytitle, etc. 00574 $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(), $parserOptions ); 00575 $wgOut->addParserOutputNoText( $this->mParserOutput ); 00576 $outputDone = true; 00577 } 00578 } 00579 break; 00580 case 4: 00581 # Run the parse, protected by a pool counter 00582 wfDebug( __METHOD__ . ": doing uncached parse\n" ); 00583 00584 $poolArticleView = new PoolWorkArticleView( $this, $parserOptions, 00585 $this->getRevIdFetched(), $useParserCache, $this->getContent() ); 00586 00587 if ( !$poolArticleView->execute() ) { 00588 $error = $poolArticleView->getError(); 00589 if ( $error ) { 00590 $wgOut->clearHTML(); // for release() errors 00591 $wgOut->enableClientCache( false ); 00592 $wgOut->setRobotPolicy( 'noindex,nofollow' ); 00593 00594 $errortext = $error->getWikiText( false, 'view-pool-error' ); 00595 $wgOut->addWikiText( '<div class="errorbox">' . $errortext . '</div>' ); 00596 } 00597 # Connection or timeout error 00598 wfProfileOut( __METHOD__ ); 00599 return; 00600 } 00601 00602 $this->mParserOutput = $poolArticleView->getParserOutput(); 00603 $wgOut->addParserOutput( $this->mParserOutput ); 00604 00605 # Don't cache a dirty ParserOutput object 00606 if ( $poolArticleView->getIsDirty() ) { 00607 $wgOut->setSquidMaxage( 0 ); 00608 $wgOut->addHTML( "<!-- parser cache is expired, sending anyway due to pool overload-->\n" ); 00609 } 00610 00611 $outputDone = true; 00612 break; 00613 # Should be unreachable, but just in case... 00614 default: 00615 break 2; 00616 } 00617 } 00618 00619 # Get the ParserOutput actually *displayed* here. 00620 # Note that $this->mParserOutput is the *current* version output. 00621 $pOutput = ( $outputDone instanceof ParserOutput ) 00622 ? $outputDone // object fetched by hook 00623 : $this->mParserOutput; 00624 00625 # Adjust title for main page & pages with displaytitle 00626 if ( $pOutput ) { 00627 $this->adjustDisplayTitle( $pOutput ); 00628 } 00629 00630 # For the main page, overwrite the <title> element with the con- 00631 # tents of 'pagetitle-view-mainpage' instead of the default (if 00632 # that's not empty). 00633 # This message always exists because it is in the i18n files 00634 if ( $this->getTitle()->isMainPage() ) { 00635 $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage(); 00636 if ( !$msg->isDisabled() ) { 00637 $wgOut->setHTMLTitle( $msg->title( $this->getTitle() )->text() ); 00638 } 00639 } 00640 00641 # Check for any __NOINDEX__ tags on the page using $pOutput 00642 $policy = $this->getRobotPolicy( 'view', $pOutput ); 00643 $wgOut->setIndexPolicy( $policy['index'] ); 00644 $wgOut->setFollowPolicy( $policy['follow'] ); 00645 00646 $this->showViewFooter(); 00647 $this->mPage->doViewUpdates( $wgUser ); 00648 00649 wfProfileOut( __METHOD__ ); 00650 } 00651 00656 public function adjustDisplayTitle( ParserOutput $pOutput ) { 00657 global $wgOut; 00658 # Adjust the title if it was set by displaytitle, -{T|}- or language conversion 00659 $titleText = $pOutput->getTitleText(); 00660 if ( strval( $titleText ) !== '' ) { 00661 $wgOut->setPageTitle( $titleText ); 00662 } 00663 } 00664 00669 public function showDiffPage() { 00670 global $wgRequest, $wgUser; 00671 00672 $diff = $wgRequest->getVal( 'diff' ); 00673 $rcid = $wgRequest->getVal( 'rcid' ); 00674 $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) ); 00675 $purge = $wgRequest->getVal( 'action' ) == 'purge'; 00676 $unhide = $wgRequest->getInt( 'unhide' ) == 1; 00677 $oldid = $this->getOldID(); 00678 00679 $de = new DifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide ); 00680 // DifferenceEngine directly fetched the revision: 00681 $this->mRevIdFetched = $de->mNewid; 00682 $de->showDiffPage( $diffOnly ); 00683 00684 if ( $diff == 0 || $diff == $this->mPage->getLatest() ) { 00685 # Run view updates for current revision only 00686 $this->mPage->doViewUpdates( $wgUser ); 00687 } 00688 } 00689 00697 protected function showCssOrJsPage() { 00698 global $wgOut; 00699 00700 $dir = $this->getContext()->getLanguage()->getDir(); 00701 $lang = $this->getContext()->getLanguage()->getCode(); 00702 00703 $wgOut->wrapWikiMsg( "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>", 00704 'clearyourcache' ); 00705 00706 // Give hooks a chance to customise the output 00707 if ( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->getTitle(), $wgOut ) ) ) { 00708 // Wrap the whole lot in a <pre> and don't parse 00709 $m = array(); 00710 preg_match( '!\.(css|js)$!u', $this->getTitle()->getText(), $m ); 00711 $wgOut->addHTML( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" ); 00712 $wgOut->addHTML( htmlspecialchars( $this->mContent ) ); 00713 $wgOut->addHTML( "\n</pre>\n" ); 00714 } 00715 } 00716 00724 public function getRobotPolicy( $action, $pOutput ) { 00725 global $wgOut, $wgArticleRobotPolicies, $wgNamespaceRobotPolicies; 00726 global $wgDefaultRobotPolicy, $wgRequest; 00727 00728 $ns = $this->getTitle()->getNamespace(); 00729 00730 if ( $ns == NS_USER || $ns == NS_USER_TALK ) { 00731 # Don't index user and user talk pages for blocked users (bug 11443) 00732 if ( !$this->getTitle()->isSubpage() ) { 00733 if ( Block::newFromTarget( null, $this->getTitle()->getText() ) instanceof Block ) { 00734 return array( 00735 'index' => 'noindex', 00736 'follow' => 'nofollow' 00737 ); 00738 } 00739 } 00740 } 00741 00742 if ( $this->mPage->getID() === 0 || $this->getOldID() ) { 00743 # Non-articles (special pages etc), and old revisions 00744 return array( 00745 'index' => 'noindex', 00746 'follow' => 'nofollow' 00747 ); 00748 } elseif ( $wgOut->isPrintable() ) { 00749 # Discourage indexing of printable versions, but encourage following 00750 return array( 00751 'index' => 'noindex', 00752 'follow' => 'follow' 00753 ); 00754 } elseif ( $wgRequest->getInt( 'curid' ) ) { 00755 # For ?curid=x urls, disallow indexing 00756 return array( 00757 'index' => 'noindex', 00758 'follow' => 'follow' 00759 ); 00760 } 00761 00762 # Otherwise, construct the policy based on the various config variables. 00763 $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy ); 00764 00765 if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) { 00766 # Honour customised robot policies for this namespace 00767 $policy = array_merge( 00768 $policy, 00769 self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] ) 00770 ); 00771 } 00772 if ( $this->getTitle()->canUseNoindex() && is_object( $pOutput ) && $pOutput->getIndexPolicy() ) { 00773 # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates 00774 # a final sanity check that we have really got the parser output. 00775 $policy = array_merge( 00776 $policy, 00777 array( 'index' => $pOutput->getIndexPolicy() ) 00778 ); 00779 } 00780 00781 if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) { 00782 # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__ 00783 $policy = array_merge( 00784 $policy, 00785 self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) 00786 ); 00787 } 00788 00789 return $policy; 00790 } 00791 00799 public static function formatRobotPolicy( $policy ) { 00800 if ( is_array( $policy ) ) { 00801 return $policy; 00802 } elseif ( !$policy ) { 00803 return array(); 00804 } 00805 00806 $policy = explode( ',', $policy ); 00807 $policy = array_map( 'trim', $policy ); 00808 00809 $arr = array(); 00810 foreach ( $policy as $var ) { 00811 if ( in_array( $var, array( 'index', 'noindex' ) ) ) { 00812 $arr['index'] = $var; 00813 } elseif ( in_array( $var, array( 'follow', 'nofollow' ) ) ) { 00814 $arr['follow'] = $var; 00815 } 00816 } 00817 00818 return $arr; 00819 } 00820 00828 public function showRedirectedFromHeader() { 00829 global $wgOut, $wgRequest, $wgRedirectSources; 00830 00831 $rdfrom = $wgRequest->getVal( 'rdfrom' ); 00832 00833 if ( isset( $this->mRedirectedFrom ) ) { 00834 // This is an internally redirected page view. 00835 // We'll need a backlink to the source page for navigation. 00836 if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) { 00837 $redir = Linker::linkKnown( 00838 $this->mRedirectedFrom, 00839 null, 00840 array(), 00841 array( 'redirect' => 'no' ) 00842 ); 00843 00844 $wgOut->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) ); 00845 00846 // Set the fragment if one was specified in the redirect 00847 if ( strval( $this->getTitle()->getFragment() ) != '' ) { 00848 $fragment = Xml::escapeJsString( $this->getTitle()->getFragmentForURL() ); 00849 $wgOut->addInlineScript( "redirectToFragment(\"$fragment\");" ); 00850 } 00851 00852 // Add a <link rel="canonical"> tag 00853 $wgOut->addLink( array( 'rel' => 'canonical', 00854 'href' => $this->getTitle()->getLocalURL() ) 00855 ); 00856 00857 // Tell $wgOut the user arrived at this article through a redirect 00858 $wgOut->setRedirectedFrom( $this->mRedirectedFrom ); 00859 00860 return true; 00861 } 00862 } elseif ( $rdfrom ) { 00863 // This is an externally redirected view, from some other wiki. 00864 // If it was reported from a trusted site, supply a backlink. 00865 if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) { 00866 $redir = Linker::makeExternalLink( $rdfrom, $rdfrom ); 00867 $wgOut->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) ); 00868 00869 return true; 00870 } 00871 } 00872 00873 return false; 00874 } 00875 00880 public function showNamespaceHeader() { 00881 global $wgOut; 00882 00883 if ( $this->getTitle()->isTalkPage() ) { 00884 if ( !wfMessage( 'talkpageheader' )->isDisabled() ) { 00885 $wgOut->wrapWikiMsg( "<div class=\"mw-talkpageheader\">\n$1\n</div>", array( 'talkpageheader' ) ); 00886 } 00887 } 00888 } 00889 00893 public function showViewFooter() { 00894 global $wgOut; 00895 00896 # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page 00897 if ( $this->getTitle()->getNamespace() == NS_USER_TALK && IP::isValid( $this->getTitle()->getText() ) ) { 00898 $wgOut->addWikiMsg( 'anontalkpagetext' ); 00899 } 00900 00901 # If we have been passed an &rcid= parameter, we want to give the user a 00902 # chance to mark this new article as patrolled. 00903 $this->showPatrolFooter(); 00904 00905 wfRunHooks( 'ArticleViewFooter', array( $this ) ); 00906 00907 } 00908 00914 public function showPatrolFooter() { 00915 global $wgOut, $wgRequest, $wgUser; 00916 00917 $rcid = $wgRequest->getVal( 'rcid' ); 00918 00919 if ( !$rcid || !$this->getTitle()->quickUserCan( 'patrol' ) ) { 00920 return; 00921 } 00922 00923 $token = $wgUser->getEditToken( $rcid ); 00924 $wgOut->preventClickjacking(); 00925 00926 $wgOut->addHTML( 00927 "<div class='patrollink'>" . 00928 wfMsgHtml( 00929 'markaspatrolledlink', 00930 Linker::link( 00931 $this->getTitle(), 00932 wfMsgHtml( 'markaspatrolledtext' ), 00933 array(), 00934 array( 00935 'action' => 'markpatrolled', 00936 'rcid' => $rcid, 00937 'token' => $token, 00938 ), 00939 array( 'known', 'noclasses' ) 00940 ) 00941 ) . 00942 '</div>' 00943 ); 00944 } 00945 00950 public function showMissingArticle() { 00951 global $wgOut, $wgRequest, $wgUser, $wgSend404Code; 00952 00953 # Show info in user (talk) namespace. Does the user exist? Is he blocked? 00954 if ( $this->getTitle()->getNamespace() == NS_USER || $this->getTitle()->getNamespace() == NS_USER_TALK ) { 00955 $parts = explode( '/', $this->getTitle()->getText() ); 00956 $rootPart = $parts[0]; 00957 $user = User::newFromName( $rootPart, false /* allow IP users*/ ); 00958 $ip = User::isIP( $rootPart ); 00959 00960 if ( !($user && $user->isLoggedIn()) && !$ip ) { # User does not exist 00961 $wgOut->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>", 00962 array( 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ) ); 00963 } elseif ( $user->isBlocked() ) { # Show log extract if the user is currently blocked 00964 LogEventsList::showLogExtract( 00965 $wgOut, 00966 'block', 00967 $user->getUserPage()->getPrefixedText(), 00968 '', 00969 array( 00970 'lim' => 1, 00971 'showIfEmpty' => false, 00972 'msgKey' => array( 00973 'blocked-notice-logextract', 00974 $user->getName() # Support GENDER in notice 00975 ) 00976 ) 00977 ); 00978 } 00979 } 00980 00981 wfRunHooks( 'ShowMissingArticle', array( $this ) ); 00982 00983 # Show delete and move logs 00984 LogEventsList::showLogExtract( $wgOut, array( 'delete', 'move' ), $this->getTitle()->getPrefixedText(), '', 00985 array( 'lim' => 10, 00986 'conds' => array( "log_action != 'revision'" ), 00987 'showIfEmpty' => false, 00988 'msgKey' => array( 'moveddeleted-notice' ) ) 00989 ); 00990 00991 if ( !$this->mPage->hasViewableContent() && $wgSend404Code ) { 00992 // If there's no backing content, send a 404 Not Found 00993 // for better machine handling of broken links. 00994 $wgRequest->response()->header( "HTTP/1.1 404 Not Found" ); 00995 } 00996 00997 $hookResult = wfRunHooks( 'BeforeDisplayNoArticleText', array( $this ) ); 00998 00999 if ( ! $hookResult ) { 01000 return; 01001 } 01002 01003 # Show error message 01004 $oldid = $this->getOldID(); 01005 if ( $oldid ) { 01006 $text = wfMsgNoTrans( 'missing-article', 01007 $this->getTitle()->getPrefixedText(), 01008 wfMsgNoTrans( 'missingarticle-rev', $oldid ) ); 01009 } elseif ( $this->getTitle()->getNamespace() === NS_MEDIAWIKI ) { 01010 // Use the default message text 01011 $text = $this->getTitle()->getDefaultMessageText(); 01012 } else { 01013 $createErrors = $this->getTitle()->getUserPermissionsErrors( 'create', $wgUser ); 01014 $editErrors = $this->getTitle()->getUserPermissionsErrors( 'edit', $wgUser ); 01015 $errors = array_merge( $createErrors, $editErrors ); 01016 01017 if ( !count( $errors ) ) { 01018 $text = wfMsgNoTrans( 'noarticletext' ); 01019 } else { 01020 $text = wfMsgNoTrans( 'noarticletext-nopermission' ); 01021 } 01022 } 01023 $text = "<div class='noarticletext'>\n$text\n</div>"; 01024 01025 $wgOut->addWikiText( $text ); 01026 } 01027 01034 public function showDeletedRevisionHeader() { 01035 global $wgOut, $wgRequest; 01036 01037 if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) { 01038 // Not deleted 01039 return true; 01040 } 01041 01042 // If the user is not allowed to see it... 01043 if ( !$this->mRevision->userCan( Revision::DELETED_TEXT ) ) { 01044 $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", 01045 'rev-deleted-text-permission' ); 01046 01047 return false; 01048 // If the user needs to confirm that they want to see it... 01049 } elseif ( $wgRequest->getInt( 'unhide' ) != 1 ) { 01050 # Give explanation and add a link to view the revision... 01051 $oldid = intval( $this->getOldID() ); 01052 $link = $this->getTitle()->getFullUrl( "oldid={$oldid}&unhide=1" ); 01053 $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ? 01054 'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide'; 01055 $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", 01056 array( $msg, $link ) ); 01057 01058 return false; 01059 // We are allowed to see... 01060 } else { 01061 $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ? 01062 'rev-suppressed-text-view' : 'rev-deleted-text-view'; 01063 $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg ); 01064 01065 return true; 01066 } 01067 } 01068 01077 public function setOldSubtitle( $oldid = 0 ) { 01078 global $wgLang, $wgOut, $wgUser, $wgRequest; 01079 01080 if ( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) { 01081 return; 01082 } 01083 01084 $unhide = $wgRequest->getInt( 'unhide' ) == 1; 01085 01086 # Cascade unhide param in links for easy deletion browsing 01087 $extraParams = array(); 01088 if ( $wgRequest->getVal( 'unhide' ) ) { 01089 $extraParams['unhide'] = 1; 01090 } 01091 01092 $revision = Revision::newFromId( $oldid ); 01093 $timestamp = $revision->getTimestamp(); 01094 01095 $current = ( $oldid == $this->mPage->getLatest() ); 01096 $td = $wgLang->timeanddate( $timestamp, true ); 01097 $tddate = $wgLang->date( $timestamp, true ); 01098 $tdtime = $wgLang->time( $timestamp, true ); 01099 01100 # Show user links if allowed to see them. If hidden, then show them only if requested... 01101 $userlinks = Linker::revUserTools( $revision, !$unhide ); 01102 01103 $infomsg = $current && !wfMessage( 'revision-info-current' )->isDisabled() 01104 ? 'revision-info-current' 01105 : 'revision-info'; 01106 01107 $wgOut->addSubtitle( "<div id=\"mw-{$infomsg}\">" . wfMessage( $infomsg, 01108 $td )->rawParams( $userlinks )->params( $revision->getID(), $tddate, 01109 $tdtime, $revision->getUser() )->parse() . "</div>" ); 01110 01111 $lnk = $current 01112 ? wfMsgHtml( 'currentrevisionlink' ) 01113 : Linker::link( 01114 $this->getTitle(), 01115 wfMsgHtml( 'currentrevisionlink' ), 01116 array(), 01117 $extraParams, 01118 array( 'known', 'noclasses' ) 01119 ); 01120 $curdiff = $current 01121 ? wfMsgHtml( 'diff' ) 01122 : Linker::link( 01123 $this->getTitle(), 01124 wfMsgHtml( 'diff' ), 01125 array(), 01126 array( 01127 'diff' => 'cur', 01128 'oldid' => $oldid 01129 ) + $extraParams, 01130 array( 'known', 'noclasses' ) 01131 ); 01132 $prev = $this->getTitle()->getPreviousRevisionID( $oldid ) ; 01133 $prevlink = $prev 01134 ? Linker::link( 01135 $this->getTitle(), 01136 wfMsgHtml( 'previousrevision' ), 01137 array(), 01138 array( 01139 'direction' => 'prev', 01140 'oldid' => $oldid 01141 ) + $extraParams, 01142 array( 'known', 'noclasses' ) 01143 ) 01144 : wfMsgHtml( 'previousrevision' ); 01145 $prevdiff = $prev 01146 ? Linker::link( 01147 $this->getTitle(), 01148 wfMsgHtml( 'diff' ), 01149 array(), 01150 array( 01151 'diff' => 'prev', 01152 'oldid' => $oldid 01153 ) + $extraParams, 01154 array( 'known', 'noclasses' ) 01155 ) 01156 : wfMsgHtml( 'diff' ); 01157 $nextlink = $current 01158 ? wfMsgHtml( 'nextrevision' ) 01159 : Linker::link( 01160 $this->getTitle(), 01161 wfMsgHtml( 'nextrevision' ), 01162 array(), 01163 array( 01164 'direction' => 'next', 01165 'oldid' => $oldid 01166 ) + $extraParams, 01167 array( 'known', 'noclasses' ) 01168 ); 01169 $nextdiff = $current 01170 ? wfMsgHtml( 'diff' ) 01171 : Linker::link( 01172 $this->getTitle(), 01173 wfMsgHtml( 'diff' ), 01174 array(), 01175 array( 01176 'diff' => 'next', 01177 'oldid' => $oldid 01178 ) + $extraParams, 01179 array( 'known', 'noclasses' ) 01180 ); 01181 01182 $cdel = Linker::getRevDeleteLink( $wgUser, $revision, $this->getTitle() ); 01183 if ( $cdel !== '' ) { 01184 $cdel .= ' '; 01185 } 01186 01187 $wgOut->addSubtitle( "<div id=\"mw-revision-nav\">" . $cdel . 01188 wfMsgExt( 'revision-nav', array( 'escapenoentities', 'parsemag', 'replaceafter' ), 01189 $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "</div>" ); 01190 } 01191 01200 public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) { 01201 global $wgOut, $wgStylePath; 01202 01203 if ( !is_array( $target ) ) { 01204 $target = array( $target ); 01205 } 01206 01207 $lang = $this->getTitle()->getPageLanguage(); 01208 $imageDir = $lang->getDir(); 01209 01210 if ( $appendSubtitle ) { 01211 $wgOut->appendSubtitle( wfMsgHtml( 'redirectpagesub' ) ); 01212 } 01213 01214 // the loop prepends the arrow image before the link, so the first case needs to be outside 01215 01219 $title = array_shift( $target ); 01220 01221 if ( $forceKnown ) { 01222 $link = Linker::linkKnown( $title, htmlspecialchars( $title->getFullText() ) ); 01223 } else { 01224 $link = Linker::link( $title, htmlspecialchars( $title->getFullText() ) ); 01225 } 01226 01227 $nextRedirect = $wgStylePath . '/common/images/nextredirect' . $imageDir . '.png'; 01228 $alt = $lang->isRTL() ? '←' : '→'; 01229 // Automatically append redirect=no to each link, since most of them are redirect pages themselves. 01230 foreach ( $target as $rt ) { 01231 $link .= Html::element( 'img', array( 'src' => $nextRedirect, 'alt' => $alt ) ); 01232 if ( $forceKnown ) { 01233 $link .= Linker::linkKnown( $rt, htmlspecialchars( $rt->getFullText(), array(), array( 'redirect' => 'no' ) ) ); 01234 } else { 01235 $link .= Linker::link( $rt, htmlspecialchars( $rt->getFullText() ), array(), array( 'redirect' => 'no' ) ); 01236 } 01237 } 01238 01239 $imageUrl = $wgStylePath . '/common/images/redirect' . $imageDir . '.png'; 01240 return '<div class="redirectMsg">' . 01241 Html::element( 'img', array( 'src' => $imageUrl, 'alt' => '#REDIRECT' ) ) . 01242 '<span class="redirectText">' . $link . '</span></div>'; 01243 } 01244 01248 public function render() { 01249 global $wgOut; 01250 01251 $wgOut->setArticleBodyOnly( true ); 01252 $this->view(); 01253 } 01254 01258 public function protect() { 01259 $form = new ProtectionForm( $this ); 01260 $form->execute(); 01261 } 01262 01266 public function unprotect() { 01267 $this->protect(); 01268 } 01269 01273 public function delete() { 01274 global $wgOut, $wgRequest, $wgLang; 01275 01276 # This code desperately needs to be totally rewritten 01277 01278 $title = $this->getTitle(); 01279 $user = $this->getContext()->getUser(); 01280 01281 # Check permissions 01282 $permission_errors = $title->getUserPermissionsErrors( 'delete', $user ); 01283 if ( count( $permission_errors ) ) { 01284 throw new PermissionsError( 'delete', $permission_errors ); 01285 } 01286 01287 # Read-only check... 01288 if ( wfReadOnly() ) { 01289 throw new ReadOnlyError; 01290 } 01291 01292 # Better double-check that it hasn't been deleted yet! 01293 $dbw = wfGetDB( DB_MASTER ); 01294 $conds = $title->pageCond(); 01295 $latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ ); 01296 if ( $latest === false ) { 01297 $wgOut->setPageTitle( wfMessage( 'cannotdelete-title', $title->getPrefixedText() ) ); 01298 $wgOut->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>", 01299 array( 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ) 01300 ); 01301 $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) ); 01302 LogEventsList::showLogExtract( 01303 $wgOut, 01304 'delete', 01305 $title->getPrefixedText() 01306 ); 01307 01308 return; 01309 } 01310 01311 $deleteReasonList = $wgRequest->getText( 'wpDeleteReasonList', 'other' ); 01312 $deleteReason = $wgRequest->getText( 'wpReason' ); 01313 01314 if ( $deleteReasonList == 'other' ) { 01315 $reason = $deleteReason; 01316 } elseif ( $deleteReason != '' ) { 01317 // Entry from drop down menu + additional comment 01318 $reason = $deleteReasonList . wfMsgForContent( 'colon-separator' ) . $deleteReason; 01319 } else { 01320 $reason = $deleteReasonList; 01321 } 01322 01323 if ( $wgRequest->wasPosted() && $user->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), 01324 array( 'delete', $this->getTitle()->getPrefixedText() ) ) ) 01325 { 01326 # Flag to hide all contents of the archived revisions 01327 $suppress = $wgRequest->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' ); 01328 01329 $this->doDelete( $reason, $suppress ); 01330 01331 if ( $wgRequest->getCheck( 'wpWatch' ) && $user->isLoggedIn() ) { 01332 $this->doWatch(); 01333 } elseif ( $title->userIsWatching() ) { 01334 $this->doUnwatch(); 01335 } 01336 01337 return; 01338 } 01339 01340 // Generate deletion reason 01341 $hasHistory = false; 01342 if ( !$reason ) { 01343 $reason = $this->generateReason( $hasHistory ); 01344 } 01345 01346 // If the page has a history, insert a warning 01347 if ( $hasHistory ) { 01348 $revisions = $this->mTitle->estimateRevisionCount(); 01349 // @todo FIXME: i18n issue/patchwork message 01350 $wgOut->addHTML( '<strong class="mw-delete-warning-revisions">' . 01351 wfMsgExt( 'historywarning', array( 'parseinline' ), $wgLang->formatNum( $revisions ) ) . 01352 wfMsgHtml( 'word-separator' ) . Linker::link( $title, 01353 wfMsgHtml( 'history' ), 01354 array( 'rel' => 'archives' ), 01355 array( 'action' => 'history' ) ) . 01356 '</strong>' 01357 ); 01358 01359 if ( $this->mTitle->isBigDeletion() ) { 01360 global $wgDeleteRevisionsLimit; 01361 $wgOut->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n", 01362 array( 'delete-warning-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) ); 01363 } 01364 } 01365 01366 return $this->confirmDelete( $reason ); 01367 } 01368 01374 public function confirmDelete( $reason ) { 01375 global $wgOut; 01376 01377 wfDebug( "Article::confirmDelete\n" ); 01378 01379 $wgOut->setPageTitle( wfMessage( 'delete-confirm', $this->getTitle()->getPrefixedText() ) ); 01380 $wgOut->addBacklinkSubtitle( $this->getTitle() ); 01381 $wgOut->setRobotPolicy( 'noindex,nofollow' ); 01382 $wgOut->addWikiMsg( 'confirmdeletetext' ); 01383 01384 wfRunHooks( 'ArticleConfirmDelete', array( $this, $wgOut, &$reason ) ); 01385 01386 $user = $this->getContext()->getUser(); 01387 01388 if ( $user->isAllowed( 'suppressrevision' ) ) { 01389 $suppress = "<tr id=\"wpDeleteSuppressRow\"> 01390 <td></td> 01391 <td class='mw-input'><strong>" . 01392 Xml::checkLabel( wfMsg( 'revdelete-suppress' ), 01393 'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '4' ) ) . 01394 "</strong></td> 01395 </tr>"; 01396 } else { 01397 $suppress = ''; 01398 } 01399 $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $this->getTitle()->userIsWatching(); 01400 01401 $form = Xml::openElement( 'form', array( 'method' => 'post', 01402 'action' => $this->getTitle()->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) . 01403 Xml::openElement( 'fieldset', array( 'id' => 'mw-delete-table' ) ) . 01404 Xml::tags( 'legend', null, wfMsgExt( 'delete-legend', array( 'parsemag', 'escapenoentities' ) ) ) . 01405 Xml::openElement( 'table', array( 'id' => 'mw-deleteconfirm-table' ) ) . 01406 "<tr id=\"wpDeleteReasonListRow\"> 01407 <td class='mw-label'>" . 01408 Xml::label( wfMsg( 'deletecomment' ), 'wpDeleteReasonList' ) . 01409 "</td> 01410 <td class='mw-input'>" . 01411 Xml::listDropDown( 'wpDeleteReasonList', 01412 wfMsgForContent( 'deletereason-dropdown' ), 01413 wfMsgForContent( 'deletereasonotherlist' ), '', 'wpReasonDropDown', 1 ) . 01414 "</td> 01415 </tr> 01416 <tr id=\"wpDeleteReasonRow\"> 01417 <td class='mw-label'>" . 01418 Xml::label( wfMsg( 'deleteotherreason' ), 'wpReason' ) . 01419 "</td> 01420 <td class='mw-input'>" . 01421 Html::input( 'wpReason', $reason, 'text', array( 01422 'size' => '60', 01423 'maxlength' => '255', 01424 'tabindex' => '2', 01425 'id' => 'wpReason', 01426 'autofocus' 01427 ) ) . 01428 "</td> 01429 </tr>"; 01430 01431 # Disallow watching if user is not logged in 01432 if ( $user->isLoggedIn() ) { 01433 $form .= " 01434 <tr> 01435 <td></td> 01436 <td class='mw-input'>" . 01437 Xml::checkLabel( wfMsg( 'watchthis' ), 01438 'wpWatch', 'wpWatch', $checkWatch, array( 'tabindex' => '3' ) ) . 01439 "</td> 01440 </tr>"; 01441 } 01442 01443 $form .= " 01444 $suppress 01445 <tr> 01446 <td></td> 01447 <td class='mw-submit'>" . 01448 Xml::submitButton( wfMsg( 'deletepage' ), 01449 array( 'name' => 'wpConfirmB', 'id' => 'wpConfirmB', 'tabindex' => '5' ) ) . 01450 "</td> 01451 </tr>" . 01452 Xml::closeElement( 'table' ) . 01453 Xml::closeElement( 'fieldset' ) . 01454 Html::hidden( 'wpEditToken', $user->getEditToken( array( 'delete', $this->getTitle()->getPrefixedText() ) ) ) . 01455 Xml::closeElement( 'form' ); 01456 01457 if ( $user->isAllowed( 'editinterface' ) ) { 01458 $title = Title::makeTitle( NS_MEDIAWIKI, 'Deletereason-dropdown' ); 01459 $link = Linker::link( 01460 $title, 01461 wfMsgHtml( 'delete-edit-reasonlist' ), 01462 array(), 01463 array( 'action' => 'edit' ) 01464 ); 01465 $form .= '<p class="mw-delete-editreasons">' . $link . '</p>'; 01466 } 01467 01468 $wgOut->addHTML( $form ); 01469 $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) ); 01470 LogEventsList::showLogExtract( $wgOut, 'delete', 01471 $this->getTitle()->getPrefixedText() 01472 ); 01473 } 01474 01480 public function doDelete( $reason, $suppress = false ) { 01481 global $wgOut; 01482 01483 $error = ''; 01484 if ( $this->mPage->doDeleteArticle( $reason, $suppress, 0, true, $error ) ) { 01485 $deleted = $this->getTitle()->getPrefixedText(); 01486 01487 $wgOut->setPageTitle( wfMessage( 'actioncomplete' ) ); 01488 $wgOut->setRobotPolicy( 'noindex,nofollow' ); 01489 01490 $loglink = '[[Special:Log/delete|' . wfMsgNoTrans( 'deletionlog' ) . ']]'; 01491 01492 $wgOut->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink ); 01493 $wgOut->returnToMain( false ); 01494 } else { 01495 $wgOut->setPageTitle( wfMessage( 'cannotdelete-title', $this->getTitle()->getPrefixedText() ) ); 01496 if ( $error == '' ) { 01497 $wgOut->wrapWikiMsg( "<div class=\"error mw-error-cannotdelete\">\n$1\n</div>", 01498 array( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) ) 01499 ); 01500 $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) ); 01501 01502 LogEventsList::showLogExtract( 01503 $wgOut, 01504 'delete', 01505 $this->getTitle()->getPrefixedText() 01506 ); 01507 } else { 01508 $wgOut->addHTML( $error ); 01509 } 01510 } 01511 } 01512 01513 /* Caching functions */ 01514 01522 protected function tryFileCache() { 01523 static $called = false; 01524 01525 if ( $called ) { 01526 wfDebug( "Article::tryFileCache(): called twice!?\n" ); 01527 return false; 01528 } 01529 01530 $called = true; 01531 if ( $this->isFileCacheable() ) { 01532 $cache = HTMLFileCache::newFromTitle( $this->getTitle(), 'view' ); 01533 if ( $cache->isCacheGood( $this->mPage->getTouched() ) ) { 01534 wfDebug( "Article::tryFileCache(): about to load file\n" ); 01535 $cache->loadFromFileCache( $this->getContext() ); 01536 return true; 01537 } else { 01538 wfDebug( "Article::tryFileCache(): starting buffer\n" ); 01539 ob_start( array( &$cache, 'saveToFileCache' ) ); 01540 } 01541 } else { 01542 wfDebug( "Article::tryFileCache(): not cacheable\n" ); 01543 } 01544 01545 return false; 01546 } 01547 01552 public function isFileCacheable() { 01553 $cacheable = false; 01554 01555 if ( HTMLFileCache::useFileCache( $this->getContext() ) ) { 01556 $cacheable = $this->mPage->getID() 01557 && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect(); 01558 // Extension may have reason to disable file caching on some pages. 01559 if ( $cacheable ) { 01560 $cacheable = wfRunHooks( 'IsFileCacheable', array( &$this ) ); 01561 } 01562 } 01563 01564 return $cacheable; 01565 } 01566 01580 public function getParserOutput( $oldid = null, User $user = null ) { 01581 global $wgUser; 01582 01583 $user = is_null( $user ) ? $wgUser : $user; 01584 $parserOptions = $this->mPage->makeParserOptions( $user ); 01585 01586 return $this->mPage->getParserOutput( $parserOptions, $oldid ); 01587 } 01588 01593 public function getParserOptions() { 01594 global $wgUser; 01595 if ( !$this->mParserOptions ) { 01596 $this->mParserOptions = $this->mPage->makeParserOptions( $wgUser ); 01597 } 01598 // Clone to allow modifications of the return value without affecting cache 01599 return clone $this->mParserOptions; 01600 } 01601 01608 public function setContext( $context ) { 01609 $this->mContext = $context; 01610 } 01611 01618 public function getContext() { 01619 if ( $this->mContext instanceof IContextSource ) { 01620 return $this->mContext; 01621 } else { 01622 wfDebug( __METHOD__ . " called and \$mContext is null. Return RequestContext::getMain(); for sanity\n" ); 01623 return RequestContext::getMain(); 01624 } 01625 } 01626 01631 public function info() { 01632 wfDeprecated( __METHOD__, '1.19' ); 01633 Action::factory( 'info', $this )->show(); 01634 } 01635 01640 public function markpatrolled() { 01641 wfDeprecated( __METHOD__, '1.18' ); 01642 Action::factory( 'markpatrolled', $this )->show(); 01643 } 01644 01649 public function purge() { 01650 return Action::factory( 'purge', $this )->show(); 01651 } 01652 01657 public function revert() { 01658 wfDeprecated( __METHOD__, '1.19' ); 01659 Action::factory( 'revert', $this )->show(); 01660 } 01661 01666 public function rollback() { 01667 wfDeprecated( __METHOD__, '1.19' ); 01668 Action::factory( 'rollback', $this )->show(); 01669 } 01670 01676 public function watch() { 01677 wfDeprecated( __METHOD__, '1.18' ); 01678 Action::factory( 'watch', $this )->show(); 01679 } 01680 01689 public function doWatch() { 01690 global $wgUser; 01691 wfDeprecated( __METHOD__, '1.18' ); 01692 return WatchAction::doWatch( $this->getTitle(), $wgUser ); 01693 } 01694 01700 public function unwatch() { 01701 wfDeprecated( __METHOD__, '1.18' ); 01702 Action::factory( 'unwatch', $this )->show(); 01703 } 01704 01710 public function doUnwatch() { 01711 global $wgUser; 01712 wfDeprecated( __METHOD__, '1.18' ); 01713 return WatchAction::doUnwatch( $this->getTitle(), $wgUser ); 01714 } 01715 01725 public function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) { 01726 wfDeprecated( __METHOD__, '1.18' ); 01727 global $wgOut; 01728 01729 if ( $noRedir ) { 01730 $query = 'redirect=no'; 01731 if ( $extraQuery ) 01732 $query .= "&$extraQuery"; 01733 } else { 01734 $query = $extraQuery; 01735 } 01736 01737 $wgOut->redirect( $this->getTitle()->getFullURL( $query ) . $sectionAnchor ); 01738 } 01739 01746 public function __get( $fname ) { 01747 if ( property_exists( $this->mPage, $fname ) ) { 01748 #wfWarn( "Access to raw $fname field " . __CLASS__ ); 01749 return $this->mPage->$fname; 01750 } 01751 trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE ); 01752 } 01753 01761 public function __set( $fname, $fvalue ) { 01762 if ( property_exists( $this->mPage, $fname ) ) { 01763 #wfWarn( "Access to raw $fname field of " . __CLASS__ ); 01764 $this->mPage->$fname = $fvalue; 01765 // Note: extensions may want to toss on new fields 01766 } elseif ( !in_array( $fname, array( 'mContext', 'mPage' ) ) ) { 01767 $this->mPage->$fname = $fvalue; 01768 } else { 01769 trigger_error( 'Inaccessible property via __set(): ' . $fname, E_USER_NOTICE ); 01770 } 01771 } 01772 01780 public function __call( $fname, $args ) { 01781 if ( is_callable( array( $this->mPage, $fname ) ) ) { 01782 #wfWarn( "Call to " . __CLASS__ . "::$fname; please use WikiPage instead" ); 01783 return call_user_func_array( array( $this->mPage, $fname ), $args ); 01784 } 01785 trigger_error( 'Inaccessible function via __call(): ' . $fname, E_USER_ERROR ); 01786 } 01787 01788 // ****** B/C functions to work-around PHP silliness with __call and references ****** // 01789 01798 public function doUpdateRestrictions( array $limit, array $expiry, &$cascade, $reason, User $user ) { 01799 return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user ); 01800 } 01801 01809 public function updateRestrictions( $limit = array(), $reason = '', &$cascade = 0, $expiry = array() ) { 01810 return $this->mPage->updateRestrictions( $limit, $reason, $cascade, $expiry ); 01811 } 01812 01821 public function doDeleteArticle( $reason, $suppress = false, $id = 0, $commit = true, &$error = '' ) { 01822 return $this->mPage->doDeleteArticle( $reason, $suppress, $id, $commit, $error ); 01823 } 01824 01834 public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) { 01835 global $wgUser; 01836 $user = is_null( $user ) ? $wgUser : $user; 01837 return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user ); 01838 } 01839 01848 public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) { 01849 global $wgUser; 01850 $guser = is_null( $guser ) ? $wgUser : $guser; 01851 return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser ); 01852 } 01853 01858 public function generateReason( &$hasHistory ) { 01859 return $this->mPage->getAutoDeleteReason( $hasHistory ); 01860 } 01861 01862 // ****** B/C functions for static methods ( __callStatic is PHP>=5.3 ) ****** // 01863 01867 public static function selectFields() { 01868 return WikiPage::selectFields(); 01869 } 01870 01874 public static function onArticleCreate( $title ) { 01875 WikiPage::onArticleCreate( $title ); 01876 } 01877 01881 public static function onArticleDelete( $title ) { 01882 WikiPage::onArticleDelete( $title ); 01883 } 01884 01888 public static function onArticleEdit( $title ) { 01889 WikiPage::onArticleEdit( $title ); 01890 } 01891 01898 public static function getAutosummary( $oldtext, $newtext, $flags ) { 01899 return WikiPage::getAutosummary( $oldtext, $newtext, $flags ); 01900 } 01901 // ****** 01902 }