wikimedia/mediawiki-extensions-Wikibase

View on GitHub
repo/includes/Notifications/WikiPageActionEntityChangeFactory.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

declare( strict_types=1 );

namespace Wikibase\Repo\Notifications;

use LogicException;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Revision\SlotRecord;
use MediaWiki\User\CentralId\CentralIdLookup;
use MediaWiki\User\UserIdentity;
use Wikibase\DataModel\Entity\EntityDocument;
use Wikibase\Lib\Changes\ChangeRow;
use Wikibase\Lib\Changes\EntityChange;
use Wikibase\Lib\Changes\EntityChangeFactory;
use Wikibase\Repo\Content\EntityContent;

/**
 * Factory for creating EntityChange objects for repo wiki page actions that clients need to be notified for.
 *
 * @license GPL-2.0-or-later
 */
class WikiPageActionEntityChangeFactory {

    private EntityChangeFactory $changeFactory;

    private ?CentralIdLookup $centralIdLookup;

    public function __construct(
        EntityChangeFactory $changeFactory,
        ?CentralIdLookup $centralIdLookup
    ) {
        $this->changeFactory = $changeFactory;
        $this->centralIdLookup = $centralIdLookup;
    }

    public function newForPageDeleted( EntityContent $content, UserIdentity $user, string $timestamp ): EntityChange {
        $change = $this->changeFactory->newFromUpdate( EntityChange::REMOVE, $content->getEntity() );
        $change->setTimestamp( $timestamp );
        $this->setEntityChangeUserInfo(
            $change,
            $user,
            $this->getCentralUserId( $user )
        );

        return $change;
    }

    public function newForPageUndeleted( EntityContent $content, RevisionRecord $revisionRecord ): EntityChange {
        $change = $this->changeFactory->newFromUpdate( EntityChange::RESTORE, null, $content->getEntity() );

        $this->setEntityChangeRevisionInfo(
            $change,
            $revisionRecord,
            /* Will get set below in setMetadataFromUser */ 0
        );

        // We don't want the change entries of newly undeleted pages to have
        // the timestamp of the original change.
        $change->setTimestamp( wfTimestampNow() );

        $user = $revisionRecord->getUser();
        $this->setEntityChangeUserInfo(
            $change,
            $user,
            $this->getCentralUserId( $user )
        );

        return $change;
    }

    public function newForPageCreated( EntityContent $content, RevisionRecord $revisionRecord ): EntityChange {
        $change = $this->changeFactory->newFromUpdate( EntityChange::ADD, null, $content->getEntity() );
        $this->setEntityChangeRevisionInfo(
            $change,
            $revisionRecord,
            $this->getCentralUserId( $revisionRecord->getUser() )
        );

        return $change;
    }

    public function newForPageModified( RevisionRecord $currentRevision, EntityContent $parentRevisionContent ): EntityChange {
        /** @var EntityContent $currentRevisionContent */
        $currentRevisionContent = $currentRevision->getContent( SlotRecord::MAIN );
        '@phan-var EntityContent $currentRevisionContent';

        $change = $this->getChangeForModification(
            $parentRevisionContent->isRedirect() ? null : $parentRevisionContent->getEntity(),
            $currentRevisionContent->isRedirect() ? null : $currentRevisionContent->getEntity()
        );

        $this->setEntityChangeRevisionInfo(
            $change,
            $currentRevision,
            $this->getCentralUserId( $currentRevision->getUser() )
        );

        return $change;
    }

    /**
     * Returns a EntityChange based on the old and new content object, taking
     * redirects into consideration.
     */
    private function getChangeForModification( ?EntityDocument $oldEntity, ?EntityDocument $newEntity ): EntityChange {
        if ( $newEntity === null ) {
            // The new version is a redirect. For now, treat that as a deletion.
            $action = EntityChange::REMOVE;
        } elseif ( $oldEntity === null ) {
            // The old version is a redirect. For now, treat that like restoring the entity.
            $action = EntityChange::RESTORE;
        } else {
            // No redirects involved
            $action = EntityChange::UPDATE;
        }

        return $this->changeFactory->newFromUpdate( $action, $oldEntity, $newEntity );
    }

    /**
     * @param UserIdentity $user Repository user
     *
     * @return int Central user ID, or 0
     */
    private function getCentralUserId( UserIdentity $user ): int {
        if ( $this->centralIdLookup ) {
            return $this->centralIdLookup->centralIdFromLocalUser( $user );
        }

        return 0;
    }

    /**
     * @param EntityChange $change
     * @param UserIdentity $user User that made change
     * @param int $centralUserId Central user ID, or 0 if unknown or not applicable
     *   (see docs/change-propagation.md)
     */
    private function setEntityChangeUserInfo( EntityChange $change, UserIdentity $user, $centralUserId ): void {
        $change->addUserMetadata(
            $user->getId(),
            $user->getName(),
            $centralUserId
        );
    }

    /**
     * @param EntityChange $change
     * @param RevisionRecord $revision Revision to populate EntityChange from
     * @param int $centralUserId Central user ID, or 0 if unknown or not applicable
     *   (see docs/change-propagation.md)
     */
    private function setEntityChangeRevisionInfo( EntityChange $change, RevisionRecord $revision, int $centralUserId ): void {
        $change->setFields( [
            ChangeRow::REVISION_ID => $revision->getId(),
            ChangeRow::TIME => $revision->getTimestamp(),
        ] );

        if ( !$change->hasField( ChangeRow::OBJECT_ID ) ) {
            throw new LogicException(
                'EntityChange::setRevisionInfo() called without calling setEntityId() first!'
            );
        }

        $comment = $revision->getComment();
        $change->setMetadata( [
            'page_id' => $revision->getPageId(),
            'parent_id' => $revision->getParentId(),
            'comment' => $comment ? $comment->text : null,
            'rev_id' => $revision->getId(),
        ] );

        $user = $revision->getUser();
        $change->addUserMetadata(
            $user ? $user->getId() : 0,
            $user ? $user->getName() : '',
            $centralUserId
        );
    }

}