r97081 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r97080‎ | r97081 | r97082 >
Date:18:08, 14 September 2011
Author:dantman
Status:deferred (Comments)
Tags:
Comment:
Checkpoint; Starting with new PageView system. This is very non-functional now. Development pattern for this project, break everything then make it better... like a bone.
Modified paths:
  • /branches/pageoutput/includes/Article.php (modified) (history)
  • /branches/pageoutput/includes/AutoLoader.php (modified) (history)
  • /branches/pageoutput/includes/Wiki.php (modified) (history)
  • /branches/pageoutput/includes/WikiPage.php (modified) (history)
  • /branches/pageoutput/includes/parser/ParserCache.php (modified) (history)
  • /branches/pageoutput/includes/view (added) (history)
  • /branches/pageoutput/includes/view/ArticlePageView.php (added) (history)
  • /branches/pageoutput/includes/view/PageView.php (added) (history)

Diff [purge]

Index: branches/pageoutput/includes/Article.php
@@ -16,6 +16,7 @@
1717 * moved to separate EditPage and HTMLFileCache classes.
1818 *
1919 * @internal documentation reviewed 15 Mar 2010
 20+ * @deprecated
2021 */
2122 class Article extends Page {
2223 /**@{{
@@ -185,52 +186,12 @@
186187 }
187188 }
188189
189 - /**
190 - * @return int The oldid of the article that is to be shown, 0 for the
191 - * current revision
192 - */
193190 public function getOldID() {
194 - if ( is_null( $this->mOldId ) ) {
195 - $this->mOldId = $this->getOldIDFromRequest();
196 - }
197 -
198 - return $this->mOldId;
 191+ throw MWException(__METHOD__ . " moved to PageView." );
199192 }
200193
201 - /**
202 - * Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect
203 - *
204 - * @return int The old id for the request
205 - */
206194 public function getOldIDFromRequest() {
207 - global $wgRequest;
208 -
209 - $this->mRedirectUrl = false;
210 -
211 - $oldid = $wgRequest->getVal( 'oldid' );
212 -
213 - if ( isset( $oldid ) ) {
214 - $oldid = intval( $oldid );
215 - if ( $wgRequest->getVal( 'direction' ) == 'next' ) {
216 - $nextid = $this->getTitle()->getNextRevisionID( $oldid );
217 - if ( $nextid ) {
218 - $oldid = $nextid;
219 - } else {
220 - $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
221 - }
222 - } elseif ( $wgRequest->getVal( 'direction' ) == 'prev' ) {
223 - $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
224 - if ( $previd ) {
225 - $oldid = $previd;
226 - }
227 - }
228 - }
229 -
230 - if ( !$oldid ) {
231 - $oldid = 0;
232 - }
233 -
234 - return $oldid;
 195+ throw MWException(__METHOD__ . " moved to PageView." );
235196 }
236197
237198 /**
@@ -347,236 +308,7 @@
348309 * page of the given title.
349310 */
350311 public function view() {
351 - global $wgUser, $wgOut, $wgRequest, $wgParser;
352 - global $wgUseFileCache, $wgUseETag;
353 -
354 - wfProfileIn( __METHOD__ );
355 -
356 - # Get variables from query string
357 - $oldid = $this->getOldID();
358 -
359 - # getOldID may want us to redirect somewhere else
360 - if ( $this->mRedirectUrl ) {
361 - $wgOut->redirect( $this->mRedirectUrl );
362 - wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
363 - wfProfileOut( __METHOD__ );
364 -
365 - return;
366 - }
367 -
368 - # Set page title (may be overridden by DISPLAYTITLE)
369 - $wgOut->setPageTitle( $this->getTitle()->getPrefixedText() );
370 -
371 - # If we got diff in the query, we want to see a diff page instead of the article.
372 - if ( $wgRequest->getCheck( 'diff' ) ) {
373 - wfDebug( __METHOD__ . ": showing diff page\n" );
374 - $this->showDiffPage();
375 - wfProfileOut( __METHOD__ );
376 -
377 - return;
378 - }
379 -
380 - $wgOut->setArticleFlag( true );
381 - # Allow frames by default
382 - $wgOut->allowClickjacking();
383 -
384 - $parserCache = ParserCache::singleton();
385 -
386 - $parserOptions = $this->mPage->getParserOptions();
387 - # Render printable version, use printable version cache
388 - if ( $wgOut->isPrintable() ) {
389 - $parserOptions->setIsPrintable( true );
390 - $parserOptions->setEditSection( false );
391 - } elseif ( $wgUseETag && !$this->getTitle()->quickUserCan( 'edit' ) ) {
392 - $parserOptions->setEditSection( false );
393 - }
394 -
395 - # Try client and file cache
396 - if ( $oldid === 0 && $this->mPage->checkTouched() ) {
397 - if ( $wgUseETag ) {
398 - $wgOut->setETag( $parserCache->getETag( $this, $parserOptions ) );
399 - }
400 -
401 - # Is it client cached?
402 - if ( $wgOut->checkLastModified( $this->mPage->getTouched() ) ) {
403 - wfDebug( __METHOD__ . ": done 304\n" );
404 - wfProfileOut( __METHOD__ );
405 -
406 - return;
407 - # Try file cache
408 - } elseif ( $wgUseFileCache && $this->tryFileCache() ) {
409 - wfDebug( __METHOD__ . ": done file cache\n" );
410 - # tell wgOut that output is taken care of
411 - $wgOut->disable();
412 - $this->mPage->viewUpdates();
413 - wfProfileOut( __METHOD__ );
414 -
415 - return;
416 - }
417 - }
418 -
419 - if ( !$wgUseETag && !$this->getTitle()->quickUserCan( 'edit' ) ) {
420 - $parserOptions->setEditSection( false );
421 - }
422 -
423 - # Should the parser cache be used?
424 - $useParserCache = $this->useParserCache( $oldid );
425 - wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
426 - if ( $wgUser->getStubThreshold() ) {
427 - wfIncrStats( 'pcache_miss_stub' );
428 - }
429 -
430 - $wasRedirected = $this->showRedirectedFromHeader();
431 - $this->showNamespaceHeader();
432 -
433 - # Iterate through the possible ways of constructing the output text.
434 - # Keep going until $outputDone is set, or we run out of things to do.
435 - $pass = 0;
436 - $outputDone = false;
437 - $this->mParserOutput = false;
438 -
439 - while ( !$outputDone && ++$pass ) {
440 - switch( $pass ) {
441 - case 1:
442 - wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$useParserCache ) );
443 - break;
444 - case 2:
445 - # Try the parser cache
446 - if ( $useParserCache ) {
447 - $this->mParserOutput = $parserCache->get( $this, $parserOptions );
448 -
449 - if ( $this->mParserOutput !== false ) {
450 - wfDebug( __METHOD__ . ": showing parser cache contents\n" );
451 - $wgOut->addParserOutput( $this->mParserOutput );
452 - # Ensure that UI elements requiring revision ID have
453 - # the correct version information.
454 - $wgOut->setRevisionId( $this->mPage->getLatest() );
455 - $outputDone = true;
456 - # Preload timestamp to avoid a DB hit
457 - if ( isset( $this->mParserOutput->mTimestamp ) ) {
458 - $this->mPage->setTimestamp( $this->mParserOutput->mTimestamp );
459 - }
460 - }
461 - }
462 - break;
463 - case 3:
464 - $text = $this->getContent();
465 - if ( $text === false || $this->mPage->getID() == 0 ) {
466 - wfDebug( __METHOD__ . ": showing missing article\n" );
467 - $this->showMissingArticle();
468 - wfProfileOut( __METHOD__ );
469 - return;
470 - }
471 -
472 - # Another whitelist check in case oldid is altering the title
473 - if ( !$this->getTitle()->userCanRead() ) {
474 - wfDebug( __METHOD__ . ": denied on secondary read check\n" );
475 - $wgOut->loginToUse();
476 - $wgOut->output();
477 - $wgOut->disable();
478 - wfProfileOut( __METHOD__ );
479 - return;
480 - }
481 -
482 - # Are we looking at an old revision
483 - if ( $oldid && !is_null( $this->mRevision ) ) {
484 - $this->setOldSubtitle( $oldid );
485 -
486 - if ( !$this->showDeletedRevisionHeader() ) {
487 - wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
488 - wfProfileOut( __METHOD__ );
489 - return;
490 - }
491 -
492 - # If this "old" version is the current, then try the parser cache...
493 - if ( $oldid === $this->mPage->getLatest() && $this->useParserCache( false ) ) {
494 - $this->mParserOutput = $parserCache->get( $this, $parserOptions );
495 - if ( $this->mParserOutput ) {
496 - wfDebug( __METHOD__ . ": showing parser cache for current rev permalink\n" );
497 - $wgOut->addParserOutput( $this->mParserOutput );
498 - $wgOut->setRevisionId( $this->mPage->getLatest() );
499 - $outputDone = true;
500 - break;
501 - }
502 - }
503 - }
504 -
505 - # Ensure that UI elements requiring revision ID have
506 - # the correct version information.
507 - $wgOut->setRevisionId( $this->getRevIdFetched() );
508 -
509 - # Pages containing custom CSS or JavaScript get special treatment
510 - if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
511 - wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
512 - $this->showCssOrJsPage();
513 - $outputDone = true;
514 - } elseif( !wfRunHooks( 'ArticleViewCustom', array( $this->mContent, $this->getTitle(), $wgOut ) ) ) {
515 - # Allow extensions do their own custom view for certain pages
516 - $outputDone = true;
517 - } else {
518 - $rt = Title::newFromRedirectArray( $text );
519 - if ( $rt ) {
520 - wfDebug( __METHOD__ . ": showing redirect=no page\n" );
521 - # Viewing a redirect page (e.g. with parameter redirect=no)
522 - # Don't append the subtitle if this was an old revision
523 - $wgOut->addHTML( $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() ) );
524 - # Parse just to get categories, displaytitle, etc.
525 - $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(), $parserOptions );
526 - $wgOut->addParserOutputNoText( $this->mParserOutput );
527 - $outputDone = true;
528 - }
529 - }
530 - break;
531 - case 4:
532 - # Run the parse, protected by a pool counter
533 - wfDebug( __METHOD__ . ": doing uncached parse\n" );
534 -
535 - $key = $parserCache->getKey( $this, $parserOptions );
536 - $poolArticleView = new PoolWorkArticleView( $this, $key, $useParserCache, $parserOptions );
537 -
538 - if ( !$poolArticleView->execute() ) {
539 - # Connection or timeout error
540 - wfProfileOut( __METHOD__ );
541 - return;
542 - } else {
543 - $outputDone = true;
544 - }
545 - break;
546 - # Should be unreachable, but just in case...
547 - default:
548 - break 2;
549 - }
550 - }
551 -
552 - # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
553 - if ( $this->mParserOutput ) {
554 - $titleText = $this->mParserOutput->getTitleText();
555 -
556 - if ( strval( $titleText ) !== '' ) {
557 - $wgOut->setPageTitle( $titleText );
558 - }
559 - }
560 -
561 - # For the main page, overwrite the <title> element with the con-
562 - # tents of 'pagetitle-view-mainpage' instead of the default (if
563 - # that's not empty).
564 - # This message always exists because it is in the i18n files
565 - if ( $this->getTitle()->isMainPage() ) {
566 - $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
567 - if ( !$msg->isDisabled() ) {
568 - $wgOut->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
569 - }
570 - }
571 -
572 - # Now that we've filled $this->mParserOutput, we know whether
573 - # there are any __NOINDEX__ tags on the page
574 - $policy = $this->getRobotPolicy( 'view' );
575 - $wgOut->setIndexPolicy( $policy['index'] );
576 - $wgOut->setFollowPolicy( $policy['follow'] );
577 -
578 - $this->showViewFooter();
579 - $this->mPage->viewUpdates();
580 - wfProfileOut( __METHOD__ );
 312+ throw MWException(__METHOD__ . " moved to PageView." );
