Completed
Push — master ( db6b00...5246ee )
by
unknown
08:09 queued 11s
created

OutputPageBeforeHTMLHookHandler   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 290
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 24

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 24
dl 0
loc 290
rs 10
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 25 1
A factory() 0 28 1
A onOutputPageBeforeHTML() 0 10 2
A addTermboxModules() 0 6 2
A replacePlaceholders() 0 18 3
A addJsUserLanguages() 0 12 1
A getEntity() 0 20 4
A needsRealEntity() 0 3 2
A getPlaceholderExpander() 0 11 2
A getLocallyRenderedEntityViewPlaceholderExpander() 0 19 2
A getExternallyRenderedEntityViewPlaceholderExpander() 0 21 1
A getEntityTermsListHtml() 0 3 1
A showOrHideEditLinks() 0 6 1
A getEntityId() 0 3 1
1
<?php
2
3
namespace Wikibase\Repo\Hooks;
4
5
use MediaWiki\Hook\OutputPageBeforeHTMLHook;
6
use MediaWiki\MediaWikiServices;
7
use OutputPage;
8
use Wikibase\DataModel\Entity\EntityDocument;
9
use Wikibase\DataModel\Entity\EntityId;
10
use Wikibase\Lib\ContentLanguages;
11
use Wikibase\Lib\EntityFactory;
12
use Wikibase\Lib\LanguageNameLookup;
13
use Wikibase\Lib\Store\EntityRevision;
14
use Wikibase\Lib\Store\EntityRevisionLookup;
15
use Wikibase\Lib\UserLanguageLookup;
16
use Wikibase\Repo\BabelUserLanguageLookup;
17
use Wikibase\Repo\Hooks\Helpers\OutputPageEditability;
18
use Wikibase\Repo\Hooks\Helpers\OutputPageRevisionIdReader;
19
use Wikibase\Repo\Hooks\Helpers\UserPreferredContentLanguagesLookup;
20
use Wikibase\Repo\MediaWikiLanguageDirectionalityLookup;
21
use Wikibase\Repo\MediaWikiLocalizedTextProvider;
22
use Wikibase\Repo\ParserOutput\PlaceholderExpander\EntityViewPlaceholderExpander;
23
use Wikibase\Repo\ParserOutput\PlaceholderExpander\ExternallyRenderedEntityViewPlaceholderExpander;
24
use Wikibase\Repo\ParserOutput\PlaceholderExpander\PlaceholderExpander;
25
use Wikibase\Repo\ParserOutput\PlaceholderExpander\TermboxRequestInspector;
26
use Wikibase\Repo\ParserOutput\TermboxFlag;
27
use Wikibase\Repo\ParserOutput\TextInjector;
28
use Wikibase\Repo\View\RepoSpecialPageLinker;
29
use Wikibase\Repo\WikibaseRepo;
30
use Wikibase\View\Template\TemplateFactory;
31
use Wikibase\View\Termbox\Renderer\TermboxRemoteRenderer;
32
use Wikibase\View\ToolbarEditSectionGenerator;
33
34
/**
35
 * Handler for the "OutputPageBeforeHTML" hook.
36
 *
37
 * @license GPL-2.0-or-later
38
 * @author Marius Hoch < [email protected] >
39
 */
