Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
42.42% covered (danger)
42.42%
28 / 66
58.33% covered (warning)
58.33%
7 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
MediaInfoHandler
42.42% covered (danger)
42.42%
28 / 66
58.33% covered (warning)
58.33%
7 / 12
123.97
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
 makeEmptyEntity
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newEntityContent
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 makeEntityId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getEntityType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 showMissingEntity
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 canCreateWithCustomId
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 allowAutomaticIds
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getContentDataForSearchIndex
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
 getTitleForId
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getTitlesForIds
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
20
 getIdForTitle
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace Wikibase\MediaInfo\Content;
4
5use Content;
6use IContextSource;
7use MediaWiki\Page\PageRecord;
8use MediaWiki\Page\PageStore;
9use MediaWiki\Title\Title;
10use MediaWiki\Title\TitleFactory;
11use Wikibase\DataModel\Entity\EntityId;
12use Wikibase\DataModel\Entity\EntityIdParser;
13use Wikibase\Lib\Store\EntityContentDataCodec;
14use Wikibase\Lib\Store\EntityIdLookup;
15use Wikibase\Lib\Store\NullEntityTermStoreWriter;
16use Wikibase\MediaInfo\DataModel\MediaInfo;
17use Wikibase\MediaInfo\DataModel\MediaInfoId;
18use Wikibase\MediaInfo\Services\FilePageLookup;
19use Wikibase\MediaInfo\Services\MediaInfoIdLookup;
20use Wikibase\Repo\Content\EntityHandler;
21use Wikibase\Repo\Content\EntityHolder;
22use Wikibase\Repo\Search\Fields\FieldDefinitions;
23use Wikibase\Repo\Validators\EntityConstraintProvider;
24use Wikibase\Repo\Validators\ValidatorErrorLocalizer;
25use Wikibase\Search\Elastic\Fields\DescriptionsField;
26use Wikibase\Search\Elastic\Fields\LabelCountField;
27use Wikibase\Search\Elastic\Fields\LabelsField;
28use Wikimedia\Assert\Assert;
29
30/**
31 * @license GPL-2.0-or-later
32 * @author Bene* < benestar.wikimedia@gmail.com >
33 */
34class MediaInfoHandler extends EntityHandler {
35
36    /**
37     * @var MissingMediaInfoHandler
38     */
39    private $missingMediaInfoHandler;
40
41    /**
42     * @var EntityIdLookup
43     */
44    private $idLookup;
45
46    /**
47     * @var FilePageLookup
48     */
49    private $filePageLookup;
50
51    /**
52     * @var array<string,Title|null>
53     */
54    private $titleForIdCache = [];
55
56    /**
57     * @var PageStore
58     */
59    private $pageStore;
60
61    /**
62     * @var TitleFactory
63     */
64    private $titleFactory;
65
66    /**
67     * @param EntityContentDataCodec $contentCodec
68     * @param EntityConstraintProvider $constraintProvider
69     * @param ValidatorErrorLocalizer $errorLocalizer
70     * @param EntityIdParser $entityIdParser
71     * @param MissingMediaInfoHandler $missingMediaInfoHandler
72     * @param MediaInfoIdLookup $idLookup
73     * @param FilePageLookup $filePageLookup
74     * @param FieldDefinitions $mediaInfoFieldDefinitions
75     * @param PageStore $pageStore
76     * @param TitleFactory $titleFactory
77     * @param callable|null $legacyExportFormatDetector
78     */
79    public function __construct(
80        EntityContentDataCodec $contentCodec,
81        EntityConstraintProvider $constraintProvider,
82        ValidatorErrorLocalizer $errorLocalizer,
83        EntityIdParser $entityIdParser,
84        MissingMediaInfoHandler $missingMediaInfoHandler,
85        MediaInfoIdLookup $idLookup,
86        FilePageLookup $filePageLookup,
87        FieldDefinitions $mediaInfoFieldDefinitions,
88        PageStore $pageStore,
89        TitleFactory $titleFactory,
90        $legacyExportFormatDetector = null
91    ) {
92        parent::__construct(
93            MediaInfoContent::CONTENT_MODEL_ID,
94            new NullEntityTermStoreWriter(),
95            $contentCodec,
96            $constraintProvider,
97            $errorLocalizer,
98            $entityIdParser,
99            $mediaInfoFieldDefinitions,
100            $legacyExportFormatDetector
101        );
102        $this->missingMediaInfoHandler = $missingMediaInfoHandler;
103        $this->idLookup = $idLookup;
104        $this->filePageLookup = $filePageLookup;
105        $this->pageStore = $pageStore;
106        $this->titleFactory = $titleFactory;
107    }
108
109    /**
110     * @return MediaInfo
111     */
112    public function makeEmptyEntity() {
113        return new MediaInfo();
114    }
115
116    /**
117     * @see EntityHandler::newEntityContent
118     *
119     * @param EntityHolder|null $entityHolder
120     *
121     * @return MediaInfoContent
122     */
123    public function newEntityContent( EntityHolder $entityHolder = null ) {
124        return new MediaInfoContent( $entityHolder );
125    }
126
127    /**
128     * @param string $id
129     *
130     * @return MediaInfoId
131     */
132    public function makeEntityId( $id ) {
133        return new MediaInfoId( $id );
134    }
135
136    /**
137     * @return string
138     */
139    public function getEntityType() {
140        return MediaInfo::ENTITY_TYPE;
141    }
142
143    /**
144     * @see EntityHandler::showMissingEntity
145     *
146     * This is overwritten to show a dummy MediaInfo entity when appropriate.
147     *
148     * @see MissingMediaInfoHandler::showMissingMediaInfo
149     *
150     * @param Title $title
151     * @param IContextSource $context
152     */
153    public function showMissingEntity( Title $title, IContextSource $context ) {
154        $id = $this->missingMediaInfoHandler->getMediaInfoId( $title, $context );
155
156        if ( $id === null ) {
157            // No virtual MediaInfo for this title, fall back to the default behavior
158            // of displaying an error message.
159            parent::showMissingEntity( $title, $context );
160        } else {
161            // Show a virtual MediaInfo
162            $this->missingMediaInfoHandler->showVirtualMediaInfo( $id, $context );
163        }
164    }
165
166    /**
167     * @param EntityId $id
168     * @return bool
169     */
170    public function canCreateWithCustomId( EntityId $id ) {
171        return ( $id instanceof MediaInfoId )
172            && ( $this->filePageLookup->getFilePage( $id ) !== null );
173    }
174
175    /**
176     * @return bool
177     */
178    public function allowAutomaticIds() {
179        return false;
180    }
181
182    /**
183     * @param Content $content
184     * @return array
185     */
186    public function getContentDataForSearchIndex( Content $content ): array {
187        $fieldsData = parent::getContentDataForSearchIndex( $content );
188        if ( $content->isRedirect() || !( $content instanceof MediaInfoContent ) ) {
189            return $fieldsData;
190        }
191        $entity = $content->getEntity();
192        $fields = $this->fieldDefinitions->getFields();
193
194        foreach ( $fields as $fieldName => $field ) {
195            $fieldsData[$fieldName] = $field->getFieldData( $entity );
196        }
197
198        // Labels data is normally indexed for prefix matching.
199        // We don't need that for MediaInfo files, so swap labels data into descriptions
200        // instead so as not to overburden the search index
201        if ( isset( $fieldsData[ LabelsField::NAME ] ) ) {
202            $fieldsData[DescriptionsField::NAME] = $fieldsData[LabelsField::NAME];
203            $fieldsData[LabelsField::NAME] = null;
204        } else {
205            $fieldsData[DescriptionsField::NAME] = null;
206        }
207
208        $fieldsData[LabelCountField::NAME] = 0;
209        return $fieldsData;
210    }
211
212    /**
213     * Returns the Title of the page in which this MediaInfoId is a slot
214     *
215     * @param EntityId $id
216     * @return Title|null
217     */
218    public function getTitleForId( EntityId $id ) {
219        '@phan-var MediaInfoId $id';
220        $idString = $id->getSerialization();
221        if ( !isset( $this->titleForIdCache[$idString] ) ) {
222            $this->titleForIdCache[$idString] = $this->titleFactory->newFromID( $id->getNumericId() );
223        }
224        return $this->titleForIdCache[$idString];
225    }
226
227    /**
228     * Returns an array of Titles of page in which these MediaInfoIds are slots, indexed
229     * by the MediaInfoId serialization
230     *
231     * @param EntityId[] $ids
232     * @return Title[]
233     */
234    public function getTitlesForIds( array $ids ) {
235        '@phan-var MediaInfoId[] $ids';
236        Assert::parameterElementType( 'Wikibase\MediaInfo\DataModel\MediaInfoId', $ids, '$ids' );
237
238        $titles = [];
239        $uncachedNumericIds = [];
240        // get whatever cached ids we can first
241        /** @var MediaInfoId $id */
242        foreach ( $ids as $id ) {
243            $idString = $id->getSerialization();
244            if ( isset( $this->titleForIdCache[$idString] ) ) {
245                $titles[$idString] = $this->titleForIdCache[$idString];
246            } else {
247                $uncachedNumericIds[] = $id->getNumericId();
248            }
249        }
250
251        $unindexedTitles = $this->pageStore
252            ->newSelectQueryBuilder()
253            ->wherePageIds( $uncachedNumericIds )
254            ->caller( __METHOD__ )
255            ->fetchPageRecords();
256
257        /** @var PageRecord $pageIdentity */
258        foreach ( $unindexedTitles as $pageIdentity ) {
259            $title = $this->titleFactory->castFromPageIdentity( $pageIdentity );
260
261            // @phan-suppress-next-line PhanTypeMismatchArgumentNullable
262            $idString = $this->getIdForTitle( $title )->getSerialization();
263            $this->titleForIdCache[$idString] = $title;
264            $titles[$idString] = $title;
265        }
266        return $titles;
267    }
268
269    /**
270     * @param Title $target
271     * @return EntityId
272     */
273    public function getIdForTitle( Title $target ) {
274        $mediaInfoId = $this->idLookup->getEntityIdForTitle( $target );
275        if ( $mediaInfoId instanceof MediaInfoId ) {
276            return $mediaInfoId;
277        }
278
279        return parent::getIdForTitle( $target );
280    }
281
282}