Merge "Deprecate User::getPasswordValidity()"
[lhc/web/wiklou.git] / includes / Storage / DerivedPageDataUpdater.php
index b45aeba..9ce12b4 100644 (file)
@@ -44,6 +44,7 @@ use MediaWiki\Revision\RevisionRecord;
 use MediaWiki\Revision\RevisionRenderer;
 use MediaWiki\Revision\RevisionSlots;
 use MediaWiki\Revision\RevisionStore;
+use MediaWiki\Revision\SlotRoleRegistry;
 use MediaWiki\Revision\SlotRecord;
 use MediaWiki\User\UserIdentity;
 use MessageCache;
@@ -150,6 +151,9 @@ class DerivedPageDataUpdater implements IDBAccessObject {
         */
        private $options = [
                'changed' => true,
+               // newrev is true if prepareUpdate is handling the creation of a new revision,
+               // as opposed to a null edit or a forced update.
+               'newrev' => false,
                'created' => false,
                'moved' => false,
                'restored' => false,
@@ -209,6 +213,9 @@ class DerivedPageDataUpdater implements IDBAccessObject {
         */
        private $revisionRenderer;
 
+       /** @var SlotRoleRegistry */
+       private $slotRoleRegistry;
+
        /**
         * A stage identifier for managing the life cycle of this instance.
         * Possible stages are 'new', 'knows-current', 'has-content', 'has-revision', and 'done'.
@@ -255,6 +262,7 @@ class DerivedPageDataUpdater implements IDBAccessObject {
         * @param WikiPage $wikiPage ,
         * @param RevisionStore $revisionStore
         * @param RevisionRenderer $revisionRenderer
+        * @param SlotRoleRegistry $slotRoleRegistry
         * @param ParserCache $parserCache
         * @param JobQueueGroup $jobQueueGroup
         * @param MessageCache $messageCache
@@ -265,6 +273,7 @@ class DerivedPageDataUpdater implements IDBAccessObject {
                WikiPage $wikiPage,
                RevisionStore $revisionStore,
                RevisionRenderer $revisionRenderer,
+               SlotRoleRegistry $slotRoleRegistry,
                ParserCache $parserCache,
                JobQueueGroup $jobQueueGroup,
                MessageCache $messageCache,
@@ -276,6 +285,7 @@ class DerivedPageDataUpdater implements IDBAccessObject {
                $this->parserCache = $parserCache;
                $this->revisionStore = $revisionStore;
                $this->revisionRenderer = $revisionRenderer;
+               $this->slotRoleRegistry = $slotRoleRegistry;
                $this->jobQueueGroup = $jobQueueGroup;
                $this->messageCache = $messageCache;
                $this->contLang = $contLang;
@@ -636,12 +646,26 @@ class DerivedPageDataUpdater implements IDBAccessObject {
                $hasLinks = null;
 
                if ( $this->articleCountMethod === 'link' ) {
+                       // NOTE: it would be more appropriate to determine for each slot separately
+                       // whether it has links, and use that information with that slot's
+                       // isCountable() method. However, that would break parity with
+                       // WikiPage::isCountable, which uses the pagelinks table to determine
+                       // whether the current revision has links.
                        $hasLinks = (bool)count( $this->getCanonicalParserOutput()->getLinks() );
                }
 
-               // TODO: MCR: ask all slots if they have links [SlotHandler/PageTypeHandler]
-               $mainContent = $this->getRawContent( SlotRecord::MAIN );
-               return $mainContent->isCountable( $hasLinks );
+               foreach ( $this->getModifiedSlotRoles() as $role ) {
+                       $roleHandler = $this->slotRoleRegistry->getRoleHandler( $role );
+                       if ( $roleHandler->supportsArticleCount() ) {
+                               $content = $this->getRawContent( $role );
+
+                               if ( $content->isCountable( $hasLinks ) ) {
+                                       return true;
+                               }
+                       }
+               }
+
+               return false;
        }
 
        /**
@@ -649,6 +673,7 @@ class DerivedPageDataUpdater implements IDBAccessObject {
         */
        public function isRedirect() {
                // NOTE: main slot determines redirect status
+               // TODO: MCR: this should be controlled by a PageTypeHandler
                $mainContent = $this->getRawContent( SlotRecord::MAIN );
 
                return $mainContent->isRedirect();
@@ -738,17 +763,6 @@ class DerivedPageDataUpdater implements IDBAccessObject {
                        $stashedEdit = ApiStashEdit::checkCache( $title, $mainContent, $legacyUser );
                }
 
-               if ( $stashedEdit ) {
-                       /** @var ParserOutput $output */
-                       $output = $stashedEdit->output;
-
-                       // TODO: this should happen when stashing the ParserOutput, not now!
-                       $output->setCacheTime( $stashedEdit->timestamp );
-
-                       // TODO: MCR: allow output for all slots to be stashed.
-                       $this->canonicalParserOutput = $output;
-               }
-
                $userPopts = ParserOptions::newFromUserAndLang( $user, $this->contLang );
                Hooks::run( 'ArticlePrepareTextForEdit', [ $wikiPage, $userPopts ] );
 
@@ -829,6 +843,27 @@ class DerivedPageDataUpdater implements IDBAccessObject {
                } else {
                        $this->parentRevision = $parentRevision;
                }
+
+               $renderHints = [ 'use-master' => $this->useMaster(), 'audience' => RevisionRecord::RAW ];
+
+               if ( $stashedEdit ) {
+                       /** @var ParserOutput $output */
+                       $output = $stashedEdit->output;
+
+                       // TODO: this should happen when stashing the ParserOutput, not now!
+                       $output->setCacheTime( $stashedEdit->timestamp );
+
+                       $renderHints['known-revision-output'] = $output;
+               }
+
+               // NOTE: we want a canonical rendering, so don't pass $this->user or ParserOptions
+               // NOTE: the revision is either new or current, so we can bypass audience checks.
+               $this->renderedRevision = $this->revisionRenderer->getRenderedRevision(
+                       $this->revision,
+                       null,
+                       null,
+                       $renderHints
+               );
        }
 
        /**
@@ -855,18 +890,7 @@ class DerivedPageDataUpdater implements IDBAccessObject {
         * @return RenderedRevision
         */
        public function getRenderedRevision() {
-               if ( !$this->renderedRevision ) {
-                       $this->assertPrepared( __METHOD__ );
-
-                       // NOTE: we want a canonical rendering, so don't pass $this->user or ParserOptions
-                       // NOTE: the revision is either new or current, so we can bypass audience checks.
-                       $this->renderedRevision = $this->revisionRenderer->getRenderedRevision(
-                               $this->revision,
-                               null,
-                               null,
-                               [ 'use-master' => $this->useMaster(), 'audience' => RevisionRecord::RAW ]
-                       );
-               }
+               $this->assertPrepared( __METHOD__ );
 
                return $this->renderedRevision;
        }
@@ -1089,12 +1113,14 @@ class DerivedPageDataUpdater implements IDBAccessObject {
                // Override fields defined in $this->options with values from $options.
                $this->options = array_intersect_key( $options, $this->options ) + $this->options;
 
-               if ( isset( $this->pageState['oldId'] ) ) {
-                       $oldId = $this->pageState['oldId'];
+               if ( $this->revision ) {
+                       $oldId = $this->pageState['oldId'] ?? 0;
+                       $this->options['newrev'] = ( $revision->getId() !== $oldId );
                } elseif ( isset( $this->options['oldrevision'] ) ) {
                        /** @var Revision|RevisionRecord $oldRev */
                        $oldRev = $this->options['oldrevision'];
                        $oldId = $oldRev->getId();
+                       $this->options['newrev'] = ( $revision->getId() !== $oldId );
                } else {
                        $oldId = $revision->getParentId();
                }
@@ -1186,6 +1212,19 @@ class DerivedPageDataUpdater implements IDBAccessObject {
                // Prune any output that depends on the revision ID.
                if ( $this->renderedRevision ) {
                        $this->renderedRevision->updateRevision( $revision );
+               } else {
+
+                       // NOTE: we want a canonical rendering, so don't pass $this->user or ParserOptions
+                       // NOTE: the revision is either new or current, so we can bypass audience checks.
+                       $this->renderedRevision = $this->revisionRenderer->getRenderedRevision(
+                               $this->revision,
+                               null,
+                               null,
+                               [ 'use-master' => $this->useMaster(), 'audience' => RevisionRecord::RAW ]
+                       );
+
+                       // XXX: Since we presumably are dealing with the current revision,
+                       // we could try to get the ParserOutput from the parser cache.
                }
 
                // TODO: optionally get ParserOutput from the ParserCache here.
@@ -1383,12 +1422,9 @@ class DerivedPageDataUpdater implements IDBAccessObject {
                        // the recent change entry (also done via deferred updates) and carry over any
                        // bot/deletion/IP flags, ect.
                        $this->jobQueueGroup->lazyPush(
-                               new CategoryMembershipChangeJob(
+                               CategoryMembershipChangeJob::newSpec(
                                        $this->getTitle(),
-                                       [
-                                               'pageId' => $this->getPageId(),
-                                               'revTimestamp' => $this->revision->getTimestamp(),
-                                       ]
+                                       $this->revision->getTimestamp()
                                )
                        );
                }
@@ -1580,8 +1616,8 @@ class DerivedPageDataUpdater implements IDBAccessObject {
                // Save it to the parser cache. Use the revision timestamp in the case of a
                // freshly saved edit, as that matches page_touched and a mismatch would trigger an
                // unnecessary reparse.
-               $timestamp = $this->options['changed'] ? $this->revision->getTimestamp()
-                       : $output->getTimestamp();
+               $timestamp = $this->options['newrev'] ? $this->revision->getTimestamp()
+                       : $output->getCacheTime();
                $this->parserCache->save(
                        $output, $wikiPage, $this->getCanonicalParserOptions(),
                        $timestamp, $this->revision->getId()