wikimedia/mediawiki-extensions-Wikibase

View on GitHub
repo/includes/UpdateRepo/UpdateRepoOnMoveJob.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

declare( strict_types = 1 );

namespace Wikibase\Repo\UpdateRepo;

use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
use MediaWiki\Site\SiteLookup;
use MediaWiki\Title\Title;
use OutOfBoundsException;
use Psr\Log\LoggerInterface;
use Wikibase\DataModel\Entity\Item;
use Wikibase\DataModel\Services\Lookup\EntityLookup;
use Wikibase\DataModel\SiteLink;
use Wikibase\Lib\FormatableSummary;
use Wikibase\Lib\SettingsArray;
use Wikibase\Lib\Store\EntityStore;
use Wikibase\Lib\Store\LookupConstants;
use Wikibase\Lib\Summary;
use Wikibase\Repo\EditEntity\MediaWikiEditEntityFactory;
use Wikibase\Repo\Store\Store;
use Wikibase\Repo\SummaryFormatter;
use Wikibase\Repo\WikibaseRepo;

/**
 * Job for updating the repo after a page on the client has been moved.
 *
 * @license GPL-2.0-or-later
 * @author Marius Hoch < hoo@online.de >
 */
class UpdateRepoOnMoveJob extends UpdateRepoJob {

    /**
     * @var SiteLookup
     */
    private $siteLookup;

    /**
     * @var string|bool|null
     */
    private $normalizedPageName = null;

    /**
     * Constructs a UpdateRepoOnMoveJob propagating a page move to the repo
     *
     * @note This is for use by Job::factory, don't call it directly;
     *           use newFrom*() instead.
     *
     * @note the constructor's signature is dictated by Job::factory, so we'll have to
     *           live with it even though it's rather ugly for our use case.
     *
     * @see Job::factory
     * @see UpdateRepoJob::__construct
     *
     * @param Title $title
     * @param array $params
     */
    public function __construct( Title $title, array $params = [] ) {
        parent::__construct( 'UpdateRepoOnMove', $title, $params );

        $this->initRepoJobServicesFromGlobalState();
    }

    protected function initRepoJobServicesFromGlobalState(): void {
        $services = MediaWikiServices::getInstance();

        $this->initServices(
            WikibaseRepo::getStore( $services )->getEntityLookup(
                Store::LOOKUP_CACHING_DISABLED,
                LookupConstants::LATEST_FROM_MASTER
            ),
            WikibaseRepo::getEntityStore( $services ),
            WikibaseRepo::getSummaryFormatter( $services ),
            LoggerFactory::getInstance( 'UpdateRepo' ),
            $services->getSiteLookup(),
            WikibaseRepo::getEditEntityFactory( $services ),
            WikibaseRepo::getSettings( $services )
        );
    }

    public function initServices(
        EntityLookup $entityLookup,
        EntityStore $entityStore,
        SummaryFormatter $summaryFormatter,
        LoggerInterface $logger,
        SiteLookup $siteLookup,
        MediaWikiEditEntityFactory $editEntityFactory,
        SettingsArray $settings
    ): void {
        $this->initRepoJobServices(
            $entityLookup,
            $entityStore,
            $summaryFormatter,
            $logger,
            $editEntityFactory,
            $settings
        );
        $this->siteLookup = $siteLookup;
    }

    /**
     * Get a SiteLink for a specific item and site
     */
    private function getSiteLink( Item $item, string $globalId ): ?SiteLink {
        try {
            return $item->getSiteLinkList()->getBySiteId( $globalId );
        } catch ( OutOfBoundsException $e ) {
            return null;
        }
    }

    /**
     * Get a Summary object for the edit
     */
    public function getSummary(): FormatableSummary {
        $params = $this->getParams();
        $siteId = $params['siteId'];
        $oldPage = $params['oldTitle'];
        $newPage = $params['newTitle'];

        return new Summary(
            'clientsitelink',
            'update',
            $siteId,
            [
                $siteId . ":$oldPage",
                $siteId . ":$newPage",
            ]
        );
    }

    /**
     * @return string|bool False in case the normalization failed
     */
    private function getNormalizedPageName() {
        if ( $this->normalizedPageName === null ) {
            $params = $this->getParams();
            $newPage = $params['newTitle'];
            $siteId = $params['siteId'];

            $site = $this->siteLookup->getSite( $siteId );
            $this->normalizedPageName = $site->normalizePageName( $newPage );

            if ( $this->normalizedPageName === false ) {
                $this->logger->debug(
                    'OnMove: Normalizing the page name {newPage} on {siteId} failed',
                    [
                        'newPage' => $newPage,
                        'siteId' => $siteId,
                    ]
                );
            }

        }

        return $this->normalizedPageName;
    }

    /**
     * Whether the propagated update is valid (and thus should be applied)
     */
    protected function verifyValid( Item $item ): bool {
        $params = $this->getParams();
        $siteId = $params['siteId'];
        $oldPage = $params['oldTitle'];

        $oldSiteLink = $this->getSiteLink( $item, $siteId );
        if ( !$oldSiteLink || $oldSiteLink->getPageName() !== $oldPage ) {
            // Probably something changed since the job has been inserted
            $this->logger->debug(
                'OnMove: The site link to {siteId} is no longer {oldPage}',
                [
                    'siteId' => $siteId,
                    'oldPage' => $oldPage,
                ]
            );
            return false;
        }

        // Normalize the name, just in case the page has been updated in the mean time
        if ( $this->getNormalizedPageName() === false ) {
            return false;
        }

        return true;
    }

    protected function applyChanges( Item $item ): bool {
        $params = $this->getParams();
        $siteId = $params['siteId'];
        $oldSiteLink = $this->getSiteLink( $item, $siteId );

        $siteLink = new SiteLink(
            $siteId,
            $this->getNormalizedPageName(),
            $oldSiteLink->getBadges() // Keep badges
        );

        $item->getSiteLinkList()->removeLinkWithSiteId( $siteId );
        $item->getSiteLinkList()->addSiteLink( $siteLink );

        return true;
    }

}