Merge "Suppress section edit links with action=render"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 26 Mar 2014 18:23:01 +0000 (18:23 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 26 Mar 2014 18:23:01 +0000 (18:23 +0000)
1  2 
RELEASE-NOTES-1.23
includes/Article.php
includes/OutputPage.php

diff --combined RELEASE-NOTES-1.23
@@@ -43,14 -43,6 +43,14 @@@ production
  * Removed deprecated $wgDisabledActions as it is hardly used anywhere.
  * $wgRateLimitLog has been deprecated and replaced by
    $wgDebugLogGroup['ratelimit'].
 +* $wgLocalInterwikis is an array containing multiple local interwiki prefixes
 +  (interwiki prefixes that point back to the current wiki). This effectively
 +  allows more than one value of $wgLocalInterwiki to be specified and
 +  understood by the parser. The value of $wgLocalInterwiki is automatically
 +  prepended to the start of this array.
 +* $wgQueryPages has been removed. Query Pages should be added to by using the
 +  wgQueryPages hook.
 +* $wgHttpOnlyBlacklist has been removed.
  
  === New features in 1.23 ===
  * ResourceLoader can utilize the Web Storage API to cache modules client-side.
    creations, similar to the topOnly option.
  * Add mediawiki.ui.button styling to all pages so wiki content can use styled
    buttons.
 +* Special:UserLogin/signup now does AJAX checks for invalid and taken usernames,
 +  displaying the error live.
 +* Added BaseTemplateAfterPortlet hook to allow injecting html after portlets in skins.
 +* Support has been added for a JSON based localisation file format. The
 +  installer has been updated to use it.
  
  === Bug fixes in 1.23 ===
  * (bug 41759) The "updated since last visit" markers (on history pages, recent
    when the email address is already confirmed. Also, consistently use
    "confirmed", rather than "authenticated", when messaging whether or not the
    user has confirmed an email address.
+ * (bug 19415) action=render no longer shows section edit links. This affects
+   behavior of several other features where (bogus) section edit links will
+   disappear, such as file description pages loaded via $wgUseInstantCommons or
+   pages transcluded cross-wiki via $wgEnableScaryTranscluding.
  * (bug 56912) Show correct link color on cached result of Special:DeadendPages.
  * Classes TitleListDependency and TitleDependency have been removed, as they
    have been found unused in core and extensions for a long time.
  * (bug 42026) Added ucshow={new,!new,top,!top} to list=usercontribs.
    Also added newonly to action=feedcontributions.
  * (bug 42026) Deprecated uctoponly in favor of ucshow=top.
 +* list=search no longer has a "srredirects" parameter. Redirects are now
 +  included in all searches.
  
  === Languages updated in 1.23 ===
  
@@@ -294,20 -283,6 +298,20 @@@ changes to languages because of Bugzill
    is transcluded.
  * (bug 62198) window.$j has been deprecated.
  * Preference "Disable link title conversion" was removed.
 +* SpecialRecentChanges no longer includes any functionality for generating feeds
 +  - it has been factored out to ApiFeedRecentChanges. Old URLs redirect to new
 +  ones.
 +* RecentChange::mExtra['lang'] is no longer set and should no longer be used.
 +  Extensions should read from other configuration variables, including
 +  $wgLocalInterwikis, to identify the current wiki.
 +* Sections in the parser test framework have been renamed and the old
 +  section names are deprecated.  Please use "!!wikitext" and "!!html"
 +  (or "!!html/php") instead of "!!input" and "!!result".  This allows
 +  us to extend parser tests to accommodate additional input/output
 +  pairs, such as "!!html/parsoid" (for the output of the Parsoid
 +  parser, where it differs from the PHP parser).
 +* Special:Search no longer has an "include redirects" option on the advanced
 +  tab. Redirects are now included in all searches.
  
  ==== Removed classes ====
  * FakeMemCachedClient (deprecated in 1.18)
diff --combined includes/Article.php
@@@ -367,11 -367,9 +367,11 @@@ class Article implements Page 
         * Does *NOT* follow redirects.
         *
         * @protected
 -       * @note this is really internal functionality that should really NOT be used by other functions. For accessing
 -       *       article content, use the WikiPage class, especially WikiBase::getContent(). However, a lot of legacy code
 -       *       uses this method to retrieve page text from the database, so the function has to remain public for now.
 +       * @note This is really internal functionality that should really NOT be
 +       * used by other functions. For accessing article content, use the WikiPage
 +       * class, especially WikiBase::getContent(). However, a lot of legacy code
 +       * uses this method to retrieve page text from the database, so the function
 +       * has to remain public for now.
         *
         * @return mixed string containing article contents, or false if null
         * @deprecated in 1.21, use WikiPage::getContent() instead
        /**
         * Get text content object
         * Does *NOT* follow redirects.
 -       * TODO: when is this null?
 +       * @todo When is this null?
         *
 -       * @note code that wants to retrieve page content from the database should use WikiPage::getContent().
 +       * @note Code that wants to retrieve page content from the database should
 +       * use WikiPage::getContent().
         *
         * @return Content|null|boolean false
         *
                        }
                } else {
                        if ( !$this->mPage->getLatest() ) {
 -                              wfDebug( __METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() . "\n" );
 +                              wfDebug( __METHOD__ . " failed to find page data for title " .
 +                                      $this->getTitle()->getPrefixedText() . "\n" );
                                wfProfileOut( __METHOD__ );
                                return false;
                        }
                        $this->mRevision = $this->mPage->getRevision();
  
                        if ( !$this->mRevision ) {
 -                              wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" );
 +                              wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " .
 +                                      $this->mPage->getLatest() . "\n" );
                                wfProfileOut( __METHOD__ );
                                return false;
                        }
  
                // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
                // We should instead work with the Revision object when we need it...
 -              $this->mContentObject = $this->mRevision->getContent( Revision::FOR_THIS_USER, $this->getContext()->getUser() ); // Loads if user is allowed
 +              // Loads if user is allowed
 +              $this->mContentObject = $this->mRevision->getContent(
 +                      Revision::FOR_THIS_USER,
 +                      $this->getContext()->getUser()
 +              );
                $this->mRevIdFetched = $this->mRevision->getId();
  
                wfRunHooks( 'ArticleAfterFetchContentObject', array( &$this, &$this->mContentObject ) );
                                        # Don't cache a dirty ParserOutput object
                                        if ( $poolArticleView->getIsDirty() ) {
                                                $outputPage->setSquidMaxage( 0 );
 -                                              $outputPage->addHTML( "<!-- parser cache is expired, sending anyway due to pool overload-->\n" );
 +                                              $outputPage->addHTML( "<!-- parser cache is expired, " .
 +                                                      "sending anyway due to pool overload-->\n" );
                                        }
  
                                        $outputDone = true;
                }
  
                $contentHandler = $rev->getContentHandler();
 -              $de = $contentHandler->createDifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide );
 +              $de = $contentHandler->createDifferenceEngine(
 +                      $this->getContext(),
 +                      $oldid,
 +                      $diff,
 +                      $rcid,
 +                      $purge,
 +                      $unhide
 +              );
  
                // DifferenceEngine directly fetched the revision:
                $this->mRevIdFetched = $de->mNewid;
                $de->showDiffPage( $diffOnly );
  
 -              // Run view updates for the newer revision being diffed (and shown below the diff if not $diffOnly)
 +              // Run view updates for the newer revision being diffed (and shown
 +              // below the diff if not $diffOnly).
                list( $old, $new ) = $de->mapDiffPrevNext( $oldid, $diff );
                // New can be false, convert it to 0 - this conveniently means the latest revision
                $this->mPage->doViewUpdates( $user, (int)$new );
         * This is hooked by SyntaxHighlight_GeSHi to do syntax highlighting of these
         * page views.
         *
 -       * @param bool $showCacheHint whether to show a message telling the user to clear the browser cache (default: true).
 +       * @param bool $showCacheHint whether to show a message telling the user
 +       *   to clear the browser cache (default: true).
         */
        protected function showCssOrJsPage( $showCacheHint = true ) {
                $outputPage = $this->getContext()->getOutput();
                        $dir = $this->getContext()->getLanguage()->getDir();
                        $lang = $this->getContext()->getLanguage()->getCode();
  
 -                      $outputPage->wrapWikiMsg( "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
 -                              'clearyourcache' );
 +                      $outputPage->wrapWikiMsg(
 +                              "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
 +                              'clearyourcache'
 +                      );
                }
  
                $this->fetchContentObject();
  
                if ( $this->mContentObject ) {
                        // Give hooks a chance to customise the output
 -                      if ( ContentHandler::runLegacyHooks( 'ShowRawCssJs', array( $this->mContentObject, $this->getTitle(), $outputPage ) ) ) {
 +                      if ( ContentHandler::runLegacyHooks(
 +                              'ShowRawCssJs',
 +                              array( $this->mContentObject, $this->getTitle(), $outputPage ) )
 +                      ) {
                                $po = $this->mContentObject->getParserOutput( $this->getTitle() );
                                $outputPage->addHTML( $po->getText() );
                        }
        public function showNamespaceHeader() {
                if ( $this->getTitle()->isTalkPage() ) {
                        if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
 -                              $this->getContext()->getOutput()->wrapWikiMsg( "<div class=\"mw-talkpageheader\">\n$1\n</div>", array( 'talkpageheader' ) );
 +                              $this->getContext()->getOutput()->wrapWikiMsg(
 +                                      "<div class=\"mw-talkpageheader\">\n$1\n</div>",
 +                                      array( 'talkpageheader' )
 +                              );
                        }
                }
        }
         */
        public function showViewFooter() {
                # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
 -              if ( $this->getTitle()->getNamespace() == NS_USER_TALK && IP::isValid( $this->getTitle()->getText() ) ) {
 +              if ( $this->getTitle()->getNamespace() == NS_USER_TALK
 +                      && IP::isValid( $this->getTitle()->getText() )
 +              ) {
                        $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
                }
  
                $patrolFooterShown = $this->showPatrolFooter();
  
                wfRunHooks( 'ArticleViewFooter', array( $this, $patrolFooterShown ) );
 -
        }
  
        /**
                $cache = wfGetMainCache();
                $rc = false;
  
 -              if ( !$this->getTitle()->quickUserCan( 'patrol', $user ) || !( $wgUseRCPatrol || $wgUseNPPatrol ) ) {
 +              if ( !$this->getTitle()->quickUserCan( 'patrol', $user )
 +                      || !( $wgUseRCPatrol || $wgUseNPPatrol )
 +              ) {
                        // Patrolling is disabled or the user isn't allowed to
                        return false;
                }
                        return false;
                }
  
 -              if ( $this->mRevision && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 ) ) {
 +              if ( $this->mRevision
 +                      && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 )
 +              ) {
                        // The current revision is already older than what could be in the RC table
                        // 6h tolerance because the RC might not be cleaned out regularly
                        wfProfileOut( __METHOD__ );
                        __METHOD__
                );
  
 -              if ( $oldestRevisionTimestamp && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 ) ) {
 +              if ( $oldestRevisionTimestamp
 +                      && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
 +              ) {
                        // 6h tolerance because the RC might not be cleaned out regularly
                        $rc = RecentChange::newFromConds(
                                array(
                $validUserPage = false;
  
                # Show info in user (talk) namespace. Does the user exist? Is he blocked?
 -              if ( $this->getTitle()->getNamespace() == NS_USER || $this->getTitle()->getNamespace() == NS_USER_TALK ) {
 +              if ( $this->getTitle()->getNamespace() == NS_USER
 +                      || $this->getTitle()->getNamespace() == NS_USER_TALK
 +              ) {
                        $parts = explode( '/', $this->getTitle()->getText() );
                        $rootPart = $parts[0];
                        $user = User::newFromName( $rootPart, false /* allow IP users*/ );
  
                // the loop prepends the arrow image before the link, so the first case needs to be outside
  
 -              /**
 -               * @var $title Title
 -               */
 +              /** @var $title Title */
                $title = array_shift( $target );
  
                if ( $forceKnown ) {
  
                $nextRedirect = $wgStylePath . '/common/images/nextredirect' . $imageDir . '.png';
                $alt = $lang->isRTL() ? '←' : '→';
 -              // Automatically append redirect=no to each link, since most of them are redirect pages themselves.
 +
 +              // Automatically append redirect=no to each link, since most of them are
 +              // redirect pages themselves.
 +              /** @var Title $rt */
                foreach ( $target as $rt ) {
                        $link .= Html::element( 'img', array( 'src' => $nextRedirect, 'alt' => $alt ) );
                        if ( $forceKnown ) {
 -                              $link .= Linker::linkKnown( $rt, htmlspecialchars( $rt->getFullText(), array(), array( 'redirect' => 'no' ) ) );
 +                              $link .= Linker::linkKnown(
 +                                      $rt,
 +                                      htmlspecialchars( $rt->getFullText(),
 +                                      array(),
 +                                      array( 'redirect' => 'no' )
 +                              )
 +                              );
                        } else {
 -                              $link .= Linker::link( $rt, htmlspecialchars( $rt->getFullText() ), array(), array( 'redirect' => 'no' ) );
 +                              $link .= Linker::link(
 +                                      $rt,
 +                                      htmlspecialchars( $rt->getFullText() ),
 +                                      array(),
 +                                      array( 'redirect' => 'no' )
 +                              );
                        }
                }
  
         */
        public function render() {
                $this->getContext()->getOutput()->setArticleBodyOnly( true );
+               $this->getContext()->getOutput()->enableSectionEditLinks( false );
                $this->view();
        }
  
                        try {
                                $reason = $this->generateReason( $hasHistory );
                        } catch ( MWException $e ) {
 -                              # if a page is horribly broken, we still want to be able to delete it. so be lenient about errors here.
 +                              # if a page is horribly broken, we still want to be able to
 +                              # delete it. So be lenient about errors here.
                                wfDebug( "Error while building auto delete summary: $e" );
                                $reason = '';
                        }
                        if ( $this->mTitle->isBigDeletion() ) {
                                global $wgDeleteRevisionsLimit;
                                $this->getContext()->getOutput()->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
 -                                      array( 'delete-warning-toobig', $this->getContext()->getLanguage()->formatNum( $wgDeleteRevisionsLimit ) ) );
 +                                      array(
 +                                              'delete-warning-toobig',
 +                                              $this->getContext()->getLanguage()->formatNum( $wgDeleteRevisionsLimit )
 +                                      )
 +                              );
                        }
                }
  
                                        Xml::label( wfMessage( 'deletecomment' )->text(), 'wpDeleteReasonList' ) .
                                "</td>
                                <td class='mw-input'>" .
 -                                      Xml::listDropDown( 'wpDeleteReasonList',
 +                                      Xml::listDropDown(
 +                                              'wpDeleteReasonList',
                                                wfMessage( 'deletereason-dropdown' )->inContentLanguage()->text(),
 -                                              wfMessage( 'deletereasonotherlist' )->inContentLanguage()->text(), '', 'wpReasonDropDown', 1 ) .
 +                                              wfMessage( 'deletereasonotherlist' )->inContentLanguage()->text(),
 +                                              '',
 +                                              'wpReasonDropDown',
 +                                              1
 +                                      ) .
                                "</td>
                        </tr>
                        <tr id=\"wpDeleteReasonRow\">
                        </tr>" .
                        Xml::closeElement( 'table' ) .
                        Xml::closeElement( 'fieldset' ) .
 -                      Html::hidden( 'wpEditToken', $user->getEditToken( array( 'delete', $this->getTitle()->getPrefixedText() ) ) ) .
 +                      Html::hidden(
 +                              'wpEditToken',
 +                              $user->getEditToken( array( 'delete', $this->getTitle()->getPrefixedText() ) )
 +                      ) .
                        Xml::closeElement( 'form' );
  
                        if ( $user->isAllowed( 'editinterface' ) ) {
  
        /**
         * Perform a deletion and output success or failure messages
 -       * @param $reason
 -       * @param $suppress bool
 +       * @param string $reason
 +       * @param bool $suppress
         */
        public function doDelete( $reason, $suppress = false ) {
                $error = '';
                $outputPage = $this->getContext()->getOutput();
                $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error );
 +
                if ( $status->isGood() ) {
                        $deleted = $this->getTitle()->getPrefixedText();
  
                        $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
                        $outputPage->returnToMain( false );
                } else {
 -                      $outputPage->setPageTitle( wfMessage( 'cannotdelete-title', $this->getTitle()->getPrefixedText() ) );
 +                      $outputPage->setPageTitle(
 +                              wfMessage( 'cannotdelete-title',
 +                                      $this->getTitle()->getPrefixedText() )
 +                      );
 +
                        if ( $error == '' ) {
                                $outputPage->addWikiText(
                                        "<div class=\"error mw-error-cannotdelete\">\n" . $status->getWikiText() . "\n</div>"
                if ( $this->mContext instanceof IContextSource ) {
                        return $this->mContext;
                } else {
 -                      wfDebug( __METHOD__ . " called and \$mContext is null. Return RequestContext::getMain(); for sanity\n" );
 +                      wfDebug( __METHOD__ . " called and \$mContext is null. " .
 +                              "Return RequestContext::getMain(); for sanity\n" );
                        return RequestContext::getMain();
                }
        }
         * @param $user User
         * @return Status
         */
 -      public function doUpdateRestrictions( array $limit, array $expiry, &$cascade, $reason, User $user ) {
 +      public function doUpdateRestrictions( array $limit, array $expiry, &$cascade,
 +              $reason, User $user
 +      ) {
                return $this->mPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
        }
  
         * @param $expiry array
         * @return bool
         */
 -      public function updateRestrictions( $limit = array(), $reason = '', &$cascade = 0, $expiry = array() ) {
 +      public function updateRestrictions( $limit = array(), $reason = '',
 +              &$cascade = 0, $expiry = array()
 +      ) {
                return $this->mPage->doUpdateRestrictions(
                        $limit,
                        $expiry,
         * @param $error string
         * @return bool
         */
 -      public function doDeleteArticle( $reason, $suppress = false, $id = 0, $commit = true, &$error = '' ) {
 +      public function doDeleteArticle( $reason, $suppress = false, $id = 0,
 +              $commit = true, &$error = ''
 +      ) {
                return $this->mPage->doDeleteArticle( $reason, $suppress, $id, $commit, $error );
        }
  
diff --combined includes/OutputPage.php
@@@ -257,10 -257,15 +257,15 @@@ class OutputPage extends ContextSource 
        private $mTarget = null;
  
        /**
-        * @var bool: Whether output should contain table of contents
+        * @var bool: Whether parser output should contain table of contents
         */
        private $mEnableTOC = true;
  
+       /**
+        * @var bool: Whether parser output should contain section edit links
+        */
+       private $mEnableSectionEditLinks = true;
        /**
         * Constructor for OutputPage. This should not be called directly.
         * Instead a new RequestContext should be created and it will implicitly create
                        return false;
                }
                if ( !$wgCachePages ) {
 -                      wfDebug( __METHOD__ . ": CACHE DISABLED\n", 'log' );
 +                      wfDebug( __METHOD__ . ": CACHE DISABLED\n" );
                        return false;
                }
  
        function addParserOutput( &$parserOutput ) {
                $this->addParserOutputNoText( $parserOutput );
                $parserOutput->setTOCEnabled( $this->mEnableTOC );
+               // Touch section edit links only if not previously disabled
+               if ( $parserOutput->getEditSectionTokens() ) {
+                       $parserOutput->setEditSectionTokens( $this->mEnableSectionEditLinks );
+               }
                $text = $parserOutput->getText();
                wfRunHooks( 'OutputPageBeforeHTML', array( &$this, &$text ) );
                $this->addHTML( $text );
@@@ -3691,4 -3701,21 +3701,21 @@@ $template
        public function isTOCEnabled() {
                return $this->mEnableTOC;
        }
+       /**
+        * Enables/disables section edit links, doesn't override __NOEDITSECTION__
+        * @param bool $flag
+        * @since 1.23
+        */
+       public function enableSectionEditLinks( $flag = true ) {
+               $this->mEnableSectionEditLinks = $flag;
+       }
+       /**
+        * @return bool
+        * @since 1.23
+        */
+       public function sectionEditLinksEnabled() {
+               return $this->mEnableSectionEditLinks;
+       }
  }