Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
85.19% covered (warning)
85.19%
23 / 27
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
WikidataTemplateLookup
85.19% covered (warning)
85.19%
23 / 27
80.00% covered (warning)
80.00%
4 / 5
11.39
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 fetchNowCommonsLocalTitle
20.00% covered (danger)
20.00%
1 / 5
0.00% covered (danger)
0.00%
0 / 1
4.05
 fetchLocalTemplateForSource
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 fetchSiteLinkPageName
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 removeNamespace
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace FileImporter\Services;
4
5use FileImporter\Data\SourceUrl;
6use FileImporter\Remote\MediaWiki\SiteTableSiteLookup;
7use FileImporter\Services\Http\HttpRequestExecutor;
8use MediaWiki\Config\Config;
9use Psr\Log\LoggerInterface;
10use RuntimeException;
11
12/**
13 * FIXME: Rename the class to something like WikibaseSiteLinkLookup, and just remove all occurences
14 * of "NowCommons" as well as "Template" from this code. Reasoning: Even if this code was written
15 * for a very specific purpose (the NowCommons template), this class does not contain any knowledge
16 * about templates, and no knowledge about a specific Wikibase installation.
17 *
18 * This service fetches an Item from a Wikibase instance (both specified via configuration), and
19 * returns the sitelink that matches a given source URL, if such a sitelink exists. In other words:
20 * It checks if the source site contains a localized version of a page, and returns it.
21 *
22 * @license GPL-2.0-or-later
23 */
24class WikidataTemplateLookup {
25
26    private SiteTableSiteLookup $siteLookup;
27    private HttpRequestExecutor $requestExecutor;
28    private LoggerInterface $logger;
29    /** @var string */
30    private $entityEndpoint;
31    /** @var string|null */
32    private $nowCommonsEntityId;
33
34    /** @var string[][] Array mapping site id and entity id to a template title name */
35    private $templateCache = [];
36
37    public function __construct(
38        Config $config,
39        SiteTableSiteLookup $siteLookup,
40        HttpRequestExecutor $requestExecutor,
41        LoggerInterface $logger
42    ) {
43        $this->siteLookup = $siteLookup;
44        $this->requestExecutor = $requestExecutor;
45        $this->logger = $logger;
46
47        $this->entityEndpoint = $config->get( 'FileImporterWikidataEntityEndpoint' );
48        $this->nowCommonsEntityId = $config->get( 'FileImporterWikidataNowCommonsEntity' ) ?: null;
49    }
50
51    /**
52     * Fetch the source wiki template title corresponding to `NowCommons`
53     *
54     * @param SourceUrl $sourceUrl Source URL, used to look up source site ID.
55     *
56     * @return string|null Local template title, without namespace prefix.
57     */
58    public function fetchNowCommonsLocalTitle( SourceUrl $sourceUrl ): ?string {
59        try {
60            return $this->fetchLocalTemplateForSource( $this->nowCommonsEntityId, $sourceUrl );
61        } catch ( RuntimeException $ex ) {
62            $this->logger->error( 'Failed to fetch template mapping from Wikidata: ' .
63                $ex->getMessage() );
64            return null;
65        }
66    }
67
68    private function fetchLocalTemplateForSource( ?string $entityId, SourceUrl $sourceUrl ): ?string {
69        $sourceSite = $this->siteLookup->getSite( $sourceUrl );
70        if ( !$sourceSite || !$entityId ) {
71            return null;
72        }
73
74        $localPageName = $this->fetchSiteLinkPageName( $entityId, $sourceSite->getGlobalId() );
75        if ( $localPageName === null ) {
76            return null;
77        }
78
79        return $this->removeNamespace( $localPageName );
80    }
81
82    private function fetchSiteLinkPageName( string $entityId, string $siteId ): ?string {
83        if ( isset( $this->templateCache[$siteId][$entityId] ) ) {
84            return $this->templateCache[$siteId][$entityId];
85        }
86
87        $url = $this->entityEndpoint . $entityId;
88        $response = $this->requestExecutor->execute( $url );
89        $entityData = json_decode( $response->getContent(), true );
90        $this->templateCache[$siteId][$entityId] =
91            $entityData['entities'][$entityId]['sitelinks'][$siteId]['title'] ?? null;
92
93        return $this->templateCache[$siteId][$entityId];
94    }
95
96    /**
97     * FIXME: copied from WikitextConversions, should use Title methods instead.
98     */
99    private function removeNamespace( string $title ): string {
100        $splitTitle = explode( ':', $title, 2 );
101        return end( $splitTitle );
102    }
103
104}