581313 }
582314
583315 /**
@@ -631,354 +363,36 @@
632364 }
633365 }
634366
635 - /**
636 - * Get the robot policy to be used for the current view
637 - * @param $action String the action= GET parameter
638 - * @return Array the policy that should be set
639 - * TODO: actions other than 'view'
640 - */
641367 public function getRobotPolicy( $action ) {
642 - global $wgOut, $wgArticleRobotPolicies, $wgNamespaceRobotPolicies;
643 - global $wgDefaultRobotPolicy, $wgRequest;
644 -
645 - $ns = $this->getTitle()->getNamespace();
646 -
647 - if ( $ns == NS_USER || $ns == NS_USER_TALK ) {
648 - # Don't index user and user talk pages for blocked users (bug 11443)
649 - if ( !$this->getTitle()->isSubpage() ) {
650 - if ( Block::newFromTarget( null, $this->getTitle()->getText() ) instanceof Block ) {
651 - return array(
652 - 'index' => 'noindex',
653 - 'follow' => 'nofollow'
654 - );
655 - }
656 - }
657 - }
658 -
659 - if ( $this->mPage->getID() === 0 || $this->getOldID() ) {
660 - # Non-articles (special pages etc), and old revisions
661 - return array(
662 - 'index' => 'noindex',
663 - 'follow' => 'nofollow'
664 - );
665 - } elseif ( $wgOut->isPrintable() ) {
666 - # Discourage indexing of printable versions, but encourage following
667 - return array(
668 - 'index' => 'noindex',
669 - 'follow' => 'follow'
670 - );
671 - } elseif ( $wgRequest->getInt( 'curid' ) ) {
672 - # For ?curid=x urls, disallow indexing
673 - return array(
674 - 'index' => 'noindex',
675 - 'follow' => 'follow'
676 - );
677 - }
678 -
679 - # Otherwise, construct the policy based on the various config variables.
680 - $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
681 -
682 - if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
683 - # Honour customised robot policies for this namespace
684 - $policy = array_merge(
685 - $policy,
686 - self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
687 - );
688 - }
689 - if ( $this->getTitle()->canUseNoindex() && is_object( $this->mParserOutput ) && $this->mParserOutput->getIndexPolicy() ) {
690 - # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
691 - # a final sanity check that we have really got the parser output.
692 - $policy = array_merge(
693 - $policy,
694 - array( 'index' => $this->mParserOutput->getIndexPolicy() )
695 - );
696 - }
697 -
698 - if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
699 - # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
700 - $policy = array_merge(
701 - $policy,
702 - self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
703 - );
704 - }
705 -
706 - return $policy;
 368+ throw MWException(__METHOD__ . " moved to PageView." );
707369 }
708370
709 - /**
710 - * Converts a String robot policy into an associative array, to allow
711 - * merging of several policies using array_merge().
712 - * @param $policy Mixed, returns empty array on null/false/'', transparent
713 - * to already-converted arrays, converts String.
714 - * @return Array: 'index' => <indexpolicy>, 'follow' => <followpolicy>
715 - */
716371 public static function formatRobotPolicy( $policy ) {
717 - if ( is_array( $policy ) ) {
718 - return $policy;
719 - } elseif ( !$policy ) {
720 - return array();
721 - }
722 -
723 - $policy = explode( ',', $policy );
724 - $policy = array_map( 'trim', $policy );
725 -
726 - $arr = array();
727 - foreach ( $policy as $var ) {
728 - if ( in_array( $var, array( 'index', 'noindex' ) ) ) {
729 - $arr['index'] = $var;
730 - } elseif ( in_array( $var, array( 'follow', 'nofollow' ) ) ) {
731 - $arr['follow'] = $var;
732 - }
733 - }
734 -
735 - return $arr;
 372+ throw MWException(__METHOD__ . " moved to PageView." );
736373 }
737374
738 - /**
739 - * If this request is a redirect view, send "redirected from" subtitle to
740 - * $wgOut. Returns true if the header was needed, false if this is not a
741 - * redirect view. Handles both local and remote redirects.
742 - *
743 - * @return boolean
744 - */
745375 public function showRedirectedFromHeader() {
746 - global $wgOut, $wgRequest, $wgRedirectSources;
747 -
748 - $rdfrom = $wgRequest->getVal( 'rdfrom' );
749 -
750 - if ( isset( $this->mRedirectedFrom ) ) {
751 - // This is an internally redirected page view.
752 - // We'll need a backlink to the source page for navigation.
753 - if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) {
754 - $redir = Linker::link(
755 - $this->mRedirectedFrom,
756 - null,
757 - array(),
758 - array( 'redirect' => 'no' ),
759 - array( 'known', 'noclasses' )
760 - );
761 -
762 - $s = wfMsgExt( 'redirectedfrom', array( 'parseinline', 'replaceafter' ), $redir );
763 - $wgOut->setSubtitle( $s );
764 -
765 - // Set the fragment if one was specified in the redirect
766 - if ( strval( $this->getTitle()->getFragment() ) != '' ) {
767 - $fragment = Xml::escapeJsString( $this->getTitle()->getFragmentForURL() );
768 - $wgOut->addInlineScript( "redirectToFragment(\"$fragment\");" );
769 - }
770 -
771 - // Add a <link rel="canonical"> tag
772 - $wgOut->addLink( array( 'rel' => 'canonical',
773 - 'href' => $this->getTitle()->getLocalURL() )
774 - );
775 -
776 - return true;
777 - }
778 - } elseif ( $rdfrom ) {
779 - // This is an externally redirected view, from some other wiki.
780 - // If it was reported from a trusted site, supply a backlink.
781 - if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
782 - $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
783 - $s = wfMsgExt( 'redirectedfrom', array( 'parseinline', 'replaceafter' ), $redir );
784 - $wgOut->setSubtitle( $s );
785 -
786 - return true;
787 - }
788 - }
789 -
790 - return false;
 376+ throw MWException(__METHOD__ . " moved to PageView." );
