InfoActionHookHandler::factory()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 9.52
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Wikibase\Client\Hooks;
4
5
use Html;
6
use IContextSource;
7
use MediaWiki\Hook\InfoActionHook;
8
use Title;
9
use Wikibase\Client\NamespaceChecker;
10
use Wikibase\Client\RepoLinker;
11
use Wikibase\Client\Store\DescriptionLookup;
12
use Wikibase\Client\Usage\UsageLookup;
13
use Wikibase\Client\WikibaseClient;
14
use Wikibase\DataModel\Entity\EntityIdParser;
15
use Wikibase\DataModel\Entity\ItemId;
16
use Wikibase\Lib\Store\LanguageFallbackLabelDescriptionLookupFactory;
17
use Wikibase\Lib\Store\SiteLinkLookup;
18
19
/**
20
 * @license GPL-2.0-or-later
21
 * @author Katie Filbert < [email protected] >
22
 */
23
class InfoActionHookHandler implements InfoActionHook {
24
25
	/**
26
	 * @var NamespaceChecker
27
	 */
28
	private $namespaceChecker;
29
30
	/**
31
	 * @var RepoLinker
32
	 */
33
	private $repoLinker;
34
35
	/**
36
	 * @var SiteLinkLookup
37
	 */
38
	private $siteLinkLookup;
39
40
	/**
41
	 * @var string
42
	 */
43
	private $siteId;
44
45
	/**
46
	 * @var UsageLookup
47
	 */
48
	private $usageLookup;
49
50
	/**
51
	 * @var LanguageFallbackLabelDescriptionLookupFactory
52
	 */
53
	private $labelDescriptionLookupFactory;
54
55
	/**
56
	 * @var EntityIdParser
57
	 */
58
	private $idParser;
59
60
	/**
61
	 * @var DescriptionLookup
62
	 */
63
	private $descriptionLookup;
64
65
	public function __construct(
66
		NamespaceChecker $namespaceChecker,
67
		RepoLinker $repoLinker,
68
		SiteLinkLookup $siteLinkLookup,
69
		$siteId,
70
		UsageLookup $usageLookup,
71
		LanguageFallbackLabelDescriptionLookupFactory $labelDescriptionLookupFactory,
72
		EntityIdParser $idParser,
73
		DescriptionLookup $descriptionLookup
74
	) {
75
		$this->namespaceChecker = $namespaceChecker;
76
		$this->repoLinker = $repoLinker;
77
		$this->siteLinkLookup = $siteLinkLookup;
78
		$this->siteId = $siteId;
79
		$this->usageLookup = $usageLookup;
80
		$this->labelDescriptionLookupFactory = $labelDescriptionLookupFactory;
81
		$this->idParser = $idParser;
82
		$this->descriptionLookup = $descriptionLookup;
83
	}
84
85
	public static function factory(): self {
86
		$wikibaseClient = WikibaseClient::getDefaultInstance();
87
		$settings = $wikibaseClient->getSettings();
88
89
		$namespaceChecker = $wikibaseClient->getNamespaceChecker();
90
		$usageLookup = $wikibaseClient->getStore()->getUsageLookup();
91
		$labelDescriptionLookupFactory = new LanguageFallbackLabelDescriptionLookupFactory(
92
			$wikibaseClient->getLanguageFallbackChainFactory(),
93
			$wikibaseClient->getTermLookup(),
94
			$wikibaseClient->getTermBuffer()
95
		);
96
		$idParser = $wikibaseClient->getEntityIdParser();
97
		$descriptionLookup = $wikibaseClient->getDescriptionLookup();
98
99
		return new self(
100
			$namespaceChecker,
101
			$wikibaseClient->newRepoLinker(),
102
			$wikibaseClient->getStore()->getSiteLinkLookup(),
103
			$settings->getSetting( 'siteGlobalID' ),
104
			$usageLookup,
105
			$labelDescriptionLookupFactory,
106
			$idParser,
107
			$descriptionLookup
108
		);
109
	}
110
111
	/**
112
	 * Adds the Entity ID of the corresponding Wikidata item in action=info
113
	 *
114
	 * @param IContextSource $context
115
	 * @param array[] &$pageInfo
116
	 */
117
	public function onInfoAction( $context, &$pageInfo ) {
118
		// Check if wikibase namespace is enabled
119
		$title = $context->getTitle();
120
		$usage = $this->usageLookup->getUsagesForPage( $title->getArticleID() );
121
		$localDescription = $this->descriptionLookup->getDescription( $title,
122
			DescriptionLookup::SOURCE_LOCAL );
123
		$centralDescription = $this->descriptionLookup->getDescription( $title,
124
			DescriptionLookup::SOURCE_CENTRAL );
125
126
		if ( $this->namespaceChecker->isWikibaseEnabled( $title->getNamespace() ) && $title->exists() ) {
127
			$pageInfo['header-basic'][] = $this->getPageInfoRow( $context, $title );
128
		}
129
		if ( $localDescription ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $localDescription of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
130
			$pageInfo['header-basic'][] = $this->getDescriptionInfoRow( $context, $localDescription,
131
				DescriptionLookup::SOURCE_LOCAL );
132
		}
133
		if ( $centralDescription ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $centralDescription of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
134
			$pageInfo['header-basic'][] = $this->getDescriptionInfoRow( $context, $centralDescription,
135
				DescriptionLookup::SOURCE_CENTRAL );
136
		}
137
138
		if ( $usage ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $usage of type Wikibase\Client\Usage\EntityUsage[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
139
			$pageInfo['header-properties'][] = $this->formatEntityUsage( $context, $usage );
140
		}
141
	}
142
143
	/**
144
	 * @param IContextSource $context
145
	 * @param Title $title
146
	 *
147
	 * @return string[]
148
	 */
149
	private function getPageInfoRow( IContextSource $context, Title $title ) {
150
		$entityId = $this->siteLinkLookup->getItemIdForLink(
151
			$this->siteId,
152
			$title->getPrefixedText()
153
		);
154
155
		$row = $entityId ? $this->getItemPageInfo( $context, $entityId )
156
			: $this->getUnconnectedItemPageInfo( $context );
157
158
		return $row;
159
	}
160
161
	/**
162
	 * @param IContextSource $context
163
	 * @param string $description
164
	 * @param string $source
165
	 *
166
	 * @return string[]
167
	 */
168
	private function getDescriptionInfoRow( $context, $description, $source ) {
169
		return [
170
			// messages: wikibase-pageinfo-description-local, wikibase-pageinfo-description-central
171
			$context->msg( 'wikibase-pageinfo-description-' . $source )->parse(),
172
			$description
173
		];
174
	}
175
176
	/**
177
	 * Creating a Repo link with Item ID as anchor text
178
	 *
179
	 * @param IContextSource $context
180
	 * @param ItemId $itemId
181
	 *
182
	 * @return string[]
183
	 */
184
	private function getItemPageInfo( IContextSource $context, ItemId $itemId ) {
185
		$itemLink = $this->repoLinker->buildEntityLink(
186
			$itemId,
187
			[ 'external' ]
188
		);
189
190
		return [
191
			$context->msg( 'wikibase-pageinfo-entity-id' )->parse(),
192
			$itemLink
193
		];
194
	}
195
196
	/**
197
	 * @param IContextSource $context
198
	 *
199
	 * @return string[]
200
	 */
201
	private function getUnconnectedItemPageInfo( IContextSource $context ) {
202
		return [
203
			$context->msg( 'wikibase-pageinfo-entity-id' )->parse(),
204
			$context->msg( 'wikibase-pageinfo-entity-id-none' )->parse()
205
		];
206
	}
207
208
	/**
209
	 * @param string[][] $aspects
210
	 * @param IContextSource $context
211
	 * @return string
212
	 */
213
	private function formatAspects( array $aspects, IContextSource $context ) {
214
		$aspectContent = '';
215
		foreach ( $aspects as $aspect ) {
216
			// Possible messages:
217
			//   wikibase-pageinfo-entity-usage-L
218
			//   wikibase-pageinfo-entity-usage-L-with-modifier
219
			//   wikibase-pageinfo-entity-usage-D
220
			//   wikibase-pageinfo-entity-usage-D-with-modifier
221
			//   wikibase-pageinfo-entity-usage-C
222
			//   wikibase-pageinfo-entity-usage-C-with-modifier
223
			//   wikibase-pageinfo-entity-usage-S
224
			//   wikibase-pageinfo-entity-usage-T
225
			//   wikibase-pageinfo-entity-usage-X
226
			//   wikibase-pageinfo-entity-usage-O
227
			$msgKey = 'wikibase-pageinfo-entity-usage-' . $aspect[0];
228
			if ( $aspect[1] !== null ) {
229
				$msgKey .= '-with-modifier';
230
			}
231
			$aspectContent .= Html::rawElement(
232
				'li',
233
				[],
234
				$context->msg( $msgKey, $aspect[1] )->parse()
235
			);
236
		}
237
		return $aspectContent;
238
	}
239
240
	/**
241
	 * @param IContextSource $context
242
	 * @param array $usage
243
	 *
244
	 * @return string[]
245
	 */
246
	private function formatEntityUsage( IContextSource $context, array $usage ) {
247
		$usageAspectsByEntity = [];
248
		$entities = [];
249
		foreach ( $usage as $entityUsage ) {
250
			$entityId = $entityUsage->getEntityId()->getSerialization();
251
			$entities[$entityId] = $entityUsage->getEntityId();
252
			$usageAspectsByEntity[$entityId][] = [
253
				$entityUsage->getAspect(),
254
				$entityUsage->getModifier()
255
			];
256
		}
257
		$output = '';
258
		$entityIds = array_map(
259
			function( $entityId ) {
260
				return $this->idParser->parse( $entityId );
261
			},
262
			array_keys( $usageAspectsByEntity )
263
		);
264
		$labelLookup = $this->labelDescriptionLookupFactory->newLabelDescriptionLookup(
265
			$context->getLanguage(),
266
			$entityIds
267
		);
268
		foreach ( $usageAspectsByEntity as $entityId => $aspects ) {
269
			$label = $labelLookup->getLabel( $this->idParser->parse( $entityId ) );
270
			$text = $label === null ? $entityId : $label->getText();
271
272
			$output .= Html::rawElement( 'li', [],
273
				$this->repoLinker->buildEntityLink(
274
					$entities[$entityId],
275
					[ 'external' ],
276
					$text
277
				)
278
			);
279
280
			$aspectContent = $this->formatAspects( $aspects, $context );
281
			$output .= Html::rawElement( 'ul', [], $aspectContent );
282
		}
283
		$output = Html::rawElement( 'ul', [], $output );
284
		return [ $context->msg( 'wikibase-pageinfo-entity-usage' )->parse(), $output ];
285
	}
286
287
}
288