Merge "Adding a bunch of hooks from wikiHow into DifferenceEngine, 2nd try"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 8 Aug 2016 16:30:59 +0000 (16:30 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 8 Aug 2016 16:30:59 +0000 (16:30 +0000)
1  2 
docs/hooks.txt
includes/diff/DifferenceEngine.php

diff --combined docs/hooks.txt
@@@ -608,7 -608,6 +608,7 @@@ $reason: the reason the article was del
  $id: id of the article that was deleted
  $content: the Content of the deleted page
  $logEntry: the ManualLogEntry used to record the deletion
 +$archivedRevisionCount: the number of revisions archived during the deletion
  
  'ArticleEditUpdateNewTalk': Before updating user_newtalk when a user talk page
  was changed.
@@@ -904,7 -903,6 +904,7 @@@ $image: Fil
  'BlockIpComplete': After an IP address or user is blocked.
  $block: the Block object that was saved
  $user: the user who did the block (not the one being blocked)
 +$priorBlock: the Block object for the prior block or null if there was none
  
  'BookInformation': Before information output on Special:Booksources.
  $isbn: ISBN to show information for
@@@ -1104,9 -1102,6 +1104,9 @@@ $row: the DB row for this lin
  $id: User identifier
  $title: User page title
  &$tools: Array of tool links
 +$specialPage: SpecialPage instance for context and services. Can be either
 +  SpecialContributions or DeletedContributionsPage. Extensions should type
 +  hint against a generic SpecialPage though.
  
  'ConvertContent': Called by AbstractContent::convert when a conversion to
  another content model is requested.
@@@ -1145,6 -1140,85 +1145,85 @@@ $page: SpecialPage object for DeletedCo
  $row: the DB row for this line
  &$classes: the classes to add to the surrounding <li>
  
+ 'DifferenceEngineMarkPatrolledLink': Allows extensions to change the "mark as patrolled" link
+ which is shown both on the diff header as well as on the bottom of a page, usually
+ wrapped in a span element which has class="patrollink".
+ $differenceEngine: DifferenceEngine object
+ &$markAsPatrolledLink: The "mark as patrolled" link HTML (string)
+ $rcid: Recent change ID (rc_id) for this change (int)
+ $token: Patrol token; $rcid is used in generating this variable
+ 'DifferenceEngineMarkPatrolledRCID': Allows extensions to possibly change the rcid parameter.
+ For example the rcid might be set to zero due to the user being the same as the
+ performer of the change but an extension might still want to show it under certain
+ conditions.
+ &$rcid: rc_id (int) of the change or 0
+ $differenceEngine: DifferenceEngine object
+ $change: RecentChange object
+ $user: User object representing the current user
+ 'DifferenceEngineNewHeader': Allows extensions to change the $newHeader variable, which
+ contains information about the new revision, such as the revision's author, whether
+ the revision was marked as a minor edit or not, etc.
+ $differenceEngine: DifferenceEngine object
+ &$newHeader: The string containing the various #mw-diff-otitle[1-5] divs, which
+ include things like revision author info, revision comment, RevisionDelete link and more
+ $formattedRevisionTools: Array containing revision tools, some of which may have
+ been injected with the DiffRevisionTools hook
+ $nextlink: String containing the link to the next revision (if any); also included in $newHeader
+ $rollback: Rollback link (string) to roll this revision back to the previous one, if any
+ $newminor: String indicating if the new revision was marked as a minor edit
+ $diffOnly: Boolean parameter passed to DifferenceEngine#showDiffPage, indicating
+ whether we should show just the diff; passed in as a query string parameter to the
+ various URLs constructed here (i.e. $nextlink)
+ $rdel: RevisionDelete link for the new revision, if the current user is allowed
+ to use the RevisionDelete feature
+ $unhide: Boolean parameter indicating whether to show RevisionDeleted revisions
+ 'DifferenceEngineOldHeader': Allows extensions to change the $oldHeader variable, which
+ contains information about the old revision, such as the revision's author, whether
+ the revision was marked as a minor edit or not, etc.
+ $differenceEngine: DifferenceEngine object
+ &$oldHeader: The string containing the various #mw-diff-otitle[1-5] divs, which
+ include things like revision author info, revision comment, RevisionDelete link and more
+ $prevlink: String containing the link to the previous revision (if any); also included in $oldHeader
+ $oldminor: String indicating if the old revision was marked as a minor edit
+ $diffOnly: Boolean parameter passed to DifferenceEngine#showDiffPage, indicating
+ whether we should show just the diff; passed in as a query string parameter to the
+ various URLs constructed here (i.e. $prevlink)
+ $ldel: RevisionDelete link for the old revision, if the current user is allowed
+ to use the RevisionDelete feature
+ $unhide: Boolean parameter indicating whether to show RevisionDeleted revisions
+ 'DifferenceEngineOldHeaderNoOldRev': Change the $oldHeader variable in cases when
+ there is no old revision
+ &$oldHeader: empty string by default
+ 'DifferenceEngineRenderRevisionAddParserOutput': Allows extensions to change the parser output.
+ Return false to not add parser output via OutputPage's addParserOutput method.
+ $differenceEngine: DifferenceEngine object
+ $out: OutputPage object
+ $parserOutput: ParserOutput object
+ $wikiPage: WikiPage object
+ DifferenceEngineRenderRevisionShowFinalPatrolLink': An extension can hook into this hook
+ point and return false to not show the final "mark as patrolled" link on the bottom
+ of a page.
+ This hook has no arguments.
+ 'DifferenceEngineShowDiff': Allows extensions to affect the diff text which
+ eventually gets sent to the OutputPage object.
+ $differenceEngine: DifferenceEngine object
+ 'DifferenceEngineShowEmptyOldContent': Allows extensions to change the diff table
+ body (without header) in cases when there is no old revision or the old and new
+ revisions are identical.
+ $differenceEngine: DifferenceEngine object
+ 'DifferenceEngineShowDiffPage': Add additional output via the available OutputPage
+ object into the diff view
+ $out: OutputPage object
  'DiffRevisionTools': Override or extend the revision tools available from the
  diff view, i.e. undo, etc.
  $newRev: Revision object of the "new" revision
@@@ -2364,12 -2438,24 +2443,12 @@@ cache or return false to not use it
  &$parser: Parser object
  &$varCache: variable cache (array)
  
 -'ParserLimitReport': DEPRECATED! Use ParserLimitReportPrepare and
 -ParserLimitReportFormat instead.
 +'ParserLimitReport': DEPRECATED! Use ParserLimitReportPrepare instead.
  Called at the end of Parser:parse() when the parser will
  include comments about size of the text parsed.
  $parser: Parser object
  &$limitReport: text that will be included (without comment tags)
  
 -'ParserLimitReportFormat': Called for each row in the parser limit report that
 -needs formatting. If nothing handles this hook, the default is to use "$key" to
 -get the label, and "$key-value" or "$key-value-text"/"$key-value-html" to
 -format the value.
 -$key: Key for the limit report item (string)
 -&$value: Value of the limit report item
 -&$report: String onto which to append the data
 -$isHTML: If true, $report is an HTML table with two columns; if false, it's
 -  text intended for display in a monospaced font.
 -$localize: If false, $report should be output in English.
 -
  'ParserLimitReportPrepare': Called at the end of Parser:parse() when the parser
  will include comments about size of the text parsed. Hooks should use
  $output->setLimitReportData() to populate data. Functions for this hook should
@@@ -2624,18 -2710,6 +2703,18 @@@ search results
  $title: Current Title object being displayed in search results.
  &$id: Revision ID (default is false, for latest)
  
 +'SearchIndexFields': Add fields to search index mapping.
 +&$fields: Array of fields, all implement SearchIndexField
 +$engine: SearchEngine instance for which mapping is being built.
 +
 +'SearchDataForIndex': Add data to search document. Allows to add any data to
 +the field map used to index the document.
 +&$fields: Array of name => value pairs for fields
 +$handler: ContentHandler for the content being indexed
 +$page: WikiPage that is being indexed
 +$output: ParserOutput that is produced from the page
 +$engine: SearchEngine for which the indexing is intended
 +
  'SecondaryDataUpdates': Allows modification of the list of DataUpdates to
  perform when page content is modified. Currently called by
  AbstractContent::getSecondaryDataUpdates.
@@@ -3307,10 -3381,9 +3386,10 @@@ in most cases over UploadVerification
  $upload: (object) an instance of UploadBase, with all info about the upload
  $mime: (string) The uploaded file's MIME type, as detected by MediaWiki.
    Handlers will typically only apply for specific MIME types.
 -&$error: (object) output: true if the file is valid. Otherwise, an indexed array
 -  representing the problem with the file, where the first element is the message
 -  key and the remaining elements are used as parameters to the message.
 +&$error: (object) output: true if the file is valid. Otherwise, set this to the reason
 +  in the form of array( messagename, param1, param2, ... ) or a MessageSpecifier
 +  instance (you might want to use ApiMessage to provide machine-readable details
 +  for the API).
  
  'UploadVerifyUpload': Upload verification, based on both file properties like
  MIME type (same as UploadVerifyFile) and the information entered by the user
@@@ -3471,9 -3544,6 +3550,9 @@@ $user: User object for the logged-in us
  For functionality that needs to run after any login (API or web) use UserLoggedIn.
  &$user: the user object that was created on login
  &$inject_html: Any HTML to inject after the "logged in" message.
 +$direct: (bool) The hook is called directly after a successful login. This will only happen once
 +  per login. A UserLoginComplete call with direct=false can happen when the user visits the login
 +  page while already logged in.
  
  'UserLoginForm': DEPRECATED! Create an AuthenticationProvider instead.
  Manipulate the login form.
   * @ingroup DifferenceEngine
   */
  
 -/**
 - * Constant to indicate diff cache compatibility.
 - * Bump this when changing the diff formatting in a way that
 - * fixes important bugs or such to force cached diff views to
 - * clear.
 - */
 +// Deprecated, use class constant instead
  define( 'MW_DIFF_VERSION', '1.11a' );
  
  /**
   * @ingroup DifferenceEngine
   */
  class DifferenceEngine extends ContextSource {
 +      /**
 +       * Constant to indicate diff cache compatibility.
 +       * Bump this when changing the diff formatting in a way that
 +       * fixes important bugs or such to force cached diff views to
 +       * clear.
 +       */
 +      const DIFF_VERSION = MW_DIFF_VERSION;
  
        /** @var int */
        public $mOldid;
        }
  
        public function showDiffPage( $diffOnly = false ) {
                # Allow frames except in certain special cases
                $out = $this->getOutput();
                $out->allowClickjacking();
                $out->setRobotPolicy( 'noindex,nofollow' );
  
+               // Allow extensions to add any extra output here
+               Hooks::run( 'DifferenceEngineShowDiffPage', [ $out ] );
                if ( !$this->loadRevisionData() ) {
                        $this->showMissingRevision();
  
                        $out->setPageTitle( $this->msg( 'difference-title', $this->mNewPage->getPrefixedText() ) );
                        $samePage = true;
                        $oldHeader = '';
+                       // Allow extensions to change the $oldHeader variable
+                       Hooks::run( 'DifferenceEngineOldHeaderNoOldRev', [ &$oldHeader ] );
                } else {
                        Hooks::run( 'DiffViewHeader', [ $this, $this->mOldRev, $this->mNewRev ] );
  
                                '<div id="mw-diff-otitle5">' . $oldChangeTags[0] . '</div>' .
                                '<div id="mw-diff-otitle4">' . $prevlink . '</div>';
  
+                       // Allow extensions to change the $oldHeader variable
+                       Hooks::run( 'DifferenceEngineOldHeader', [ $this, &$oldHeader, $prevlink, $oldminor,
+                               $diffOnly, $ldel, $this->unhide ] );
                        if ( $this->mOldRev->isDeleted( Revision::DELETED_TEXT ) ) {
                                $deleted = true; // old revisions text is hidden
                                if ( $this->mOldRev->isDeleted( Revision::DELETED_RESTRICTED ) ) {
                        '<div id="mw-diff-ntitle5">' . $newChangeTags[0] . '</div>' .
                        '<div id="mw-diff-ntitle4">' . $nextlink . $this->markPatrolledLink() . '</div>';
  
+               // Allow extensions to change the $newHeader variable
+               Hooks::run( 'DifferenceEngineNewHeader', [ $this, &$newHeader, $formattedRevisionTools,
+                       $nextlink, $rollback, $newminor, $diffOnly, $rdel, $this->unhide ] );
                if ( $this->mNewRev->isDeleted( Revision::DELETED_TEXT ) ) {
                        $deleted = true; // new revisions text is hidden
                        if ( $this->mNewRev->isDeleted( Revision::DELETED_RESTRICTED ) ) {
                                                        'token' => $linkInfo['token'],
                                                ]
                                        ) . ']</span>';
+                               // Allow extensions to change the markpatrolled link
+                               Hooks::run( 'DifferenceEngineMarkPatrolledLink', [ $this,
+                                       &$this->mMarkPatrolledLink, $linkInfo['rcid'], $linkInfo['token'] ] );
                        }
                }
                return $this->mMarkPatrolledLink;
                                // If the user could patrol this it already would be patrolled
                                $rcid = 0;
                        }
+                       // Allow extensions to possibly change the rcid here
+                       // For example the rcid might be set to zero due to the user
+                       // being the same as the performer of the change but an extension
+                       // might still want to show it under certain conditions
+                       Hooks::run( 'DifferenceEngineMarkPatrolledRCID', [ &$rcid, $this, $change, $user ] );
                        // Build the link
                        if ( $rcid ) {
                                $this->getOutput()->preventClickjacking();
 +                              $this->getOutput()->addModuleStyles( 'mediawiki.page.patrol' );
                                if ( $wgEnableAPI && $wgEnableWriteAPI
                                        && $user->isAllowed( 'writeapi' )
                                ) {
  
                                # WikiPage::getParserOutput() should not return false, but just in case
                                if ( $parserOutput ) {
-                                       $out->addParserOutput( $parserOutput );
+                                       // Allow extensions to change parser output here
+                                       if ( Hooks::run( 'DifferenceEngineRenderRevisionAddParserOutput', [ $this, $out, $parserOutput, $wikiPage ] ) ) {
+                                               $out->addParserOutput( $parserOutput );
+                                       }
                                }
                        }
                }
                # @codingStandardsIgnoreEnd
  
-               # Add redundant patrol link on bottom...
-               $out->addHTML( $this->markPatrolledLink() );
+               // Allow extensions to optionally not show the final patrolled link
+               if ( Hooks::run( 'DifferenceEngineRenderRevisionShowFinalPatrolLink' ) ) {
+                       # Add redundant patrol link on bottom...
+                       $out->addHTML( $this->markPatrolledLink() );
+               }
        }
  
        protected function getParserOutput( WikiPage $page, Revision $rev ) {
         * @return bool
         */
        public function showDiff( $otitle, $ntitle, $notice = '' ) {
+               // Allow extensions to affect the output here
+               Hooks::run( 'DifferenceEngineShowDiff', [ $this ] );
                $diff = $this->getDiff( $otitle, $ntitle, $notice );
                if ( $diff === false ) {
                        $this->showMissingRevision();
                if ( $this->mOldRev === false || ( $this->mOldRev && $this->mNewRev
                        && $this->mOldRev->getId() == $this->mNewRev->getId() )
                ) {
-                       return '';
+                       if ( Hooks::run( 'DifferenceEngineShowEmptyOldContent', [ $this ] ) ) {
+                               return '';
+                       }
                }
                // Cacheable?
                $key = false;
                        throw new MWException( 'mOldid and mNewid must be set to get diff cache key.' );
                }
  
 -              return wfMemcKey( 'diff', 'version', MW_DIFF_VERSION,
 +              return wfMemcKey( 'diff', 'version', self::DIFF_VERSION,
                        'oldid', $this->mOldid, 'newid', $this->mNewid );
        }
  
                return $this->generateTextDiffBody( $otext, $ntext );
        }
  
 -      /**
 -       * Generate a diff, no caching
 -       *
 -       * @param string $otext Old text, must be already segmented
 -       * @param string $ntext New text, must be already segmented
 -       *
 -       * @return bool|string
 -       * @deprecated since 1.21, use generateContentDiffBody() instead!
 -       */
 -      public function generateDiffBody( $otext, $ntext ) {
 -              ContentHandler::deprecated( __METHOD__, "1.21" );
 -
 -              return $this->generateTextDiffBody( $otext, $ntext );
 -      }
 -
        /**
         * Generate a diff, no caching
         *
                } elseif ( $wgExternalDiffEngine == 'wikidiff2' ) {
                        // Same as above, but with no deprecation warnings
                        $wgExternalDiffEngine = false;
 -              } elseif ( !is_string( $wgExternalDiffEngine ) ) {
 +              } elseif ( !is_string( $wgExternalDiffEngine ) && $wgExternalDiffEngine !== false ) {
                        // And prevent people from shooting themselves in the foot...
                        wfWarn( '$wgExternalDiffEngine is set to a non-string value, forcing it to false' );
                        $wgExternalDiffEngine = false;