Merge "DifferenceEngine: use a fake title when there's no real title"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 5 Sep 2018 12:18:34 +0000 (12:18 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 5 Sep 2018 12:18:34 +0000 (12:18 +0000)
1  2 
includes/diff/DifferenceEngine.php

@@@ -57,41 -57,29 +57,41 @@@ class DifferenceEngine extends ContextS
         */
        const DIFF_VERSION = '1.12';
  
 -      /** @var int Revision ID or 0 for current */
 +      /**
 +       * Revision ID for the old revision. 0 for the revision previous to $mNewid, false
 +       * if the diff does not have an old revision (e.g. 'oldid=<first revision of page>&diff=prev'),
 +       * or the revision does not exist, null if the revision is unsaved.
 +       * @var int|false|null
 +       */
        protected $mOldid;
  
 -      /** @var int|string Revision ID or null for current or an alias such as 'next' */
 +      /**
 +       * Revision ID for the new revision. 0 for the last revision of the current page
 +       * (as defined by the request context), false if the revision does not exist, null
 +       * if it is unsaved, or an alias such as 'next'.
 +       * @var int|string|false|null
 +       */
        protected $mNewid;
  
 -      private $mOldTags;
 -      private $mNewTags;
 -
        /**
         * Old revision (left pane).
         * Allowed to be an unsaved revision, unlikely that's ever needed though.
 -       * Null when the old revision does not exist; this can happen when using
 -       * diff=prev on the first revision.
 +       * False when the old revision does not exist; this can happen when using
 +       * diff=prev on the first revision. Null when the revision should exist but
 +       * doesn't (e.g. load failure); loadRevisionData() will return false in that
 +       * case. Also null until lazy-loaded. Ignored completely when isContentOverridden
 +       * is set.
         * Since 1.32 public access is deprecated.
 -       * @var Revision|null
 +       * @var Revision|null|false
         */
        protected $mOldRev;
  
        /**
         * New revision (right pane).
         * Note that this might be an unsaved revision (e.g. for edit preview).
 -       * Null only in case of load failure; diff methods will just return an error message in that case.
 +       * Null in case of load failure; diff methods will just return an error message in that case,
 +       * and loadRevisionData() will return false. Also null until lazy-loaded. Ignored completely
 +       * when isContentOverridden is set.
         * Since 1.32 public access is deprecated.
         * @var Revision|null
         */
         */
        protected $mNewPage;
  
 +      /**
 +       * Change tags of $mOldRev or null if it does not exist / is not saved.
 +       * @var string[]|null
 +       */
 +      private $mOldTags;
 +
 +      /**
 +       * Change tags of $mNewRev or null if it does not exist / is not saved.
 +       * @var string[]|null
 +       */
 +      private $mNewTags;
 +
        /**
         * @var Content|null
         * @deprecated since 1.32, content slots are now handled by the corresponding SlotDiffRenderer.
        /**
         * Get the old and new content objects for all slots.
         * This method does not do any permission checks.
 -       * @return array [ role => [ 'old' => SlotRecord, 'new' => SlotRecord ], ... ]
 +       * @return array [ role => [ 'old' => SlotRecord|null, 'new' => SlotRecord|null ], ... ]
         */
        protected function getSlotContents() {
                if ( $this->isContentOverridden ) {
                                        'new' => $this->mNewContent,
                                ]
                        ];
 +              } elseif ( !$this->loadRevisionData() ) {
 +                      return [];
                }
  
 -              $oldRev = $this->mOldRev->getRevisionRecord();
 -              $newRev = $this->mNewRev->getRevisionRecord();
 +              $newSlots = $this->mNewRev->getRevisionRecord()->getSlots()->getSlots();
 +              if ( $this->mOldRev ) {
 +                      $oldSlots = $this->mOldRev->getRevisionRecord()->getSlots()->getSlots();
 +              } else {
 +                      $oldSlots = [];
 +              }
                // The order here will determine the visual order of the diff. The current logic is
 -              // changed first, then added, then deleted. This is ad hoc and should not be relied on
 -              // - in the future we may want the ordering to depend on the page type.
 -              $roles = array_merge( $newRev->getSlotRoles(), $oldRev->getSlotRoles() );
 -              $oldSlots = $oldRev->getSlots()->getSlots();
 -              $newSlots = $newRev->getSlots()->getSlots();
 +              // slots of the new revision first in natural order, then deleted ones. This is ad hoc
 +              // and should not be relied on - in the future we may want the ordering to depend
 +              // on the page type.
 +              $roles = array_merge( array_keys( $newSlots ), array_keys( $oldSlots ) );
  
                $slots = [];
                foreach ( $roles as $role ) {
                return $slots;
        }
  