791377 }
792378
793 - /**
794 - * Show a header specific to the namespace currently being viewed, like
795 - * [[MediaWiki:Talkpagetext]]. For Article::view().
796 - */
797379 public function showNamespaceHeader() {
798 - global $wgOut;
799 -
800 - if ( $this->getTitle()->isTalkPage() ) {
801 - if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
802 - $wgOut->wrapWikiMsg( "<div class=\"mw-talkpageheader\">\n$1\n</div>", array( 'talkpageheader' ) );
803 - }
804 - }
 380+ throw MWException(__METHOD__ . " moved to PageView." );
805381 }
806382
807 - /**
808 - * Show the footer section of an ordinary page view
809 - */
810383 public function showViewFooter() {
811 - global $wgOut, $wgUseTrackbacks;
812 -
813 - # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
814 - if ( $this->getTitle()->getNamespace() == NS_USER_TALK && IP::isValid( $this->getTitle()->getText() ) ) {
815 - $wgOut->addWikiMsg( 'anontalkpagetext' );
816 - }
817 -
818 - # If we have been passed an &rcid= parameter, we want to give the user a
819 - # chance to mark this new article as patrolled.
820 - $this->showPatrolFooter();
821 -
822 - # Trackbacks
823 - if ( $wgUseTrackbacks ) {
824 - $this->addTrackbacks();
825 - }
826 -
827 - wfRunHooks( 'ArticleViewFooter', array( $this ) );
828 -
 384+ throw MWException(__METHOD__ . " moved to PageView." );
829385 }
830386
831 - /**
832 - * If patrol is possible, output a patrol UI box. This is called from the
833 - * footer section of ordinary page views. If patrol is not possible or not
834 - * desired, does nothing.
835 - */
836387 public function showPatrolFooter() {
837 - global $wgOut, $wgRequest, $wgUser;
838 -
839 - $rcid = $wgRequest->getVal( 'rcid' );
840 -
841 - if ( !$rcid || !$this->getTitle()->quickUserCan( 'patrol' ) ) {
842 - return;
843 - }
844 -
845 - $token = $wgUser->editToken( $rcid );
846 - $wgOut->preventClickjacking();
847 -
848 - $wgOut->addHTML(
849 - "<div class='patrollink'>" .
850 - wfMsgHtml(
851 - 'markaspatrolledlink',
852 - Linker::link(
853 - $this->getTitle(),
854 - wfMsgHtml( 'markaspatrolledtext' ),
855 - array(),
856 - array(
857 - 'action' => 'markpatrolled',
858 - 'rcid' => $rcid,
859 - 'token' => $token,
860 - ),
861 - array( 'known', 'noclasses' )
862 - )
863 - ) .
864 - '</div>'
865 - );
 388+ throw MWException(__METHOD__ . " moved to PageView." );
866389 }
867390
868 - /**
869 - * Show the error text for a missing article. For articles in the MediaWiki
870 - * namespace, show the default message text. To be called from Article::view().
871 - */
872391 public function showMissingArticle() {
873 - global $wgOut, $wgRequest, $wgUser;
874 -
875 - # Show info in user (talk) namespace. Does the user exist? Is he blocked?
876 - if ( $this->getTitle()->getNamespace() == NS_USER || $this->getTitle()->getNamespace() == NS_USER_TALK ) {
877 - $parts = explode( '/', $this->getTitle()->getText() );
878 - $rootPart = $parts[0];
879 - $user = User::newFromName( $rootPart, false /* allow IP users*/ );
880 - $ip = User::isIP( $rootPart );
881 -
882 - if ( !$user->isLoggedIn() && !$ip ) { # User does not exist
883 - $wgOut->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
884 - array( 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ) );
885 - } elseif ( $user->isBlocked() ) { # Show log extract if the user is currently blocked
886 - LogEventsList::showLogExtract(
887 - $wgOut,
888 - 'block',
889 - $user->getUserPage()->getPrefixedText(),
890 - '',
891 - array(
892 - 'lim' => 1,
893 - 'showIfEmpty' => false,
894 - 'msgKey' => array(
895 - 'blocked-notice-logextract',
896 - $user->getName() # Support GENDER in notice
897 - )
898 - )
899 - );
900 - }
901 - }
902 -
903 - wfRunHooks( 'ShowMissingArticle', array( $this ) );
904 -
905 - # Show delete and move logs
906 - LogEventsList::showLogExtract( $wgOut, array( 'delete', 'move' ), $this->getTitle()->getPrefixedText(), '',
907 - array( 'lim' => 10,
908 - 'conds' => array( "log_action != 'revision'" ),
909 - 'showIfEmpty' => false,
910 - 'msgKey' => array( 'moveddeleted-notice' ) )
911 - );
912 -
913 - # Show error message
914 - $oldid = $this->getOldID();
915 - if ( $oldid ) {
916 - $text = wfMsgNoTrans( 'missing-article',
917 - $this->getTitle()->getPrefixedText(),
918 - wfMsgNoTrans( 'missingarticle-rev', $oldid ) );
919 - } elseif ( $this->getTitle()->getNamespace() === NS_MEDIAWIKI ) {
920 - // Use the default message text
921 - $text = $this->getTitle()->getDefaultMessageText();
922 - } else {
923 - $createErrors = $this->getTitle()->getUserPermissionsErrors( 'create', $wgUser );
924 - $editErrors = $this->getTitle()->getUserPermissionsErrors( 'edit', $wgUser );
925 - $errors = array_merge( $createErrors, $editErrors );
926 -
927 - if ( !count( $errors ) ) {
928 - $text = wfMsgNoTrans( 'noarticletext' );
929 - } else {
930 - $text = wfMsgNoTrans( 'noarticletext-nopermission' );
931 - }
932 - }
933 - $text = "<div class='noarticletext'>\n$text\n</div>";
934 -
935 - if ( !$this->mPage->hasViewableContent() ) {
936 - // If there's no backing content, send a 404 Not Found
937 - // for better machine handling of broken links.
938 - $wgRequest->response()->header( "HTTP/1.1 404 Not Found" );
939 - }
940 -
941 - $wgOut->addWikiText( $text );
 392+ throw MWException(__METHOD__ . " moved to PageView." );
