(Bug 19725) Do not include suppressed edits in the "View X deleted edits" message...
[lhc/web/wiklou.git] / includes / OutputPage.php
index 3ceac65..1de0b65 100644 (file)
@@ -10,9 +10,9 @@ if ( !defined( 'MEDIAWIKI' ) ) {
  *
  * This class is used to prepare the final rendering. A skin is then
  * applied to the output parameters (links, javascript, html, categories ...).
- * 
- * Another class (fixme) handles sending the whole page to the client.
- * 
+ *
+ * @todo FIXME: Another class handles sending the whole page to the client.
+ *
  * Some comments comes from a pairing session between Zak Greant and Ashar Voultoiz
  * in November 2010.
  *
@@ -79,7 +79,7 @@ class OutputPage {
         * to compare its cached version with the server version. Client sends
         * headers If-Match and If-None-Match containing its locally cached ETAG value.
         *
-        * To get more information, you will have to look at HTTP1/1 protocols which
+        * To get more information, you will have to look at HTTP/1.1 protocol which
         * is properly described in RFC 2616 : http://tools.ietf.org/html/rfc2616
         */
        var $mETag = false;
@@ -115,11 +115,11 @@ class OutputPage {
        /// Array of elements in <head>. Parser might add its own headers!
        var $mHeadItems = array();
 
-       // Next variables probably comes from the resource loader @todo FIXME
+       // @todo FIXME: Next variables probably comes from the resource loader
        var $mModules = array(), $mModuleScripts = array(), $mModuleStyles = array(), $mModuleMessages = array();
        var $mResourceLoader;
 
-       /** @fixme is this still used ?*/
+       /** @todo FIXME: Is this still used ?*/
        var $mInlineMsg = array();
 
        var $mTemplateIds = array();
@@ -190,6 +190,8 @@ class OutputPage {
        /// should be private. To include the variable {{REVISIONID}}
        var $mRevisionId = null;
 
+       var $mFileVersion = null;
+
        private $mContext;
 
        /**
@@ -307,7 +309,7 @@ class OutputPage {
         *
         * @return String
         */
-       private function getMetadataAttribute() {
+       public function getMetadataAttribute() {
                # note: buggy CC software only reads first "meta" link
                static $haveMeta = false;
                if ( $haveMeta ) {
@@ -391,6 +393,7 @@ class OutputPage {
         * which are no longer registered (eg a page is cached before an extension is disabled)
         * @param $modules Array
         * @param $position String if not null, only return modules with this position
+        * @param $type string
         * @return Array
         */
        protected function filterModules( $modules, $position = null, $type = ResourceLoaderModule::TYPE_COMBINED ){
@@ -413,6 +416,7 @@ class OutputPage {
         *
         * @param $filter Bool whether to filter out insufficiently trustworthy modules
         * @param $position String if not null, only return modules with this position
+        * @param $param string
         * @return Array of module names
         */
        public function getModules( $filter = false, $position = null, $param = 'mModules' ) {
@@ -435,6 +439,10 @@ class OutputPage {
 
        /**
         * Get the list of module JS to include on this page
+        *
+        * @param $filter
+        * @param $position
+        *
         * @return array of module names
         */
        public function getModuleScripts( $filter = false, $position = null ) {
@@ -455,6 +463,9 @@ class OutputPage {
        /**
         * Get the list of module CSS to include on this page
         *
+        * @param $filter
+        * @param $position
+        * 
         * @return Array of module names
         */
        public function getModuleStyles( $filter = false, $position = null ) {
@@ -475,6 +486,9 @@ class OutputPage {
        /**
         * Get the list of module messages to include on this page
         *
+        * @param $filter
+        * @param $position
+        *
         * @return Array of module names
         */
        public function getModuleMessages( $filter = false, $position = null ) {
@@ -561,6 +575,8 @@ class OutputPage {
         *
         * Side effect: sets mLastModified for Last-Modified header
         *
+        * @param $timestamp string
+        *
         * @return Boolean: true iff cache-ok headers was sent.
         */
        public function checkLastModified( $timestamp ) {
@@ -724,6 +740,8 @@ class OutputPage {
        /**
         * "HTML title" means the contents of <title>.
         * It is stored as plain, unescaped text and will be run through htmlspecialchars in the skin file.
+        *
+        * @param $name string
         */
        public function setHTMLTitle( $name ) {
                $this->mHTMLtitle = $name;
@@ -743,6 +761,8 @@ class OutputPage {
         * This function allows good tags like \<sup\> in the \<h1\> tag, but not bad tags like \<script\>.
         * This function automatically sets \<title\> to the same content as \<h1\> but with all tags removed.
         * Bad tags that were escaped in \<h1\> will still be escaped in \<title\>, and good tags like \<i\> will be dropped entirely.
+        *
+        * @param $name string
         */
        public function setPageTitle( $name ) {
                # change "<script>foo&bar</script>" to "&lt;script&gt;foo&amp;bar&lt;/script&gt;"
@@ -792,7 +812,7 @@ class OutputPage {
         * @param $t Title object
         */
        public function setTitle( $t ) {
-               $this->getContext()->setTitle($t);
+               $this->getContext()->setTitle( $t );
        }
 
        /**
@@ -1111,7 +1131,7 @@ class OutputPage {
                                }
                                $text = $wgContLang->convertHtml( $title->getText() );
                                $this->mCategories[] = $title->getText();
-                               $this->mCategoryLinks[$type][] = $this->getSkin()->link( $title, $text );
+                               $this->mCategoryLinks[$type][] = Linker::link( $title, $text );
                        }
                }
        }
@@ -1161,7 +1181,7 @@ class OutputPage {
        /**
         * Return whether user JavaScript is allowed for this page
         * @deprecated since 1.18 Load modules with ResourceLoader, and origin and
-        *     trustworthiness is identified and enforced automagically. 
+        *     trustworthiness is identified and enforced automagically.
         * @return Boolean
         */
        public function isUserJsAllowed() {
@@ -1272,7 +1292,7 @@ class OutputPage {
        }
 
        /**
-        * Get the current revision ID
+        * Get the displayed revision ID
         *
         * @return Integer
         */
@@ -1280,6 +1300,28 @@ class OutputPage {
                return $this->mRevisionId;
        }
 
+       /**
+        * Set the displayed file version
+        *
+        * @param $file File|false
+        * @return Mixed: previous value
+        */
+       public function setFileVersion( $file ) {
+               if ( $file instanceof File && $file->exists() ) {
+                       $val = array( 'time' => $file->getTimestamp(), 'sha1' => $file->getSha1() );
+               }
+               return wfSetVar( $this->mFileVersion, $val );
+       }
+
+       /**
+        * Get the displayed file version
+        *
+        * @return Array|null ('time' => MW timestamp, 'sha1' => sha1)
+        */
+       public function getFileVersion() {
+               return $this->mFileVersion;
+       }
+
        /**
         * Get the templates used on this page
         *
@@ -1526,7 +1568,9 @@ class OutputPage {
        /**
         * Use enableClientCache(false) to force it to send nocache headers
         *
-        * @param $state ??
+        * @param $state bool
+        *
+        * @return bool
         */
        public function enableClientCache( $state ) {
                return wfSetVar( $this->mEnableClientCache, $state );
@@ -1594,7 +1638,7 @@ class OutputPage {
         *
         * @param $header String: header name
         * @param $option Array|null
-        * @fixme Document the $option parameter; it appears to be for
+        * @todo FIXME: Document the $option parameter; it appears to be for
         *        X-Vary-Options but what format is acceptable?
         */
        public function addVaryHeader( $header, $option = null ) {
@@ -1672,12 +1716,14 @@ class OutputPage {
        }
 
        /**
-        * Set a flag which will cause an X-Frame-Options header appropriate for 
-        * edit pages to be sent. The header value is controlled by 
+        * Set a flag which will cause an X-Frame-Options header appropriate for
+        * edit pages to be sent. The header value is controlled by
         * $wgEditPageFrameOptions.
         *
-        * This is the default for special pages. If you display a CSRF-protected 
+        * This is the default for special pages. If you display a CSRF-protected
         * form on an ordinary view page, then you need to call this function.
+        *
+        * @param $enable bool
         */
        public function preventClickjacking( $enable = true ) {
                $this->mPreventClickjacking = $enable;
@@ -1693,9 +1739,11 @@ class OutputPage {
        }
 
        /**
-        * Get the X-Frame-Options header value (without the name part), or false 
-        * if there isn't one. This is used by Skin to determine whether to enable 
+        * Get the X-Frame-Options header value (without the name part), or false
+        * if there isn't one. This is used by Skin to determine whether to enable
         * JavaScript frame-breaking, for clients that don't support X-Frame-Options.
+        *
+        * @return string
         */
        public function getFrameOptions() {
                global $wgBreakFrames, $wgEditPageFrameOptions;
@@ -1779,58 +1827,13 @@ class OutputPage {
         *
         * @param $code Integer: status code
         * @return String or null: message or null if $code is not in the list of
-        *         messages
+        *         messages 
+        *
+        * @deprecated since 1.19 Use HttpStatus::getMessage() instead.
         */
        public static function getStatusMessage( $code ) {
-               static $statusMessage = array(
-                       100 => 'Continue',
-                       101 => 'Switching Protocols',
-                       102 => 'Processing',
-                       200 => 'OK',
-                       201 => 'Created',
-                       202 => 'Accepted',
-                       203 => 'Non-Authoritative Information',
-                       204 => 'No Content',
-                       205 => 'Reset Content',
-                       206 => 'Partial Content',
-                       207 => 'Multi-Status',
-                       300 => 'Multiple Choices',
-                       301 => 'Moved Permanently',
-                       302 => 'Found',
-                       303 => 'See Other',
-                       304 => 'Not Modified',
-                       305 => 'Use Proxy',
-                       307 => 'Temporary Redirect',
-                       400 => 'Bad Request',
-                       401 => 'Unauthorized',
-                       402 => 'Payment Required',
-                       403 => 'Forbidden',
-                       404 => 'Not Found',
-                       405 => 'Method Not Allowed',
-                       406 => 'Not Acceptable',
-                       407 => 'Proxy Authentication Required',
-                       408 => 'Request Timeout',
-                       409 => 'Conflict',
-                       410 => 'Gone',
-                       411 => 'Length Required',
-                       412 => 'Precondition Failed',
-                       413 => 'Request Entity Too Large',
-                       414 => 'Request-URI Too Large',
-                       415 => 'Unsupported Media Type',
-                       416 => 'Request Range Not Satisfiable',
-                       417 => 'Expectation Failed',
-                       422 => 'Unprocessable Entity',
-                       423 => 'Locked',
-                       424 => 'Failed Dependency',
-                       500 => 'Internal Server Error',
-                       501 => 'Not Implemented',
-                       502 => 'Bad Gateway',
-                       503 => 'Service Unavailable',
-                       504 => 'Gateway Timeout',
-                       505 => 'HTTP Version Not Supported',
-                       507 => 'Insufficient Storage'
-               );
-               return isset( $statusMessage[$code] ) ? $statusMessage[$code] : null;
+               wfDeprecated( __METHOD__ );
+               return HttpStatus::getMessage( $code );
        }
 
        /**
@@ -1838,7 +1841,6 @@ class OutputPage {
         * the object, let's actually output it:
         */
        public function output() {
-               global $wgOutputEncoding;
                global $wgLanguageCode, $wgDebugRedirects, $wgMimeType;
 
                if( $this->mDoNothing ) {
@@ -1854,7 +1856,7 @@ class OutputPage {
                        $this->mRedirect = wfExpandUrl( $this->mRedirect );
                        if( $this->mRedirectCode == '301' || $this->mRedirectCode == '303' ) {
                                if( !$wgDebugRedirects ) {
-                                       $message = self::getStatusMessage( $this->mRedirectCode );
+                                       $message = HttpStatus::getMessage( $this->mRedirectCode );
                                        $response->header( "HTTP/1.1 {$this->mRedirectCode} $message" );
                                }
                                $this->mLastModified = wfTimestamp( TS_RFC2822 );
@@ -1873,7 +1875,7 @@ class OutputPage {
                        wfProfileOut( __METHOD__ );
                        return;
                } elseif ( $this->mStatusCode ) {
-                       $message = self::getStatusMessage( $this->mStatusCode );
+                       $message = HttpStatus::getMessage( $this->mStatusCode );
                        if ( $message ) {
                                $response->header( 'HTTP/1.1 ' . $this->mStatusCode . ' ' . $message );
                        }
@@ -1882,7 +1884,7 @@ class OutputPage {
                # Buffer output; final headers may depend on later processing
                ob_start();
 
-               $response->header( "Content-type: $wgMimeType; charset={$wgOutputEncoding}" );
+               $response->header( "Content-type: $wgMimeType; charset=UTF-8" );
                $response->header( 'Content-language: ' . $wgLanguageCode );
 
                // Prevent framing, if requested
@@ -1896,7 +1898,7 @@ class OutputPage {
                } else {
                        $this->addDefaultModules();
 
-                       $sk = $this->getSkin( $this->getTitle() );
+                       $sk = $this->getSkin();
 
                        // Hook that allows last minute changes to the output page, e.g.
                        // adding of CSS or Javascript by extensions.
@@ -1913,73 +1915,20 @@ class OutputPage {
        }
 
        /**
-        * Actually output something with print(). Performs an iconv to the
-        * output encoding, if needed.
+        * Actually output something with print().
         *
         * @param $ins String: the string to output
         */
        public function out( $ins ) {
-               global $wgInputEncoding, $wgOutputEncoding, $wgContLang;
-               if ( 0 == strcmp( $wgInputEncoding, $wgOutputEncoding ) ) {
-                       $outs = $ins;
-               } else {
-                       $outs = $wgContLang->iconv( $wgInputEncoding, $wgOutputEncoding, $ins );
-                       if ( false === $outs ) {
-                               $outs = $ins;
-                       }
-               }
-               print $outs;
+               print $ins;
        }
 
        /**
         * Produce a "user is blocked" page.
-        *
-        * @param $return Boolean: whether to have a "return to $wgTitle" message or not.
-        * @return nothing
+        * @deprecated since 1.18
         */
-       function blockedPage( $return = true ) {
-               global $wgContLang;
-
-               $this->setPageTitle( wfMsg( 'blockedtitle' ) );
-               $this->setRobotPolicy( 'noindex,nofollow' );
-               $this->setArticleRelated( false );
-
-               $name = $this->getUser()->blockedBy();
-               $reason = $this->getUser()->blockedFor();
-               if( $reason == '' ) {
-                       $reason = wfMsg( 'blockednoreason' );
-               }
-               $blockTimestamp = $this->getContext()->getLang()->timeanddate(
-                       wfTimestamp( TS_MW, $this->getUser()->mBlock->mTimestamp ), true
-               );
-               $ip = wfGetIP();
-
-               $link = '[[' . $wgContLang->getNsText( NS_USER ) . ":{$name}|{$name}]]";
-
-               $blockid = $this->getUser()->mBlock->getId();
-
-               $blockExpiry = $this->getContext()->getLang()->formatExpiry( $this->getUser()->mBlock->mExpiry );
-
-               if ( $this->getUser()->mBlock->mAuto ) {
-                       $msg = 'autoblockedtext';
-               } else {
-                       $msg = 'blockedtext';
-               }
-
-               /* $ip returns who *is* being blocked, $intended contains who was meant to be blocked.
-                * This could be a username, an IP range, or a single IP. */
-               $intended = $this->getUser()->mBlock->getTarget();
-
-               $this->addWikiMsg(
-                       $msg, $link, $reason, $ip, $name, $blockid, $blockExpiry,
-                       $intended, $blockTimestamp
-               );
-
-               # Don't auto-return to special pages
-               if( $return ) {
-                       $return = $this->getTitle()->getNamespace() > -1 ? $this->getTitle() : null;
-                       $this->returnToMain( null, $return );
-               }
+       function blockedPage() {
+               throw new UserBlockedError( $this->getUser()->mBlock );
        }
 
        /**
@@ -2044,28 +1993,11 @@ class OutputPage {
 
        /**
         * Display an error page noting that a given permission bit is required.
-        *
+        * @deprecated since 1.18, just throw the exception directly
         * @param $permission String: key required
         */
        public function permissionRequired( $permission ) {
-               $this->setPageTitle( wfMsg( 'badaccess' ) );
-               $this->setHTMLTitle( wfMsg( 'errorpagetitle' ) );
-               $this->setRobotPolicy( 'noindex,nofollow' );
-               $this->setArticleRelated( false );
-               $this->mBodytext = '';
-
-               $groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
-                       User::getGroupsWithPermission( $permission ) );
-               if( $groups ) {
-                       $this->addWikiMsg(
-                               'badaccess-groups',
-                               $this->getContext()->getLang()->commaList( $groups ),
-                               count( $groups )
-                       );
-               } else {
-                       $this->addWikiMsg( 'badaccess-group0' );
-               }
-               $this->returnToMain();
+               throw new PermissionsError( $permission );
        }
 
        /**
@@ -2073,8 +2005,7 @@ class OutputPage {
         */
        public function loginToUse() {
                if( $this->getUser()->isLoggedIn() ) {
-                       $this->permissionRequired( 'read' );
-                       return;
+                       throw new PermissionsError( 'read' );
                }
 
                $this->setPageTitle( wfMsg( 'loginreqtitle' ) );
@@ -2083,7 +2014,7 @@ class OutputPage {
                $this->setArticleRelated( false );
 
                $loginTitle = SpecialPage::getTitleFor( 'Userlogin' );
-               $loginLink = $this->getSkin()->link(
+               $loginLink = Linker::link(
                        $loginTitle,
                        wfMsgHtml( 'loginreqlink' ),
                        array(),
@@ -2173,7 +2104,7 @@ class OutputPage {
                        if( $source ) {
                                $this->setPageTitle( wfMsg( 'viewsource' ) );
                                $this->setSubtitle(
-                                       wfMsg( 'viewsourcefor', $this->getSkin()->linkKnown( $this->getTitle() ) )
+                                       wfMsg( 'viewsourcefor', Linker::linkKnown( $this->getTitle() ) )
                                );
                        } else {
                                $this->setPageTitle( wfMsg( 'badaccess' ) );
@@ -2181,9 +2112,7 @@ class OutputPage {
                        $this->addWikiText( $this->formatPermissionsErrorMessage( $reasons, $action ) );
                } else {
                        // Wiki is read only
-                       $this->setPageTitle( wfMsg( 'readonly' ) );
-                       $reason = wfReadOnlyReason();
-                       $this->wrapWikiMsg( "<div class='mw-readonly-error'>\n$1\n</div>", array( 'readonlytext', $reason ) );
+                       throw new ReadOnlyError;
                }
 
                // Show source, if supplied
@@ -2200,10 +2129,10 @@ class OutputPage {
                        $this->addHTML( Html::element( 'textarea', $params, $source ) );
 
                        // Show templates used by this article
-                       $skin = $this->getSkin();
                        $article = new Article( $this->getTitle() );
+                       $templates = Linker::formatTemplates( $article->getUsedTemplates() );
                        $this->addHTML( "<div class='templatesUsed'>
-{$skin->formatTemplates( $article->getUsedTemplates() )}
+$templates
 </div>
 " );
                }
@@ -2217,24 +2146,31 @@ class OutputPage {
        }
 
        /**
-        * Adds JS-based password security checker
-        * @param $passwordId String ID of input box containing password
-        * @param $retypeId String ID of input box containing retyped password
-        * @return none
+        * Turn off regular page output and return an error reponse
+        * for when rate limiting has triggered.
         */
-       public function addPasswordSecurity( $passwordId, $retypeId ) {
-               $data = array(
-                       'password' => '#' . $passwordId,
-                       'retype' => '#' . $retypeId,
-                       'messages' => array(),
-               );
-               foreach ( array( 'password-strength', 'password-strength-bad', 'password-strength-mediocre',
-                               'password-strength-acceptable', 'password-strength-good', 'password-retype', 'password-retype-mismatch'
-                       ) as $message ) {
-                       $data['messages'][$message] = wfMsg( $message );
+       public function rateLimited() {
+               throw new ThrottledError;
+       }
+
+       /**
+        * Show a warning about slave lag
+        *
+        * If the lag is higher than $wgSlaveLagCritical seconds,
+        * then the warning is a bit more obvious. If the lag is
+        * lower than $wgSlaveLagWarning, then no warning is shown.
+        *
+        * @param $lag Integer: slave lag
+        */
+       public function showLagWarning( $lag ) {
+               global $wgSlaveLagWarning, $wgSlaveLagCritical;
+               if( $lag >= $wgSlaveLagWarning ) {
+                       $message = $lag < $wgSlaveLagCritical
+                               ? 'lag-warn-normal'
+                               : 'lag-warn-high';
+                       $wrap = Html::rawElement( 'div', array( 'class' => "mw-{$message}" ), "\n$1\n" );
+                       $this->wrapWikiMsg( "$wrap\n", array( $message, $this->getContext()->getLang()->formatNum( $lag ) ) );
                }
-               $this->addScript( Html::inlineScript( 'var passwordSecurity=' . FormatJson::encode( $data ) ) );
-               $this->addModules( 'mediawiki.legacy.password' );
        }
 
        public function showFatalError( $message ) {
@@ -2270,14 +2206,14 @@ class OutputPage {
         * Add a "return to" link pointing to a specified title
         *
         * @param $title Title to link
-        * @param $query String: query string
+        * @param $query String query string
         * @param $text String text of the link (input is not escaped)
         */
        public function addReturnTo( $title, $query = array(), $text = null ) {
                $this->addLink( array( 'rel' => 'next', 'href' => $title->getFullURL() ) );
                $link = wfMsgHtml(
                        'returnto',
-                       $this->getSkin()->link( $title, $text, array(), $query )
+                       Linker::link( $title, $text, array(), $query )
                );
                $this->addHTML( "<p id=\"mw-returnto\">{$link}</p>\n" );
        }
@@ -2321,16 +2257,14 @@ class OutputPage {
         * @return String: The doctype, opening <html>, and head element.
         */
        public function headElement( Skin $sk, $includeStyle = true ) {
-               global $wgOutputEncoding, $wgMimeType;
-               global $wgUseTrackbacks, $wgHtml5;
+               global $wgUseTrackbacks, $wgLang;
 
                if ( $sk->commonPrintStylesheet() ) {
                        $this->addModuleStyles( 'mediawiki.legacy.wikiprintable' );
                }
                $sk->setupUserCss( $this );
 
-               $lang = wfUILang();
-               $ret = Html::htmlHeader( array( 'lang' => $lang->getCode(), 'dir' => $lang->getDir() ) );
+               $ret = Html::htmlHeader( array( 'lang' => $wgLang->getCode(), 'dir' => $wgLang->getDir() ) );
 
                if ( $this->getHTMLTitle() == '' ) {
                        $this->setHTMLTitle( wfMsg( 'pagetitle', $this->getPageTitle() ) );
@@ -2376,9 +2310,11 @@ class OutputPage {
                                Xml::escapeJsString( $editUrl ) . "'";
                }
 
-               # Class bloat
-               $dir = wfUILang()->getDir();
-               $bodyAttrs['class'] = "mediawiki $dir";
+               # Classes for LTR/RTL directionality support
+               global $wgLang, $wgContLang;
+               $userdir = $wgLang->getDir();
+               $sitedir = $wgContLang->getDir();
+               $bodyAttrs['class'] = "mediawiki $userdir sitedir-$sitedir";
 
                if ( $this->getContext()->getLang()->capitalizeAllNouns() ) {
                        # A <body> class is probably not the best way to do this . . .
@@ -2403,8 +2339,13 @@ class OutputPage {
                        $wgUseAjax, $wgAjaxWatch, $wgEnableMWSuggest;
 
                // Add base resources
-               $this->addModules( 'mediawiki.util' );
-               if( $wgIncludeLegacyJavaScript ){
+               $this->addModules( array(
+                       'mediawiki.user',
+                       'mediawiki.util',
+                       'mediawiki.page.startup',
+                       'mediawiki.page.ready',
+               ) );
+               if ( $wgIncludeLegacyJavaScript ){
                        $this->addModules( 'mediawiki.legacy.wikibits' );
                }
 
@@ -2438,7 +2379,7 @@ class OutputPage {
                        $this->mResourceLoader = new ResourceLoader();
                }
                return $this->mResourceLoader;
-       }               
+       }
 
        /**
         * TODO: Document
@@ -2453,7 +2394,6 @@ class OutputPage {
                        $wgResourceLoaderInlinePrivateModules;
                // Lazy-load ResourceLoader
                // TODO: Should this be a static function of ResourceLoader instead?
-               // TODO: Divide off modules starting with "user", and add the user parameter to them
                $baseQuery = array(
                        'lang' => $this->getContext()->getLang()->getCode(),
                        'debug' => ResourceLoader::inDebugMode() ? 'true' : 'false',
@@ -2517,7 +2457,7 @@ class OutputPage {
                        if ( ( $group === 'user' || $group === 'private' ) && $this->getUser()->isLoggedIn() ) {
                                $query['user'] = $this->getUser()->getName();
                        }
-                       
+
                        // Create a fake request based on the one we are about to make so modules return
                        // correct timestamp and emptiness data
                        $context = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) );
@@ -2531,9 +2471,9 @@ class OutputPage {
                        if ( $modules === array() ) {
                                continue;
                        }
-                       
-                       $query['modules'] = implode( '|', array_keys( $modules ) );
-                       
+
+                       $query['modules'] = ResourceLoader::makePackedModulesString( array_keys( $modules ) );
+
                        // Support inlining of private modules if configured as such
                        if ( $group === 'private' && $wgResourceLoaderInlinePrivateModules ) {
                                if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
@@ -2567,6 +2507,9 @@ class OutputPage {
                        ksort( $query );
 
                        $url = wfAppendQuery( $wgLoadScript, $query );
+                       // Prevent the IE6 extension check from being triggered (bug 28840)
+                       // by appending a character that's invalid in Windows extensions ('*')
+                       $url .= '&*';
                        if ( $useESI && $wgResourceLoaderUseESI ) {
                                $esi = Xml::element( 'esi:include', array( 'src' => $url ) );
                                if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
@@ -2577,9 +2520,9 @@ class OutputPage {
                        } else {
                                // Automatically select style/script elements
                                if ( $only === ResourceLoaderModule::TYPE_STYLES ) {
-                                       $link = Html::linkedStyle( wfAppendQuery( $wgLoadScript, $query ) );
+                                       $link = Html::linkedStyle( $url );
                                } else {
-                                       $link = Html::linkedScript( wfAppendQuery( $wgLoadScript, $query ) );
+                                       $link = Html::linkedScript( $url );
                                }
                        }
 
@@ -2602,14 +2545,14 @@ class OutputPage {
        function getHeadScripts( Skin $sk ) {
                // Startup - this will immediately load jquery and mediawiki modules
                $scripts = $this->makeResourceLoaderLink( $sk, 'startup', ResourceLoaderModule::TYPE_SCRIPTS, true );
-               
+
                // Load config before anything else
                $scripts .= Html::inlineScript(
                        ResourceLoader::makeLoaderConditionalScript(
                                ResourceLoader::makeConfigSetScript( $this->getJSVars() )
                        )
                );
-               
+
                // Script and Messages "only" requests marked for top inclusion
                // Messages should go first
                $scripts .= $this->makeResourceLoaderLink( $sk, $this->getModuleMessages( true, 'top' ), ResourceLoaderModule::TYPE_MESSAGES );
@@ -2621,22 +2564,25 @@ class OutputPage {
                if ( $modules ) {
                        $scripts .= Html::inlineScript(
                                ResourceLoader::makeLoaderConditionalScript(
-                                       Xml::encodeJsCall( 'mw.loader.load', array( $modules ) ) .
-                                       Xml::encodeJsCall( 'mw.loader.go', array() )
+                                       Xml::encodeJsCall( 'mw.loader.load', array( $modules ) )
                                )
                        );
                }
 
                return $scripts;
        }
-       
+
        /**
         * JS stuff to put at the bottom of the <body>: modules marked with position 'bottom',
         * legacy scripts ($this->mScripts), user preferences, site JS and user JS
+        *
+        * @param $sk Skin
+        *
+        * @return string
         */
        function getBottomScripts( Skin $sk ) {
                global $wgUseSiteJs, $wgAllowUserJs;
-               
+
                // Script and Messages "only" requests marked for bottom inclusion
                // Messages should go first
                $scripts = $this->makeResourceLoaderLink( $sk, $this->getModuleMessages( true, 'bottom' ), ResourceLoaderModule::TYPE_MESSAGES );
@@ -2648,17 +2594,15 @@ class OutputPage {
                if ( $modules ) {
                        $scripts .= Html::inlineScript(
                                ResourceLoader::makeLoaderConditionalScript(
-                                       Xml::encodeJsCall( 'mw.loader.load', array( $modules ) ) .
-                                       // the go() call is unnecessary if we inserted top modules, but we don't know for sure that we did
-                                       Xml::encodeJsCall( 'mw.loader.go', array() )
+                                       Xml::encodeJsCall( 'mw.loader.load', array( $modules ) )
                                )
                        );
                }
-               
+
                // Legacy Scripts
                $scripts .= "\n" . $this->mScripts;
 
-               $userScripts = array( 'user.options' );
+               $userScripts = array( 'user.options', 'user.tokens' );
 
                // Add site JS if enabled
                if ( $wgUseSiteJs ) {
@@ -2675,19 +2619,19 @@ class OutputPage {
                                # XXX: additional security check/prompt?
                                $scripts .= Html::inlineScript( "\n" . $this->getRequest()->getText( 'wpTextbox1' ) . "\n" ) . "\n";
                        } else {
-                               # FIXME: this means that User:Me/Common.js doesn't load when previewing
+                               # @todo FIXME: This means that User:Me/Common.js doesn't load when previewing
                                # User:Me/Vector.js, and vice versa (bug26283)
                                $userScripts[] = 'user';
                        }
                }
                $scripts .= $this->makeResourceLoaderLink( $sk, $userScripts, ResourceLoaderModule::TYPE_SCRIPTS );
-               
+
                return $scripts;
        }
 
        /**
         * Get an array containing global JS variables
-        * 
+        *
         * Do not add things here which can be evaluated in
         * ResourceLoaderStartupScript - in other words, without state.
         * You will only be adding bloat to the page and causing page caches to
@@ -2700,8 +2644,7 @@ class OutputPage {
                $ns = $title->getNamespace();
                $nsname = MWNamespace::exists( $ns ) ? MWNamespace::getCanonicalName( $ns ) : $title->getNsText();
                if ( $ns == NS_SPECIAL ) {
-                       $parts = SpecialPage::resolveAliasWithSubpage( $title->getDBkey() );
-                       $canonicalName = $parts[0];
+                       list( $canonicalName, /*...*/ ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
                } else {
                        $canonicalName = false; # bug 21115
                }
@@ -2730,21 +2673,26 @@ class OutputPage {
                if ( $wgUseAjax && $wgEnableMWSuggest && !$this->getUser()->getOption( 'disablesuggest', false ) ) {
                        $vars['wgSearchNamespaces'] = SearchEngine::userNamespaces( $this->getUser() );
                }
-               
+               if ( $title->isMainPage() ) {
+                       $vars['wgIsMainPage'] = true;
+               }
+
                // Allow extensions to add their custom variables to the global JS variables
                wfRunHooks( 'MakeGlobalVariablesScript', array( &$vars ) );
-               
+
                return $vars;
        }
 
        /**
+        * @param $sk Skin
+        * @param $addContentType bool
+        *
         * @return string HTML tag links to be put in the header.
         */
        public function getHeadLinks( Skin $sk, $addContentType = false ) {
                global $wgUniversalEditButton, $wgFavicon, $wgAppleTouchIcon, $wgEnableAPI,
-                       $wgSitename, $wgVersion, $wgHtml5, $wgMimeType, $wgOutputEncoding,
+                       $wgSitename, $wgVersion, $wgHtml5, $wgMimeType,
                        $wgFeed, $wgOverrideSiteFeed, $wgAdvertisedFeedTypes,
-                       $wgEnableDublinCoreRdf, $wgEnableCreativeCommonsRdf,
                        $wgDisableLangConversion, $wgCanonicalLanguageLinks, $wgContLang,
                        $wgRightsPage, $wgRightsUrl;
 
@@ -2754,11 +2702,11 @@ class OutputPage {
                        if ( $wgHtml5 ) {
                                # More succinct than <meta http-equiv=Content-Type>, has the
                                # same effect
-                               $tags[] = Html::element( 'meta', array( 'charset' => $wgOutputEncoding ) );
+                               $tags[] = Html::element( 'meta', array( 'charset' => 'UTF-8' ) );
                        } else {
                                $tags[] = Html::element( 'meta', array(
                                        'http-equiv' => 'Content-Type',
-                                       'content' => "$wgMimeType; charset=$wgOutputEncoding"
+                                       'content' => "$wgMimeType; charset=UTF-8"
                                ) );
                                $tags[] = Html::element( 'meta', array(  // bug 15835
                                        'http-equiv' => 'Content-Style-Type',
@@ -2869,33 +2817,6 @@ class OutputPage {
                        ) );
                }
 
-               # Metadata links
-               # - Creative Commons
-               #   See http://wiki.creativecommons.org/Extend_Metadata.
-               # - Dublin Core
-               #   Use hreflang to specify canonical and alternate links
-               #   See http://www.google.com/support/webmasters/bin/answer.py?answer=189077
-               if ( $this->isArticleRelated() ) {
-                       # note: buggy CC software only reads first "meta" link
-                       if ( $wgEnableCreativeCommonsRdf ) {
-                               $tags[] = Html::element( 'link', array(
-                                       'rel' => $this->getMetadataAttribute(),
-                                       'title' => 'Creative Commons',
-                                       'type' => 'application/rdf+xml',
-                                       'href' => $this->getTitle()->getLocalURL( 'action=creativecommons' ) )
-                               );
-                       }
-
-                       if ( $wgEnableDublinCoreRdf ) {
-                               $tags[] = Html::element( 'link', array(
-                                       'rel' => $this->getMetadataAttribute(),
-                                       'title' => 'Dublin Core',
-                                       'type' => 'application/rdf+xml',
-                                       'href' => $this->getTitle()->getLocalURL( 'action=dublincore' ) )
-                               );
-                       }
-               }
-
                # Language variants
                if ( !$wgDisableLangConversion && $wgCanonicalLanguageLinks
                        && $wgContLang->hasVariants() ) {
@@ -2943,10 +2864,10 @@ class OutputPage {
                # Feeds
                if ( $wgFeed ) {
                        foreach( $this->getSyndicationLinks() as $format => $link ) {
-                               # Use the page name for the title (accessed through $wgTitle since
-                               # there's no other way).  In principle, this could lead to issues
-                               # with having the same name for different feeds corresponding to
-                               # the same page, but we can't avoid that at this low a level.
+                               # Use the page name for the title.  In principle, this could
+                               # lead to issues with having the same name for different feeds
+                               # corresponding to the same page, but we can't avoid that at
+                               # this low a level.
 
                                $tags[] = $this->feedLink(
                                        $format,
@@ -3041,6 +2962,8 @@ class OutputPage {
         * Build a set of <link>s for the stylesheets specified in the $this->styles array.
         * These will be applied to various media & IE conditionals.
         * @param $sk Skin object
+        *
+        * @return string
         */
        public function buildCssLinks( $sk ) {
                $ret = '';
@@ -3065,7 +2988,7 @@ class OutputPage {
                // Add marker tag to mark the place where the client-side loader should inject dynamic styles
                // We use a <meta> tag with a made-up name for this because that's valid HTML
                $ret .= Html::element( 'meta', array( 'name' => 'ResourceLoaderDynamicStyles', 'content' => '' ) ) . "\n";
-               
+
                // Add site, private and user styles
                // 'private' at present only contains user.options, so put that before 'user'
                // Any future private modules will likely have a similar user-specific character
@@ -3098,8 +3021,8 @@ class OutputPage {
         */
        protected function styleLink( $style, $options ) {
                if( isset( $options['dir'] ) ) {
-                       $siteDir = wfUILang()->getDir();
-                       if( $siteDir != $options['dir'] ) {
+                       global $wgLang;
+                       if( $wgLang->getDir() != $options['dir'] ) {
                                return '';
                        }
                }
@@ -3170,43 +3093,6 @@ class OutputPage {
                return $media;
        }
 
-       /**
-        * Turn off regular page output and return an error reponse
-        * for when rate limiting has triggered.
-        */
-       public function rateLimited() {
-               $this->setPageTitle( wfMsg( 'actionthrottled' ) );
-               $this->setRobotPolicy( 'noindex,follow' );
-               $this->setArticleRelated( false );
-               $this->enableClientCache( false );
-               $this->mRedirect = '';
-               $this->clearHTML();
-               $this->setStatusCode( 503 );
-               $this->addWikiMsg( 'actionthrottledtext' );
-
-               $this->returnToMain( null, $this->getTitle() );
-       }
-
-       /**
-        * Show a warning about slave lag
-        *
-        * If the lag is higher than $wgSlaveLagCritical seconds,
-        * then the warning is a bit more obvious. If the lag is
-        * lower than $wgSlaveLagWarning, then no warning is shown.
-        *
-        * @param $lag Integer: slave lag
-        */
-       public function showLagWarning( $lag ) {
-               global $wgSlaveLagWarning, $wgSlaveLagCritical;
-               if( $lag >= $wgSlaveLagWarning ) {
-                       $message = $lag < $wgSlaveLagCritical
-                               ? 'lag-warn-normal'
-                               : 'lag-warn-high';
-                       $wrap = Html::rawElement( 'div', array( 'class' => "mw-{$message}" ), "\n$1\n" );
-                       $this->wrapWikiMsg( "$wrap\n", array( $message, $this->getContext()->getLang()->formatNum( $lag ) ) );
-               }
-       }
-
        /**
         * Add a wikitext-formatted message to the output.
         * This is equivalent to:
@@ -3225,6 +3111,10 @@ class OutputPage {
         * instead of a variable argument list.
         *
         * $options is passed through to wfMsgExt(), see that function for details.
+        *
+        * @param $name string
+        * @param $args array
+        * @param $options array
         */
        public function addWikiMsgArray( $name, $args, $options = array() ) {
                $options[] = 'parse';
@@ -3255,6 +3145,8 @@ class OutputPage {
         *    $wgOut->addWikiText( "<div class='error'>\n" . wfMsgNoTrans( 'some-error' ) . "\n</div>" );
         *
         * The newline after opening div is needed in some wikitext. See bug 19226.
+        *
+        * @param $wrap string
         */
        public function wrapWikiMsg( $wrap /*, ...*/ ) {
                $msgSpecs = func_get_args();