+       public function getTitle() {
+               // T202454 avoid errors when there is no title
+               return parent::getTitle() ?: Title::makeTitle( NS_SPECIAL, 'BadTitle/DifferenceEngine' );
+       }
        /**
         * Set reduced line numbers mode.
         * When set, line X is not displayed when X is 1, for example to increase readability and
        }
  
        /**
 -       * @return int
 +       * Get the ID of old revision (left pane) of the diff. 0 for the revision
 +       * previous to getNewid(), false if the old revision does not exist, null
 +       * if it's unsaved.
 +       * To get a real revision ID instead of 0, call loadRevisionData() first.
 +       * @return int|false|null
         */
        public function getOldid() {
                $this->loadRevisionIds();
        }
  
        /**
 -       * @return bool|int
 +       * Get the ID of new revision (right pane) of the diff. 0 for the current revision,
 +       * false if the new revision does not exist, null if it's unsaved.
 +       * To get a real revision ID instead of 0, call loadRevisionData() first.
 +       * @return int|false|null
         */
        public function getNewid() {
                $this->loadRevisionIds();
                return $difftext;
        }
  
 +      /**
 +       * Get the diff table body for one slot, without header
 +       *
 +       * @param string $role
 +       * @return string|false
 +       */
 +      public function getDiffBodyForRole( $role ) {
 +              $diffRenderers = $this->getSlotDiffRenderers();
 +              if ( !isset( $diffRenderers[$role] ) ) {
 +                      return false;
 +              }
 +
 +              $slotContents = $this->getSlotContents();
 +              $slotDiff = $diffRenderers[$role]->getDiff( $slotContents[$role]['old'],
 +                      $slotContents[$role]['new'] );
 +              if ( !$slotDiff ) {
 +                      return false;
 +              }
 +
 +              if ( $role !== 'main' ) {
 +                      // TODO use human-readable role name at least
 +                      $slotTitle = $role;
 +                      $slotDiff = $this->getSlotHeader( $slotTitle ) . $slotDiff;
 +              }
 +
 +              return $this->localiseDiff( $slotDiff );
 +      }
 +
        /**
         * Get a slot header for inclusion in a diff body (as a table row).
         *
                        $this->mOldContent = $oldRevision ? $oldRevision->getContent( 'main',
                                RevisionRecord::FOR_THIS_USER, $this->getUser() ) : null;
                } else {
 -                      $this->mOldRev = $this->mOldid = $this->mOldPage = null;
 +                      $this->mOldPage = null;
 +                      $this->mOldRev = $this->mOldid = false;
                }
                $this->mNewRev = new Revision( $newRevision );
                $this->mNewid = $newRevision->getId();
         * @param int $old Revision id, e.g. from URL parameter 'oldid'
         * @param int|string $new Revision id or strings 'next' or 'prev', e.g. from URL parameter 'diff'
         *
 -       * @return int[] List of two revision ids, older first, later second.
 +       * @return array List of two revision ids, older first, later second.
         *     Zero signifies invalid argument passed.
         *     false signifies that there is no previous/next revision ($old is the oldest/newest one).
         */
        }
  
        /**
 -       * Load revision metadata for the specified articles. If newid is 0, then compare
 -       * the old article in oldid to the current article; if oldid is 0, then
 -       * compare the current article to the immediately previous one (ignoring the
 -       * value of newid).
 +       * Load revision metadata for the specified revisions. If newid is 0, then compare
 +       * the old revision in oldid to the current revision of the current page (as defined
 +       * by the request context); if oldid is 0, then compare the revision in newid to the
 +       * immediately previous one.
         *
         * If oldid is false, leave the corresponding revision object set
 -       * to false. This is impossible via ordinary user input, and is provided for
 -       * API convenience.
 +       * to false. This can happen with 'diff=prev' pointing to a non-existent revision,
 +       * and is also used directly by the API.
         *
 -       * @return bool Whether both revisions were loaded successfully.
 +       * @return bool Whether both revisions were loaded successfully. Setting mOldRev
 +       *   to false counts as successful loading.
         */
        public function loadRevisionData() {
                if ( $this->mRevisionsLoaded ) {
 -                      return $this->isContentOverridden || $this->mNewRev && $this->mOldRev;
 +                      return $this->isContentOverridden || $this->mNewRev && !is_null( $this->mOldRev );
                }
  
                // Whether it succeeds or fails, we don't want to try again
  
        /**
         * Load the text of the revisions, as well as revision data.
 +       * When the old revision is missing (mOldRev is false), loading mOldContent is not attempted.
         *
         * @return bool Whether the content of both revisions could be loaded successfully.
 +       *   (When mOldRev is false, that still counts as a success.)
 +       *
         */
        public function loadText() {
                if ( $this->mTextLoaded == 2 ) {
 -                      return $this->loadRevisionData() && $this->mOldContent && $this->mNewContent;
 +                      return $this->loadRevisionData() && ( $this->mOldRev === false || $this->mOldContent )
 +                              && $this->mNewContent;
                }
  
                // Whether it succeeds or fails, we don't want to try again
                        }
                }
  
 -              if ( $this->mNewRev ) {
 -                      $this->mNewContent = $this->mNewRev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
 -                      Hooks::run( 'DifferenceEngineLoadTextAfterNewContentIsLoaded', [ $this ] );
 -                      if ( $this->mNewContent === null ) {
 -                              return false;
 -                      }
 +              $this->mNewContent = $this->mNewRev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
 +              Hooks::run( 'DifferenceEngineLoadTextAfterNewContentIsLoaded', [ $this ] );
 +              if ( $this->mNewContent === null ) {
 +                      return false;
                }
  
                return true;