942393 }
943394
944 - /**
945 - * If the revision requested for view is deleted, check permissions.
946 - * Send either an error message or a warning header to $wgOut.
947 - *
948 - * @return boolean true if the view is allowed, false if not.
949 - */
950395 public function showDeletedRevisionHeader() {
951 - global $wgOut, $wgRequest;
952 -
953 - if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
954 - // Not deleted
955 - return true;
956 - }
957 -
958 - // If the user is not allowed to see it...
959 - if ( !$this->mRevision->userCan( Revision::DELETED_TEXT ) ) {
960 - $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
961 - 'rev-deleted-text-permission' );
962 -
963 - return false;
964 - // If the user needs to confirm that they want to see it...
965 - } elseif ( $wgRequest->getInt( 'unhide' ) != 1 ) {
966 - # Give explanation and add a link to view the revision...
967 - $oldid = intval( $this->getOldID() );
968 - $link = $this->getTitle()->getFullUrl( "oldid={$oldid}&unhide=1" );
969 - $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
970 - 'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
971 - $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
972 - array( $msg, $link ) );
973 -
974 - return false;
975 - // We are allowed to see...
976 - } else {
977 - $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
978 - 'rev-suppressed-text-view' : 'rev-deleted-text-view';
979 - $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
980 -
981 - return true;
982 - }
 396+ throw MWException(__METHOD__ . " moved to PageView." );