40
class OutputPageBeforeHTMLHookHandler implements OutputPageBeforeHTMLHook {
41
42
	/**
43
	 * @var TemplateFactory
44
	 */
45
	private $templateFactory;
46
47
	/**
48
	 * @var UserLanguageLookup
49
	 */
50
	private $userLanguageLookup;
51
52
	/**
53
	 * @var ContentLanguages
54
	 */
55
	private $termsLanguages;
56
57
	/**
58
	 * @var EntityRevisionLookup
59
	 */
60
	private $entityRevisionLookup;
61
62
	/**
63
	 * @var LanguageNameLookup
64
	 */
65
	private $languageNameLookup;
66
67
	/**
68
	 * @var OutputPageEntityIdReader
69
	 */
70
	private $outputPageEntityIdReader;
71
72
	/**
73
	 * @var EntityFactory
74
	 */
75
	private $entityFactory;
76
77
	/**
78
	 * @var string
79
	 */
80
	private $cookiePrefix;
81
82
	/**
83
	 * @var OutputPageEditability
84
	 */
85
	private $editability;
86
87
	/**
88
	 * @var bool
89
	 */
90
	private $isExternallyRendered;
91
92
	/**
93
	 * @var UserPreferredContentLanguagesLookup
94
	 */
95
	private $userPreferredTermsLanguages;
96
97
	public function __construct(
98
		TemplateFactory $templateFactory,
99
		UserLanguageLookup $userLanguageLookup,
100
		ContentLanguages $termsLanguages,
101
		EntityRevisionLookup $entityRevisionLookup,
102
		LanguageNameLookup $languageNameLookup,
103
		OutputPageEntityIdReader $outputPageEntityIdReader,
104
		EntityFactory $entityFactory,
105
		$cookiePrefix,
106
		OutputPageEditability $editability,
107
		$isExternallyRendered,
108
		UserPreferredContentLanguagesLookup $userPreferredTermsLanguages
109
	) {
110
		$this->templateFactory = $templateFactory;
111
		$this->userLanguageLookup = $userLanguageLookup;
112
		$this->termsLanguages = $termsLanguages;
113
		$this->entityRevisionLookup = $entityRevisionLookup;
114
		$this->languageNameLookup = $languageNameLookup;
115
		$this->outputPageEntityIdReader = $outputPageEntityIdReader;
116
		$this->entityFactory = $entityFactory;
117
		$this->cookiePrefix = $cookiePrefix;
118
		$this->isExternallyRendered = $isExternallyRendered;
119
		$this->editability = $editability;
120
		$this->userPreferredTermsLanguages = $userPreferredTermsLanguages;
121
	}
122
123
	/**
124
	 * @return self
125
	 */
126
	public static function factory(): self {
127
		global $wgLang, $wgCookiePrefix;
128
129
		$wikibaseRepo = WikibaseRepo::getDefaultInstance();
130
		$termLanguages = $wikibaseRepo->getTermsLanguages();
131
		$babelUserLanguageLookup = new BabelUserLanguageLookup();
132
133
		return new self(
134
			TemplateFactory::getDefaultInstance(),
135
			$babelUserLanguageLookup,
136
			$termLanguages,
137
			$wikibaseRepo->getEntityRevisionLookup(),
138
			new LanguageNameLookup( $wgLang->getCode() ),
139
			new OutputPageEntityIdReader(
140
				$wikibaseRepo->getEntityContentFactory(),
141
				$wikibaseRepo->getEntityIdParser()
142
			),
143
			$wikibaseRepo->getEntityFactory(),
144
			$wgCookiePrefix,
145
			new OutputPageEditability(),
146
			TermboxFlag::getInstance()->shouldRenderTermbox(),
147
			new UserPreferredContentLanguagesLookup(
148
				$termLanguages,
149
				$babelUserLanguageLookup,
150
				MediaWikiServices::getInstance()->getContentLanguage()->getCode()
151
			)
152
		);
153
	}
154
155
	/**
156
	 * Called when pushing HTML from the ParserOutput into OutputPage.
157
	 * Used to expand any placeholders in the OutputPage's 'wb-placeholders' property
158
	 * in the HTML.
159
	 *
160
	 * @param OutputPage $out
161
	 * @param string &$html the HTML to mangle
162
	 */
163
	public function onOutputPageBeforeHTML( $out, &$html ): void {
164
		if ( !$out->isArticle() ) {
165
			return;
166
		}
167
168
		$html = $this->replacePlaceholders( $out, $html );
169
		$this->addJsUserLanguages( $out );
170
		$html = $this->showOrHideEditLinks( $out, $html );
171
		$this->addTermboxModules( $out );
172
	}
173
174
	private function addTermboxModules( OutputPage $out ) {
175
		if ( $this->isExternallyRendered ) {
176
			$out->addModules( 'wikibase.termbox' );
177
			$out->addModuleStyles( [ 'wikibase.termbox.styles' ] );
178
		}
179
	}
180
181
	/**
182
	 * @param OutputPage $out
183
	 * @param string $html
184
	 *
185
	 * @return string
186
	 */
187
	private function replacePlaceholders( OutputPage $out, $html ) {
188
		$placeholders = $out->getProperty( 'wikibase-view-chunks' );
189
		if ( !$placeholders ) {
190
			return $html;
191
		}
192
193
		$injector = new TextInjector( $placeholders );
194
		$getHtmlCallback = function() {
195
			return '';
196
		};
197
198
		$entity = $this->getEntity( $out );
199
		if ( $entity instanceof EntityDocument ) {
200
			$getHtmlCallback = [ $this->getPlaceholderExpander( $entity, $out ), 'getHtmlForPlaceholder' ];
201
		}
202
203
		return $injector->inject( $html, $getHtmlCallback );
204
	}
205
206
	private function addJsUserLanguages( OutputPage $out ) {
207
		$out->addJsConfigVars(
208
			'wbUserSpecifiedLanguages',
209
			// All user-specified languages, that are valid term languages
210
			// Reindex the keys so that JavaScript still works if an unknown
211
			// language code in the babel box causes an index to miss
212
			array_values( array_intersect(
213
				$this->userLanguageLookup->getUserSpecifiedLanguages( $out->getUser() ),
214
				$this->termsLanguages->getLanguages()
215
			) )
216
		);
217
	}
218
219
	/**
220
	 * @param OutputPage $out
221
	 *
222
	 * @return EntityDocument|null
223
	 */
224
	private function getEntity( OutputPage $out ) {
225
		$entityId = $this->getEntityId( $out );
226
227
		if ( !$entityId ) {
228
			return null;
229
		}
230
231
		if ( $this->needsRealEntity( $out ) ) {
232
			// The parser cache content is too old to contain the terms list items
233
			// Pass the correct entity to generate terms list items on the fly
234
			$entityRev = $this->entityRevisionLookup->getEntityRevision( $entityId, $out->getRevisionId() );
235
			if ( !( $entityRev instanceof EntityRevision ) ) {
236
				return null;
237
			}
238
239
			return $entityRev->getEntity();
240
		}
241
242
		return $this->entityFactory->newEmpty( $entityId->getEntityType() );
243
	}
244
245
	private function needsRealEntity( OutputPage $out ) {
246
		return !$this->isExternallyRendered && !$this->getEntityTermsListHtml( $out );
247
	}
248
249
	private function getPlaceholderExpander(
250
		EntityDocument $entity,
251
		OutputPage $out
252
	): PlaceholderExpander {
253
		return $this->isExternallyRendered
254
			? $this->getExternallyRenderedEntityViewPlaceholderExpander( $out )
255
			: $this->getLocallyRenderedEntityViewPlaceholderExpander(
256
				$entity,
257
				$out
258
			);
259
	}
260
261
	/**
262
	 * @param EntityDocument $entity
263
	 * @param OutputPage $out
264
	 *
265
	 * @return EntityViewPlaceholderExpander
266
	 */
267
	private function getLocallyRenderedEntityViewPlaceholderExpander(
268
		EntityDocument $entity,
269
		OutputPage $out
270
	) {
271
		$language = $out->getLanguage();
272
		$user = $out->getUser();
273
274
		return new EntityViewPlaceholderExpander(
275
			$this->templateFactory,
276
			$user,
277
			$entity,
278
			$this->userPreferredTermsLanguages->getLanguages( $language->getCode(), $user ),
279
			new MediaWikiLanguageDirectionalityLookup(),
280
			$this->languageNameLookup,
281
			new MediaWikiLocalizedTextProvider( $language ),
282
			$this->cookiePrefix,
283
			$this->getEntityTermsListHtml( $out ) ?: []
284
		);
285
	}
286
287
	private function getExternallyRenderedEntityViewPlaceholderExpander( OutputPage $out ) {
288
		$repo = WikibaseRepo::getDefaultInstance();
289
		$languageFallbackChainFactory = $repo->getLanguageFallbackChainFactory();
290
291
		return new ExternallyRenderedEntityViewPlaceholderExpander(
292
			$out,
293
			new TermboxRequestInspector( $languageFallbackChainFactory ),
294
			new TermboxRemoteRenderer(
295
				MediaWikiServices::getInstance()->getHttpRequestFactory(),
296
				$repo->getSettings()->getSetting( 'ssrServerUrl' ),
297
				$repo->getSettings()->getSetting( 'ssrServerTimeout' ),
298
				$repo->getLogger(),
299
				MediaWikiServices::getInstance()->getStatsdDataFactory()
300
			),
301
			$this->outputPageEntityIdReader,
302
			new RepoSpecialPageLinker(),
303
			$languageFallbackChainFactory,
304
			new OutputPageRevisionIdReader(),
305
			$repo->getSettings()->getSetting( 'termboxUserSpecificSsrEnabled' )
306
		);
307
	}
308
309
	private function getEntityTermsListHtml( OutputPage $out ) {
310
		return $out->getProperty( 'wikibase-terms-list-items' );
311
	}
312
313
	private function showOrHideEditLinks( OutputPage $out, $html ) {
314
		return ToolbarEditSectionGenerator::enableSectionEditLinks(
315
			$html,
316
			$this->editability->validate( $out )
317
		);
318
	}
319
320
	/**
321
	 * @param OutputPage $out
322
	 *
323
	 * @return EntityId|null
324
	 */
325
	private function getEntityId( OutputPage $out ) {
326
		return $this->outputPageEntityIdReader->getEntityIdFromOutputPage( $out );
327
	}
328
329
}
330