983397 }
984398
985399 /**
Index: branches/pageoutput/includes/parser/ParserCache.php
@@ -41,15 +41,15 @@
4242 }
4343
4444 /**
45 - * @param $article Article
 45+ * @param $page Page
4646 * @param $hash string
4747 * @return mixed|string
4848 */
49 - protected function getParserOutputKey( $article, $hash ) {
 49+ protected function getParserOutputKey( Page $page, $hash ) {
5050 global $wgRequest;
5151
5252 // idhash seem to mean 'page id' + 'rendering hash' (r3710)
53 - $pageid = $article->getID();
 53+ $pageid = $page->getID();
5454 $renderkey = (int)($wgRequest->getVal('action') == 'render');
5555
5656 $key = wfMemcKey( 'pcache', 'idhash', "{$pageid}-{$renderkey}!{$hash}" );
@@ -57,53 +57,53 @@
5858 }
5959
6060 /**
61 - * @param $article Article
 61+ * @param $page Page
6262 * @return mixed|string
6363 */
64 - protected function getOptionsKey( $article ) {
65 - $pageid = $article->getID();
 64+ protected function getOptionsKey( Page $page ) {
 65+ $pageid = $page->getID();
6666 return wfMemcKey( 'pcache', 'idoptions', "{$pageid}" );
6767 }
6868
6969 /**
70 - * Provides an E-Tag suitable for the whole page. Note that $article
 70+ * Provides an E-Tag suitable for the whole page. Note that $page
7171 * is just the main wikitext. The E-Tag has to be unique to the whole
7272 * page, even if the article itself is the same, so it uses the
7373 * complete set of user options. We don't want to use the preference
7474 * of a different user on a message just because it wasn't used in
75 - * $article. For example give a Chinese interface to a user with
 75+ * $page. For example give a Chinese interface to a user with
7676 * English preferences. That's why we take into account *all* user
7777 * options. (r70809 CR)
7878 *
79 - * @param $article Article
 79+ * @param $page Page
8080 * @param $popts ParserOptions
8181 */
82 - function getETag( $article, $popts ) {
83 - return 'W/"' . $this->getParserOutputKey( $article,
 82+ function getETag( Page $page, $popts ) {
 83+ return 'W/"' . $this->getParserOutputKey( $page,
8484 $popts->optionsHash( ParserOptions::legacyOptions() ) ) .
85 - "--" . $article->getTouched() . '"';
 85+ "--" . $page->getTouched() . '"';
8686 }
8787
8888 /**
8989 * Retrieve the ParserOutput from ParserCache, even if it's outdated.
90 - * @param $article Article
 90+ * @param $page Page
9191 * @param $popts ParserOptions
9292 * @return ParserOutput|false
9393 */
94 - public function getDirty( $article, $popts ) {
95 - $value = $this->get( $article, $popts, true );
 94+ public function getDirty( Page $page, $popts ) {
 95+ $value = $this->get( $page, $popts, true );
9696 return is_object( $value ) ? $value : false;
9797 }
9898
9999 /**
100100 * Used to provide a unique id for the PoolCounter.
101101 * It would be preferable to have this code in get()
102 - * instead of having Article looking in our internals.
 102+ * instead of having Page looking in our internals.
103103 *
104 - * @param $article Article
 104+ * @param $page Page
105105 * @param $popts ParserOptions
106106 */
107 - public function getKey( $article, $popts, $useOutdated = true ) {
 107+ public function getKey( $page, $popts, $useOutdated = true ) {
108108 global $wgCacheEpoch;
109109
110110 if( $popts instanceof User ) {
@@ -112,12 +112,12 @@
113113 }
114114
115115 // Determine the options which affect this article
116 - $optionsKey = $this->mMemc->get( $this->getOptionsKey( $article ) );
 116+ $optionsKey = $this->mMemc->get( $this->getOptionsKey( $page ) );
117117 if ( $optionsKey != false ) {
118 - if ( !$useOutdated && $optionsKey->expired( $article->getTouched() ) ) {
 118+ if ( !$useOutdated && $optionsKey->expired( $page->getTouched() ) ) {
119119 wfIncrStats( "pcache_miss_expired" );
120120 $cacheTime = $optionsKey->getCacheTime();
121 - wfDebug( "Parser options key expired, touched " . $article->getTouched() . ", epoch $wgCacheEpoch, cached $cacheTime\n" );
 121+ wfDebug( "Parser options key expired, touched " . $page->getTouched() . ", epoch $wgCacheEpoch, cached $cacheTime\n" );
122122 return false;
123123 }
124124
@@ -130,33 +130,33 @@
131131 $usedOptions = ParserOptions::legacyOptions();
132132 }
133133
134 - return $this->getParserOutputKey( $article, $popts->optionsHash( $usedOptions ) );
 134+ return $this->getParserOutputKey( $page, $popts->optionsHash( $usedOptions ) );
135135 }
136136
137137 /**
138138 * Retrieve the ParserOutput from ParserCache.
139139 * false if not found or outdated.
140140 *
141 - * @param $article Article
 141+ * @param $page Page
142142 * @param $popts ParserOptions
143143 * @param $useOutdated
144144 *
145145 * @return ParserOutput|false
146146 */
147 - public function get( $article, $popts, $useOutdated = false ) {
 147+ public function get( Page $page, $popts, $useOutdated = false ) {
148148 global $wgCacheEpoch;
149149 wfProfileIn( __METHOD__ );
150150
151 - $canCache = $article->checkTouched();
 151+ $canCache = $page->checkTouched();
152152 if ( !$canCache ) {
153153 // It's a redirect now
154154 wfProfileOut( __METHOD__ );
155155 return false;
156156 }
157157
158 - $touched = $article->getTouched();
 158+ $touched = $page->getTouched();
159159
160 - $parserOutputKey = $this->getKey( $article, $popts, $useOutdated );
 160+ $parserOutputKey = $this->getKey( $page, $popts, $useOutdated );
161161 if ( $parserOutputKey === false ) {
162162 wfProfileOut( __METHOD__ );
163163 return false;
@@ -165,7 +165,7 @@
166166 $value = $this->mMemc->get( $parserOutputKey );
167167 if ( self::try116cache && !$value && strpos( $value, '*' ) !== -1 ) {
168168 wfDebug( "New format parser cache miss.\n" );
169 - $parserOutputKey = $this->getParserOutputKey( $article, $popts->optionsHash( ParserOptions::legacyOptions() ) );
 169+ $parserOutputKey = $this->getParserOutputKey( $page, $popts->optionsHash( ParserOptions::legacyOptions() ) );
170170 $value = $this->mMemc->get( $parserOutputKey );
171171 }
172172 if ( !$value ) {
@@ -192,10 +192,10 @@
193193
194194 /**
195195 * @param $parserOutput ParserOutput
196 - * @param $article Article
 196+ * @param $page Page
197197 * @param $popts ParserOptions
198198 */
199 - public function save( $parserOutput, $article, $popts ) {
 199+ public function save( $parserOutput, Page $page, $popts ) {
200200 $expire = $parserOutput->getCacheExpiry();
201201
202202 if( $expire > 0 ) {
@@ -210,11 +210,11 @@
211211
212212 $optionsKey->setContainsOldMagic( $parserOutput->containsOldMagic() );
213213
214 - $parserOutputKey = $this->getParserOutputKey( $article,
 214+ $parserOutputKey = $this->getParserOutputKey( $page,
215215 $popts->optionsHash( $optionsKey->mUsedOptions ) );
216216
217217 // Save the timestamp so that we don't have to load the revision row on view
218 - $parserOutput->mTimestamp = $article->getTimestamp();
 218+ $parserOutput->mTimestamp = $page->getTimestamp();
219219
220220 $parserOutput->mText .= "\n<!-- Saved in parser cache with key $parserOutputKey and timestamp $now -->\n";
221221 wfDebug( "Saved in parser cache with key $parserOutputKey and timestamp $now\n" );
@@ -223,7 +223,7 @@
224224 $this->mMemc->set( $parserOutputKey, $parserOutput, $expire );
225225
226226 // ...and its pointer
227 - $this->mMemc->set( $this->getOptionsKey( $article ), $optionsKey, $expire );
 227+ $this->mMemc->set( $this->getOptionsKey( $page ), $optionsKey, $expire );
228228 } else {
229229 wfDebug( "Parser output was marked as uncacheable and has not been saved.\n" );
230230 }
Index: branches/pageoutput/includes/AutoLoader.php
@@ -271,6 +271,10 @@
272272 'UnwatchAction' => 'includes/actions/WatchAction.php',
273273 'WatchAction' => 'includes/actions/WatchAction.php',
274274
 275+ # includes/view
 276+ 'PageView' => 'includes/view/PageView.php',
 277+ 'ArticlePageView' => 'includes/view/ArticlePageView.php',
 278+
275279 # includes/api
276280 'ApiBase' => 'includes/api/ApiBase.php',
277281 'ApiBlock' => 'includes/api/ApiBlock.php',
Index: branches/pageoutput/includes/Wiki.php
@@ -137,6 +137,7 @@
138138 $output = $this->context->getOutput();
139139 $user = $this->context->getUser();
140140
 141+ // @fixme Move this to PageView; Implement in basic since this applies to most pages (though there may be some exceptions)
141142 if ( $request->getVal( 'printable' ) === 'yes' ) {
142143 $output->setPrintable();
143144 }
@@ -217,30 +218,40 @@
218219 }
219220 // Special pages
220221 } elseif ( NS_SPECIAL == $title->getNamespace() ) {
 222+ // @todo @fixme SpecialPage should be a PageView and handled with the rest of the PageView code
221223 $pageView = true;
222224 // Actions that need to be made when we have a special pages
223225 SpecialPageFactory::executePath( $title, $this->context );
224226 } else {
225 - // ...otherwise treat it as an article view. The article
226 - // may be a redirect to another article or URL.
227 - $article = $this->initializeArticle();
228 - if ( is_object( $article ) ) {
 227+ $view = PageView::factory( $this->context );
 228+ if ( is_object( $view ) ) {
229229 $pageView = true;
230 - /**
231 - * $wgArticle is deprecated, do not use it. This will possibly be removed
232 - * entirely in 1.20 or 1.21
233 - * @deprecated since 1.18
234 - */
235 - global $wgArticle;
236 - $wgArticle = $article;
237 -
238 - $this->performAction( $article );
239 - } elseif ( is_string( $article ) ) {
240 - $output->redirect( $article );
 230+ $view->render();
241231 } else {
242232 wfProfileOut( __METHOD__ );
243 - throw new MWException( "Shouldn't happen: MediaWiki::initializeArticle() returned neither an object nor a URL" );
 233+ throw new MWException( "Shouldn't happen: PageView::newFromContext did not return an object" );
244234 }
 235+
 236+# // ...otherwise treat it as an article view. The article
 237+# // may be a redirect to another article or URL.
 238+# $article = $this->initializeArticle();
 239+# if ( is_object( $article ) ) {
 240+# $pageView = true;
 241+# /**
 242+# * $wgArticle is deprecated, do not use it. This will possibly be removed
 243+# * entirely in 1.20 or 1.21
 244+# * @deprecated since 1.18
 245+# */
 246+# global $wgArticle;
 247+# $wgArticle = $article;
 248+#
 249+# $this->performAction( $article );
 250+# } elseif ( is_string( $article ) ) {
 251+# $output->redirect( $article );
 252+# } else {
 253+# wfProfileOut( __METHOD__ );
 254+# throw new MWException( "Shouldn't happen: MediaWiki::initializeArticle() returned neither an object nor a URL" );
 255+# }
245256 }
246257
247258 if ( $pageView ) {
Index: branches/pageoutput/includes/WikiPage.php
@@ -2562,7 +2562,7 @@
25632563 * @return mixed ParserOptions object or boolean false
25642564 */
25652565 public function getParserOptions( $canonical = false ) {
2566 - global $wgUser, $wgLanguageCode;
 2566+ global $wgUser, $wgLanguageCode; // @fixme Eliminate $wgUser
25672567
25682568 if ( !$this->mParserOptions || $canonical ) {
25692569 $user = !$canonical ? $wgUser : new User;
Index: branches/pageoutput/includes/view/ArticlePageView.php
@@ -0,0 +1,656 @@
 2+<?php
 3+
 4+class ArticlePageView extends PageView {
 5+
 6+ private $mOldId, $mRedirectedFrom, $mRedirectUrl, $mPage, $mParserOutput;
 7+
 8+ function getTabs() {
 9+ }
 10+
 11+ /** **/
 12+
 13+ public function getPage() {
 14+ if ( is_null( $this->mPage ) ) {
 15+ $this->mPage = WikiPage::factory( $this->getTitle() );
 16+ }
 17+ return $this->mPage;
 18+ }
 19+
 20+ /**
 21+ * @return int The oldid of the article that is to be shown, 0 for the
 22+ * current revision
 23+ */
 24+ public function getOldID() {
 25+ // @fixme Merge getOldID and getOldIDFromRequest together for PageView
 26+ if ( is_null( $this->mOldId ) ) {
 27+ $this->mOldId = $this->getOldIDFromRequest();
 28+ }
 29+
 30+ return $this->mOldId;
 31+ }
 32+
 33+ /**
 34+ * Get the robot policy to be used for the current view
 35+ * @return Array the policy that should be set
 36+ */
 37+ public function getRobotPolicy() {
 38+ global $wgArticleRobotPolicies, $wgNamespaceRobotPolicies;
 39+ global $wgDefaultRobotPolicy;
 40+
 41+ $ns = $this->getTitle()->getNamespace();
 42+
 43+ if ( $ns == NS_USER || $ns == NS_USER_TALK ) {
 44+ # Don't index user and user talk pages for blocked users (bug 11443)
 45+ if ( !$this->getTitle()->isSubpage() ) {
 46+ if ( Block::newFromTarget( null, $this->getTitle()->getText() ) instanceof Block ) {
 47+ return array(
 48+ 'index' => 'noindex',
 49+ 'follow' => 'nofollow'
 50+ );
 51+ }
 52+ }
 53+ }
 54+
 55+ if ( $this->getPage()->getID() === 0 || $this->getOldID() ) {
 56+ # Non-articles (special pages etc), and old revisions
 57+ return array(
 58+ 'index' => 'noindex',
 59+ 'follow' => 'nofollow'
 60+ );
 61+ } elseif ( $this->getOutput()->isPrintable() ) {
 62+ # Discourage indexing of printable versions, but encourage following
 63+ return array(
 64+ 'index' => 'noindex',
 65+ 'follow' => 'follow'
 66+ );
 67+ } elseif ( $this->getRequest()->getInt( 'curid' ) ) {
 68+ # For ?curid=x urls, disallow indexing
 69+ return array(
 70+ 'index' => 'noindex',
 71+ 'follow' => 'follow'
 72+ );
 73+ }
 74+
 75+ # Otherwise, construct the policy based on the various config variables.
 76+ $policy = self::formatRobotPolicy( $wgDefaultRobotPolicy );
 77+
 78+ if ( isset( $wgNamespaceRobotPolicies[$ns] ) ) {
 79+ # Honour customised robot policies for this namespace
 80+ $policy = array_merge(
 81+ $policy,
 82+ self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
 83+ );
 84+ }
 85+ if ( $this->getTitle()->canUseNoindex() && is_object( $this->mParserOutput ) && $this->mParserOutput->getIndexPolicy() ) {
 86+ # __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
 87+ # a final sanity check that we have really got the parser output.
 88+ $policy = array_merge(
 89+ $policy,
 90+ array( 'index' => $this->mParserOutput->getIndexPolicy() )
 91+ );
 92+ }
 93+
 94+ if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
 95+ # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
 96+ $policy = array_merge(
 97+ $policy,
 98+ self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
 99+ );
 100+ }
 101+
 102+ return $policy;
 103+ }
 104+
 105+ /**
 106+ * Converts a String robot policy into an associative array, to allow
 107+ * merging of several policies using array_merge().
 108+ * @param $policy Mixed, returns empty array on null/false/'', transparent
 109+ * to already-converted arrays, converts String.
 110+ * @return Array: 'index' => <indexpolicy>, 'follow' => <followpolicy>
 111+ */
 112+ public static function formatRobotPolicy( $policy ) {
 113+ if ( is_array( $policy ) ) {
 114+ return $policy;
 115+ } elseif ( !$policy ) {
 116+ return array();
 117+ }
 118+
 119+ $policy = explode( ',', $policy );
 120+ $policy = array_map( 'trim', $policy );
 121+
 122+ $arr = array();
 123+ foreach ( $policy as $var ) {
 124+ if ( in_array( $var, array( 'index', 'noindex' ) ) ) {
 125+ $arr['index'] = $var;
 126+ } elseif ( in_array( $var, array( 'follow', 'nofollow' ) ) ) {
 127+ $arr['follow'] = $var;
 128+ }
 129+ }
 130+
 131+ return $arr;
 132+ }
 133+
 134+ /**
 135+ * Sets $this->mRedirectUrl to a correct URL if the query parameters are incorrect
 136+ *
 137+ * @return int The old id for the request
 138+ */
 139+ public function getOldIDFromRequest() {
 140+ $this->mRedirectUrl = false;
 141+
 142+ $request = $this->getRequest();
 143+
 144+ $oldid = $request->getVal( 'oldid' );
 145+
 146+ if ( isset( $oldid ) ) {
 147+ $oldid = intval( $oldid );
 148+ if ( $rRequest->getVal( 'direction' ) == 'next' ) {
 149+ $nextid = $this->getTitle()->getNextRevisionID( $oldid );
 150+ if ( $nextid ) {
 151+ $oldid = $nextid;
 152+ } else {
 153+ $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
 154+ }
 155+ } elseif ( $request->getVal( 'direction' ) == 'prev' ) {
 156+ $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
 157+ if ( $previd ) {
 158+ $oldid = $previd;
 159+ }
 160+ }
 161+ }
 162+
 163+ if ( !$oldid ) {
 164+ $oldid = 0;
 165+ }
 166+
 167+ return $oldid;
 168+ }
 169+
 170+ /** PageView render and sub-areas of the rendering process **/
 171+
 172+ function render() {
 173+ global $wgParser;
 174+ global $wgUseFileCache, $wgUseETag;
 175+
 176+ wfProfileIn( __METHOD__ );
 177+
 178+ $request = $this->getRequest();
 179+ $out = $this->getOutput();
 180+ $user = $this->getUser();
 181+
 182+ # Get variables from query string
 183+ $oldid = $this->getOldID();
 184+
 185+ # getOldID may want us to redirect somewhere else
 186+ if ( $this->mRedirectUrl ) {
 187+ $out->redirect( $this->mRedirectUrl );
 188+ wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
 189+ wfProfileOut( __METHOD__ );
 190+
 191+ return;
 192+ }
 193+
 194+ # Set page title (may be overridden by DISPLAYTITLE)
 195+ $out->setPageTitle( $this->getTitle()->getPrefixedText() );
 196+
 197+ # If we got diff in the query, we want to see a diff page instead of the article.
 198+ if ( $request->getCheck( 'diff' ) ) {
 199+ wfDebug( __METHOD__ . ": showing diff page\n" );
 200+ $this->showDiffPage();
 201+ wfProfileOut( __METHOD__ );
 202+
 203+ return;
 204+ }
 205+
 206+ $out->setArticleFlag( true );
 207+ # Allow frames by default
 208+ $out->allowClickjacking();
 209+
 210+ $parserCache = ParserCache::singleton();
 211+
 212+ $parserOptions = $this->getPage()->getParserOptions();
 213+ # Render printable version, use printable version cache
 214+ // @fixme printable check should be moved from Wiki.php to PageView
 215+ if ( $out->isPrintable() ) {
 216+ $parserOptions->setIsPrintable( true );
 217+ $parserOptions->setEditSection( false );
 218+ } elseif ( $wgUseETag && !$this->getTitle()->quickUserCan( 'edit' ) ) {
 219+ $parserOptions->setEditSection( false );
 220+ }
 221+
 222+ # Try client and file cache
 223+ if ( $oldid === 0 && $this->getPage()->checkTouched() ) {
 224+ if ( $wgUseETag ) {
 225+ $out->setETag( $parserCache->getETag( $this->getPage(), $parserOptions ) );
 226+ }
 227+
 228+ # Is it client cached?
 229+ if ( $out->checkLastModified( $this->getPage()->getTouched() ) ) {
 230+ wfDebug( __METHOD__ . ": done 304\n" );
 231+ wfProfileOut( __METHOD__ );
 232+
 233+ return;
 234+ # Try file cache
 235+ } elseif ( $wgUseFileCache && $this->tryFileCache() ) {
 236+ wfDebug( __METHOD__ . ": done file cache\n" );
 237+ # tell wgOut that output is taken care of
 238+ $out->disable();
 239+ $this->getPage()->viewUpdates();
 240+ wfProfileOut( __METHOD__ );
 241+
 242+ return;
 243+ }
 244+ }
 245+
 246+ if ( !$wgUseETag && !$this->getTitle()->quickUserCan( 'edit' ) ) {
 247+ $parserOptions->setEditSection( false );
 248+ }
 249+
 250+ # Should the parser cache be used?
 251+ $useParserCache = $this->getPage()->isParserCacheUsed( $user, $oldid );
 252+ wfDebug( __METHOD__ . ' using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
 253+ if ( $user->getStubThreshold() ) {
 254+ wfIncrStats( 'pcache_miss_stub' );
 255+ }
 256+
 257+ $wasRedirected = $this->showRedirectedFromHeader();
 258+ $this->showNamespaceHeader();
 259+
 260+ # Iterate through the possible ways of constructing the output text.
 261+ # Keep going until $outputDone is set, or we run out of things to do.
 262+ $pass = 0;
 263+ $outputDone = false;
 264+ $this->mParserOutput = false;
 265+
 266+ while ( !$outputDone && ++$pass ) {
 267+ switch( $pass ) {
 268+ case 1:
 269+ wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$useParserCache ) ); // @fixme
 270+ break;
 271+ case 2:
 272+ # Try the parser cache
 273+ if ( $useParserCache ) {
 274+ $this->mParserOutput = $parserCache->get( $this->getPage(), $parserOptions );
 275+
 276+ if ( $this->mParserOutput !== false ) {
 277+ wfDebug( __METHOD__ . ": showing parser cache contents\n" );
 278+ $out->addParserOutput( $this->mParserOutput );
 279+ # Ensure that UI elements requiring revision ID have
 280+ # the correct version information.
 281+ $out->setRevisionId( $this->getPage()->getLatest() );
 282+ $outputDone = true;
 283+ # Preload timestamp to avoid a DB hit
 284+ if ( isset( $this->mParserOutput->mTimestamp ) ) {
 285+ $this->getPage()->setTimestamp( $this->mParserOutput->mTimestamp );
 286+ }
 287+ }
 288+ }
 289+ break;
 290+ case 3:
 291+ $text = $this->getContent();
 292+ if ( $text === false || $this->getPage()->getID() == 0 ) {
 293+ wfDebug( __METHOD__ . ": showing missing article\n" );
 294+ $this->showMissingArticle();
 295+ wfProfileOut( __METHOD__ );
 296+ return;
 297+ }
 298+
 299+ # Another whitelist check in case oldid is altering the title
 300+ if ( !$this->getTitle()->userCanRead() ) {
 301+ wfDebug( __METHOD__ . ": denied on secondary read check\n" );
 302+ $out->loginToUse();
 303+ $out->output();
 304+ $out->disable();
 305+ wfProfileOut( __METHOD__ );
 306+ return;
 307+ }
 308+
 309+ # Are we looking at an old revision
 310+ if ( $oldid && !is_null( $this->mRevision ) ) {
 311+ $this->setOldSubtitle( $oldid );
 312+
 313+ if ( !$this->showDeletedRevisionHeader() ) {
 314+ wfDebug( __METHOD__ . ": cannot view deleted revision\n" );
 315+ wfProfileOut( __METHOD__ );
 316+ return;
 317+ }
 318+
 319+ # If this "old" version is the current, then try the parser cache...
 320+ if ( $oldid === $this->getPage()->getLatest() && $this->getPage()->isParserCacheUsed( $user, false ) ) {
 321+ $this->mParserOutput = $parserCache->get( $this->getPage(), $parserOptions );
 322+ if ( $this->mParserOutput ) {
 323+ wfDebug( __METHOD__ . ": showing parser cache for current rev permalink\n" );
 324+ $out->addParserOutput( $this->mParserOutput );
 325+ $out->setRevisionId( $this->getPage()->getLatest() );
 326+ $outputDone = true;
 327+ break;
 328+ }
 329+ }
 330+ }
 331+
 332+ # Ensure that UI elements requiring revision ID have
 333+ # the correct version information.
 334+ $out->setRevisionId( $this->getRevIdFetched() );
 335+
 336+ # Pages containing custom CSS or JavaScript get special treatment
 337+ if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
 338+ wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
 339+ $this->showCssOrJsPage();
 340+ $outputDone = true;
 341+ } elseif( !wfRunHooks( 'ArticleViewCustom', array( $this->mContent, $this->getTitle(), $out ) ) ) { // @fixme
 342+ # Allow extensions do their own custom view for certain pages
 343+ $outputDone = true;
 344+ } else {
 345+ $rt = Title::newFromRedirectArray( $text );
 346+ if ( $rt ) {
 347+ wfDebug( __METHOD__ . ": showing redirect=no page\n" );
 348+ # Viewing a redirect page (e.g. with parameter redirect=no)
 349+ # Don't append the subtitle if this was an old revision
 350+ $out->addHTML( $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() ) );
 351+ # Parse just to get categories, displaytitle, etc.
 352+ $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(), $parserOptions );
 353+ $out->addParserOutputNoText( $this->mParserOutput );
 354+ $outputDone = true;
 355+ }
 356+ }
 357+ break;
 358+ case 4:
 359+ # Run the parse, protected by a pool counter
 360+ wfDebug( __METHOD__ . ": doing uncached parse\n" );
 361+
 362+ $key = $parserCache->getKey( $this->getPage(), $parserOptions );
 363+ $poolArticleView = new PoolWorkArticleView( $this, $key, $useParserCache, $parserOptions ); // @fixme
 364+
 365+ if ( !$poolArticleView->execute() ) {
 366+ # Connection or timeout error
 367+ wfProfileOut( __METHOD__ );
 368+ return;
 369+ } else {
 370+ $outputDone = true;
 371+ }
 372+ break;
 373+ # Should be unreachable, but just in case...
 374+ default:
 375+ break 2;
 376+ }
 377+ }
 378+
 379+ # Adjust the title if it was set by displaytitle, -{T|}- or language conversion
 380+ if ( $this->mParserOutput ) {
 381+ $titleText = $this->mParserOutput->getTitleText();
 382+
 383+ if ( strval( $titleText ) !== '' ) {
 384+ $out->setPageTitle( $titleText );
 385+ }
 386+ }
 387+
 388+ # For the main page, overwrite the <title> element with the con-
 389+ # tents of 'pagetitle-view-mainpage' instead of the default (if
 390+ # that's not empty).
 391+ # This message always exists because it is in the i18n files
 392+ if ( $this->getTitle()->isMainPage() ) {
 393+ $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
 394+ if ( !$msg->isDisabled() ) {
 395+ $out->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
 396+ }
 397+ }
 398+
 399+ # Now that we've filled $this->mParserOutput, we know whether
 400+ # there are any __NOINDEX__ tags on the page
 401+ $policy = $this->getRobotPolicy();
 402+ $out->setIndexPolicy( $policy['index'] );
 403+ $out->setFollowPolicy( $policy['follow'] );
 404+
 405+ $this->showViewFooter();
 406+ $this->getPage()->viewUpdates();
 407+ wfProfileOut( __METHOD__ );
 408+ }
 409+
 410+ /**
 411+ * If this request is a redirect view, send "redirected from" subtitle to
 412+ * OutputPage. Returns true if the header was needed, false if this is not a
 413+ * redirect view. Handles both local and remote redirects.
 414+ *
 415+ * @return boolean
 416+ */
 417+ public function showRedirectedFromHeader() {
 418+ global $wgRedirectSources;
 419+
 420+ $rdfrom = $this->getRequest()->getVal( 'rdfrom' );
 421+
 422+ $out = $this->getOutput();
 423+
 424+ if ( isset( $this->mRedirectedFrom ) ) {
 425+ // This is an internally redirected page view.
 426+ // We'll need a backlink to the source page for navigation.
 427+ if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) { // @fixme
 428+ $redir = Linker::link(
 429+ $this->mRedirectedFrom,
 430+ null,
 431+ array(),
 432+ array( 'redirect' => 'no' ),
 433+ array( 'known', 'noclasses' )
 434+ );
 435+
 436+ $s = wfMsgExt( 'redirectedfrom', array( 'parseinline', 'replaceafter' ), $redir );
 437+ $out->setSubtitle( $s );
 438+
 439+ // Set the fragment if one was specified in the redirect
 440+ if ( strval( $this->getTitle()->getFragment() ) != '' ) {
 441+ $fragment = Xml::escapeJsString( $this->getTitle()->getFragmentForURL() );
 442+ $out->addInlineScript( "redirectToFragment(\"$fragment\");" );
 443+ }
 444+
 445+ // Add a <link rel="canonical"> tag
 446+ $out->addLink( array( 'rel' => 'canonical',
 447+ 'href' => $this->getTitle()->getLocalURL() )
 448+ );
 449+
 450+ return true;
 451+ }
 452+ } elseif ( $rdfrom ) {
 453+ // This is an externally redirected view, from some other wiki.
 454+ // If it was reported from a trusted site, supply a backlink.
 455+ if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
 456+ $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
 457+ $s = wfMsgExt( 'redirectedfrom', array( 'parseinline', 'replaceafter' ), $redir );
 458+ $out->setSubtitle( $s );
 459+
 460+ return true;
 461+ }
 462+ }
 463+
 464+ return false;
 465+ }
 466+
 467+ /**
 468+ * Show a header specific to the namespace currently being viewed, like
 469+ * [[MediaWiki:Talkpagetext]]..
 470+ */
 471+ public function showNamespaceHeader() {
 472+ if ( $this->getTitle()->isTalkPage() ) {
 473+ if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
 474+ $this->getOutput()->wrapWikiMsg( "<div class=\"mw-talkpageheader\">\n$1\n</div>", array( 'talkpageheader' ) );
 475+ }
 476+ }
 477+ }
 478+
 479+ /**
 480+ * Show the footer section of an ordinary page view
 481+ */
 482+ public function showViewFooter() {
 483+ global $wgUseTrackbacks;
 484+
 485+ # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
 486+ if ( $this->getTitle()->getNamespace() == NS_USER_TALK && IP::isValid( $this->getTitle()->getText() ) ) {
 487+ $this->getOutput()->addWikiMsg( 'anontalkpagetext' );
 488+ }
 489+
 490+ # If we have been passed an &rcid= parameter, we want to give the user a
 491+ # chance to mark this new article as patrolled.
 492+ $this->showPatrolFooter();
 493+
 494+ # Trackbacks
 495+ if ( $wgUseTrackbacks ) {
 496+ $this->addTrackbacks();
 497+ }
 498+
 499+ wfRunHooks( 'ArticleViewFooter', array( $this ) ); // @fixme
 500+
 501+ }
 502+
 503+ /**
 504+ * If patrol is possible, output a patrol UI box. This is called from the
 505+ * footer section of ordinary page views. If patrol is not possible or not
 506+ * desired, does nothing.
 507+ */
 508+ public function showPatrolFooter() {
 509+ $rcid = $this->getRequest()->getVal( 'rcid' );
 510+
 511+ if ( !$rcid || !$this->getTitle()->quickUserCan( 'patrol' ) ) {
 512+ return;
 513+ }
 514+
 515+ $token = $this->getUser()->editToken( $rcid );
 516+ $this->getOutput()->preventClickjacking();
 517+
 518+ $this->getOutput()->addHTML(
 519+ "<div class='patrollink'>" .
 520+ wfMsgHtml(
 521+ 'markaspatrolledlink',
 522+ Linker::link(
 523+ $this->getTitle(),
 524+ wfMsgHtml( 'markaspatrolledtext' ),
 525+ array(),
 526+ array(
 527+ 'action' => 'markpatrolled',
 528+ 'rcid' => $rcid,
 529+ 'token' => $token,
 530+ ),
 531+ array( 'known', 'noclasses' )
 532+ )
 533+ ) .
 534+ '</div>'
 535+ );
 536+ }
 537+
 538+ /**
 539+ * Show the error text for a missing article. For articles in the MediaWiki
 540+ * namespace, show the default message text.
 541+ */
 542+ public function showMissingArticle() {
 543+ $out = $this->getOutput();
 544+ $user = $this->getUser();
 545+
 546+ # Show info in user (talk) namespace. Does the user exist? Is he blocked?
 547+ if ( $this->getTitle()->getNamespace() == NS_USER || $this->getTitle()->getNamespace() == NS_USER_TALK ) {
 548+ $parts = explode( '/', $this->getTitle()->getText() );
 549+ $rootPart = $parts[0];
 550+ $user = User::newFromName( $rootPart, false /* allow IP users*/ );
 551+ $ip = User::isIP( $rootPart );
 552+
 553+ if ( !$user->isLoggedIn() && !$ip ) { # User does not exist
 554+ $out->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
 555+ array( 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ) );
 556+ } elseif ( $user->isBlocked() ) { # Show log extract if the user is currently blocked
 557+ LogEventsList::showLogExtract(
 558+ $out,
 559+ 'block',
 560+ $user->getUserPage()->getPrefixedText(),
 561+ '',
 562+ array(
 563+ 'lim' => 1,
 564+ 'showIfEmpty' => false,
 565+ 'msgKey' => array(
 566+ 'blocked-notice-logextract',
 567+ $user->getName() # Support GENDER in notice
 568+ )
 569+ )
 570+ );
 571+ }
 572+ }
 573+
 574+ wfRunHooks( 'ShowMissingArticle', array( $this ) ); // @fixme
 575+
 576+ # Show delete and move logs
 577+ LogEventsList::showLogExtract( $out, array( 'delete', 'move' ), $this->getTitle()->getPrefixedText(), '',
 578+ array( 'lim' => 10,
 579+ 'conds' => array( "log_action != 'revision'" ),
 580+ 'showIfEmpty' => false,
 581+ 'msgKey' => array( 'moveddeleted-notice' ) )
 582+ );
 583+
 584+ # Show error message
 585+ $oldid = $this->getOldID();
 586+ if ( $oldid ) {
 587+ $text = wfMsgNoTrans( 'missing-article',
 588+ $this->getTitle()->getPrefixedText(),
 589+ wfMsgNoTrans( 'missingarticle-rev', $oldid ) );
 590+ } elseif ( $this->getTitle()->getNamespace() === NS_MEDIAWIKI ) {
 591+ // Use the default message text
 592+ $text = $this->getTitle()->getDefaultMessageText();
 593+ } else {
 594+ $createErrors = $this->getTitle()->getUserPermissionsErrors( 'create', $user );
 595+ $editErrors = $this->getTitle()->getUserPermissionsErrors( 'edit', $user );
 596+ $errors = array_merge( $createErrors, $editErrors );
 597+
 598+ if ( !count( $errors ) ) {
 599+ $text = wfMsgNoTrans( 'noarticletext' );
 600+ } else {
 601+ $text = wfMsgNoTrans( 'noarticletext-nopermission' );
 602+ }
 603+ }
 604+ $text = "<div class='noarticletext'>\n$text\n</div>";
 605+
 606+ if ( !$this->getPage()->hasViewableContent() ) {
 607+ // If there's no backing content, send a 404 Not Found
 608+ // for better machine handling of broken links.
 609+ $this->getRequest()->response()->header( "HTTP/1.1 404 Not Found" );
 610+ }
 611+
 612+ $out->addWikiText( $text );
 613+ }
 614+
 615+ /**
 616+ * If the revision requested for view is deleted, check permissions.
 617+ * Send either an error message or a warning header to OutputPage.
 618+ *
 619+ * @return boolean true if the view is allowed, false if not.
 620+ */
 621+ public function showDeletedRevisionHeader() {
 622+ $out = $this->getOutput();
 623+
 624+ if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
 625+ // Not deleted
 626+ return true;
 627+ }
 628+
 629+ // If the user is not allowed to see it...
 630+ if ( !$this->mRevision->userCan( Revision::DELETED_TEXT ) ) {
 631+ $out->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
 632+ 'rev-deleted-text-permission' );
 633+
 634+ return false;
 635+ // If the user needs to confirm that they want to see it...
 636+ } elseif ( $this->getRequest()->getInt( 'unhide' ) != 1 ) {
 637+ # Give explanation and add a link to view the revision...
 638+ $oldid = intval( $this->getOldID() );
 639+ $link = $this->getTitle()->getFullUrl( "oldid={$oldid}&unhide=1" );
 640+ $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
 641+ 'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
 642+ $out->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
 643+ array( $msg, $link ) );
 644+
 645+ return false;
 646+ // We are allowed to see...
 647+ } else {
 648+ $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
 649+ 'rev-suppressed-text-view' : 'rev-deleted-text-view';
 650+ $out->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
 651+
 652+ return true;
 653+ }
 654+ }
 655+
 656+}
 657+
Property changes on: branches/pageoutput/includes/view/ArticlePageView.php
___________________________________________________________________
Added: svn:eol-style
1658 + native
Index: branches/pageoutput/includes/view/PageView.php
@@ -0,0 +1,46 @@
 2+<?php
 3+
 4+abstract class PageView extends ContextSource {
 5+
 6+ public static function factory( RequestContext $context ) {
 7+
 8+ $pageview = null;
 9+ wfRunHooks( 'PageViewFromContext', array( $context, &$pageview ) );
 10+ if ( !$pageview ) {
 11+ switch ( $context->getTitle()->getNamespace() ) {
 12+ case NS_SPECIAL:
 13+ $pageview = SpecialPage::newFromContext( $context );
 14+ break;
 15+ case NS_FILE:
 16+ $pageview = new FilePageView( $context );
 17+ break;
 18+ case NS_CATEGORY:
 19+ $pageview = new CategoryPageView( $context );
 20+ break;
 21+ case NS_MEDIAWIKI:
 22+ $pageview = new MessagePageView( $context );
 23+ break;
 24+ default:
 25+ $pageview = new ArticlePageView( $context );
 26+ break;
 27+ }
 28+ }
 29+ $pageview->setContext( $context );
 30+
 31+ return $pageview;
 32+ }
 33+
 34+
 35+ /** The rel=canonical url if any */
 36+ protected $canonicalURL = null;
 37+
 38+ protected function setCanonicalURL( $url ) {
 39+ $this->canonicalURL = $url;
 40+ }
 41+
 42+
 43+ abstract function getTabs();
 44+
 45+ abstract function render();
 46+
 47+}
Property changes on: branches/pageoutput/includes/view/PageView.php
___________________________________________________________________
Added: svn:eol-style
148 + native

Comments

#Comment by Dantman (talk | contribs)   18:24, 14 September 2011

Notes:

  • While moving stuff from Article to PageView some things were changed:
    • Lots of things like $wg{User,Request,Out} globals have been cleaned up.
    • $this->mPage replaced with $this->getPage().
    • $action was removed from getRobotsPolicy
  • I called the entrypoint ::render() this is partially because originally the classname pattern was actually PageRenderer not PageView, I may rename it later.
  • Naturally hooks are broken, marked with @fixme-s, those can be sorted out when all the core functionality has been re-implemented in PageViews

Status & tagging log