This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Wikibase\Repo; |
||
4 | |||
5 | use ApiBase; |
||
6 | use ApiEditPage; |
||
7 | use ApiModuleManager; |
||
8 | use ApiQuery; |
||
9 | use ApiQuerySiteinfo; |
||
10 | use CentralIdLookup; |
||
11 | use Content; |
||
12 | use ContentHandler; |
||
13 | use ExtensionRegistry; |
||
14 | use HistoryPager; |
||
15 | use IContextSource; |
||
16 | use LogEntry; |
||
17 | use MediaWiki\Linker\LinkTarget; |
||
18 | use MediaWiki\MediaWikiServices; |
||
19 | use MediaWiki\Revision\RevisionRecord; |
||
20 | use MediaWiki\Revision\SlotRecord; |
||
21 | use MediaWiki\User\UserIdentity; |
||
22 | use MWException; |
||
23 | use OutputPage; |
||
24 | use PageProps; |
||
25 | use Parser; |
||
26 | use ParserOptions; |
||
27 | use ParserOutput; |
||
28 | use RecentChange; |
||
29 | use ResourceLoader; |
||
30 | use Skin; |
||
31 | use SkinTemplate; |
||
32 | use StubUserLang; |
||
33 | use Title; |
||
34 | use User; |
||
35 | use Wikibase\Lib\Formatters\AutoCommentFormatter; |
||
36 | use Wikibase\Lib\LibHooks; |
||
37 | use Wikibase\Lib\ParserFunctions\CommaSeparatedList; |
||
38 | use Wikibase\Lib\Store\EntityRevision; |
||
39 | use Wikibase\Lib\Store\Sql\EntityChangeLookup; |
||
40 | use Wikibase\Repo\Api\MetaDataBridgeConfig; |
||
41 | use Wikibase\Repo\Content\EntityContent; |
||
42 | use Wikibase\Repo\Content\EntityHandler; |
||
43 | use Wikibase\Repo\Hooks\Helpers\OutputPageEntityViewChecker; |
||
44 | use Wikibase\Repo\Hooks\InfoActionHookHandler; |
||
45 | use Wikibase\Repo\Hooks\OutputPageEntityIdReader; |
||
46 | use Wikibase\Repo\Hooks\SidebarBeforeOutputHookHandler; |
||
47 | use Wikibase\Repo\Notifications\RepoEntityChange; |
||
48 | use Wikibase\Repo\ParserOutput\PlaceholderEmittingEntityTermsView; |
||
49 | use Wikibase\Repo\ParserOutput\TermboxFlag; |
||
50 | use Wikibase\Repo\ParserOutput\TermboxVersionParserCacheValueRejector; |
||
51 | use Wikibase\Repo\ParserOutput\TermboxView; |
||
52 | use Wikibase\Repo\Store\Sql\DispatchStats; |
||
53 | use Wikibase\Repo\Store\Sql\SqlSubscriptionLookup; |
||
54 | use Wikibase\View\ViewHooks; |
||
55 | use WikiPage; |
||
56 | |||
57 | /** |
||
58 | * File defining the hook handlers for the Wikibase extension. |
||
59 | * |
||
60 | * @license GPL-2.0-or-later |
||
61 | */ |
||
62 | final class RepoHooks { |
||
63 | |||
64 | /** |
||
65 | * Handler for the BeforePageDisplay hook, simply injects wikibase.ui.entitysearch module |
||
66 | * replacing the native search box with the entity selector widget. |
||
67 | * |
||
68 | * @param OutputPage $out |
||
69 | * @param Skin $skin |
||
70 | */ |
||
71 | public static function onBeforePageDisplay( OutputPage $out, Skin $skin ) { |
||
0 ignored issues
–
show
|
|||
72 | $settings = WikibaseRepo::getDefaultInstance()->getSettings(); |
||
73 | if ( $settings->getSetting( 'enableEntitySearchUI' ) === true ) { |
||
74 | $out->addModules( 'wikibase.ui.entitysearch' ); |
||
75 | } |
||
76 | } |
||
77 | |||
78 | /** |
||
79 | * Handler for the BeforePageDisplayMobile hook that adds the wikibase mobile styles. |
||
80 | * |
||
81 | * @param OutputPage $out |
||
82 | * @param Skin $skin |
||
83 | */ |
||
84 | public static function onBeforePageDisplayMobile( OutputPage $out, Skin $skin ) { |
||
0 ignored issues
–
show
|
|||
85 | $title = $out->getTitle(); |
||
86 | $repo = WikibaseRepo::getDefaultInstance(); |
||
87 | $entityNamespaceLookup = $repo->getEntityNamespaceLookup(); |
||
88 | $isEntityTitle = $entityNamespaceLookup->isNamespaceWithEntities( $title->getNamespace() ); |
||
89 | $useNewTermbox = $repo->getSettings()->getSetting( 'termboxEnabled' ); |
||
90 | |||
91 | if ( $isEntityTitle ) { |
||
92 | $out->addModules( 'wikibase.mobile' ); |
||
93 | |||
94 | if ( $useNewTermbox ) { |
||
95 | $out->addModules( 'wikibase.termbox' ); |
||
96 | $out->addModuleStyles( [ 'wikibase.termbox.styles' ] ); |
||
97 | } |
||
98 | } |
||
99 | } |
||
100 | |||
101 | /** |
||
102 | * Handler for the SetupAfterCache hook, completing the content and namespace setup. |
||
103 | * This updates the $wgContentHandlers and $wgNamespaceContentModels registries |
||
104 | * according to information provided by entity type definitions and the entityNamespaces |
||
105 | * setting. |
||
106 | * |
||
107 | * @throws MWException |
||
108 | */ |
||
109 | public static function onSetupAfterCache() { |
||
110 | global $wgContentHandlers, |
||
111 | $wgNamespaceContentModels; |
||
112 | |||
113 | $wikibaseRepo = WikibaseRepo::getDefaultInstance(); |
||
114 | $namespaces = $wikibaseRepo->getLocalEntityNamespaces(); |
||
115 | $namespaceLookup = $wikibaseRepo->getEntityNamespaceLookup(); |
||
116 | |||
117 | // Register entity namespaces. |
||
118 | // Note that $wgExtraNamespaces and $wgNamespaceAliases have already been processed at this |
||
119 | // point and should no longer be touched. |
||
120 | $contentModelIds = $wikibaseRepo->getContentModelMappings(); |
||
121 | |||
122 | foreach ( $namespaces as $entityType => $namespace ) { |
||
123 | // TODO: once there is a mechanism for registering the default content model for |
||
124 | // slots other than the main slot, do that! |
||
125 | // XXX: we should probably not just ignore $entityTypes that don't match $contentModelIds. |
||
126 | if ( !isset( $wgNamespaceContentModels[$namespace] ) |
||
127 | && isset( $contentModelIds[$entityType] ) |
||
128 | && $namespaceLookup->getEntitySlotRole( $namespace ) === 'main' |
||
129 | ) { |
||
130 | $wgNamespaceContentModels[$namespace] = $contentModelIds[$entityType]; |
||
131 | } |
||
132 | } |
||
133 | |||
134 | // Register callbacks for instantiating ContentHandlers for EntityContent. |
||
135 | foreach ( $contentModelIds as $entityType => $model ) { |
||
136 | $wgContentHandlers[$model] = function () use ( $wikibaseRepo, $entityType ) { |
||
137 | $entityContentFactory = $wikibaseRepo->getEntityContentFactory(); |
||
138 | return $entityContentFactory->getContentHandlerForType( $entityType ); |
||
139 | }; |
||
140 | } |
||
141 | |||
142 | return true; |
||
143 | } |
||
144 | |||
145 | /** |
||
146 | * Hook to add PHPUnit test cases. |
||
147 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/UnitTestsList |
||
148 | * |
||
149 | * @param string[] &$paths |
||
150 | */ |
||
151 | public static function registerUnitTests( array &$paths ) { |
||
152 | $paths[] = __DIR__ . '/../tests/phpunit/'; |
||
153 | } |
||
154 | |||
155 | /** |
||
156 | * Handler for the NamespaceIsMovable hook. |
||
157 | * |
||
158 | * Implemented to prevent moving pages that are in an entity namespace. |
||
159 | * |
||
160 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/NamespaceIsMovable |
||
161 | * |
||
162 | * @param int $ns Namespace ID |
||
163 | * @param bool &$movable |
||
164 | */ |
||
165 | public static function onNamespaceIsMovable( $ns, &$movable ) { |
||
166 | if ( self::isNamespaceUsedByLocalEntities( $ns ) ) { |
||
167 | $movable = false; |
||
168 | } |
||
169 | } |
||
170 | |||
171 | private static function isNamespaceUsedByLocalEntities( $namespace ) { |
||
172 | $wikibaseRepo = WikibaseRepo::getDefaultInstance(); |
||
173 | $namespaceLookup = $wikibaseRepo->getEntityNamespaceLookup(); |
||
174 | |||
175 | // TODO: this logic seems badly misplaced, probably WikibaseRepo should be asked and be |
||
176 | // providing different and more appropriate EntityNamespaceLookup instance |
||
177 | // However looking at the current use of EntityNamespaceLookup, it seems to be used |
||
178 | // for different kinds of things, which calls for more systematic audit and changes. |
||
179 | if ( !$namespaceLookup->isEntityNamespace( $namespace ) ) { |
||
180 | return false; |
||
181 | } |
||
182 | |||
183 | $entityType = $namespaceLookup->getEntityType( $namespace ); |
||
184 | |||
185 | if ( $entityType === null ) { |
||
186 | return false; |
||
187 | } |
||
188 | |||
189 | $entitySource = $wikibaseRepo->getEntitySourceDefinitions()->getSourceForEntityType( |
||
190 | $entityType |
||
191 | ); |
||
192 | if ( $entitySource === null ) { |
||
193 | return false; |
||
194 | } |
||
195 | |||
196 | $localEntitySourceName = $wikibaseRepo->getSettings()->getSetting( 'localEntitySourceName' ); |
||
197 | if ( $entitySource->getSourceName() === $localEntitySourceName ) { |
||
198 | return true; |
||
199 | } |
||
200 | |||
201 | return false; |
||
202 | } |
||
203 | |||
204 | /** |
||
205 | * Called when a revision was inserted due to an edit. |
||
206 | * |
||
207 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/RevisionFromEditComplete |
||
208 | * |
||
209 | * @param WikiPage $wikiPage |
||
210 | * @param RevisionRecord $revisionRecord |
||
211 | * @param int $baseID |
||
212 | * @param UserIdentity $user |
||
213 | */ |
||
214 | public static function onRevisionFromEditComplete( |
||
215 | WikiPage $wikiPage, |
||
216 | RevisionRecord $revisionRecord, |
||
217 | $baseID, |
||
0 ignored issues
–
show
|
|||
218 | UserIdentity $user |
||
0 ignored issues
–
show
|
|||
219 | ) { |
||
220 | $entityContentFactory = WikibaseRepo::getDefaultInstance()->getEntityContentFactory(); |
||
221 | |||
222 | if ( $entityContentFactory->isEntityContentModel( $wikiPage->getContent()->getModel() ) ) { |
||
223 | self::notifyEntityStoreWatcherOnUpdate( |
||
224 | $revisionRecord->getContent( SlotRecord::MAIN ), |
||
225 | $revisionRecord |
||
226 | ); |
||
227 | |||
228 | $notifier = WikibaseRepo::getDefaultInstance()->getChangeNotifier(); |
||
229 | $parentId = $revisionRecord->getParentId(); |
||
230 | |||
231 | if ( !$parentId ) { |
||
232 | $notifier->notifyOnPageCreated( $revisionRecord ); |
||
233 | } else { |
||
234 | $parent = MediaWikiServices::getInstance() |
||
235 | ->getRevisionLookup() |
||
236 | ->getRevisionById( $parentId ); |
||
237 | |||
238 | if ( !$parent ) { |
||
239 | wfLogWarning( |
||
240 | __METHOD__ . ': Cannot notify on page modification: ' |
||
241 | . 'failed to load parent revision with ID ' . $parentId |
||
242 | ); |
||
243 | } else { |
||
244 | $notifier->notifyOnPageModified( $revisionRecord, $parent ); |
||
245 | } |
||
246 | } |
||
247 | } |
||
248 | } |
||
249 | |||
250 | private static function notifyEntityStoreWatcherOnUpdate( |
||
251 | EntityContent $content, |
||
252 | RevisionRecord $revision |
||
253 | ) { |
||
254 | $watcher = WikibaseRepo::getDefaultInstance()->getEntityStoreWatcher(); |
||
255 | |||
256 | // Notify storage/lookup services that the entity was updated. Needed to track page-level changes. |
||
257 | // May be redundant in some cases. Take care not to cause infinite regress. |
||
258 | if ( $content->isRedirect() ) { |
||
259 | $watcher->redirectUpdated( |
||
260 | $content->getEntityRedirect(), |
||
0 ignored issues
–
show
It seems like
$content->getEntityRedirect() can be null ; however, redirectUpdated() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
261 | $revision->getId() |
||
262 | ); |
||
263 | } else { |
||
264 | $watcher->entityUpdated( new EntityRevision( |
||
265 | $content->getEntity(), |
||
266 | $revision->getId(), |
||
267 | $revision->getTimestamp() |
||
268 | ) ); |
||
269 | } |
||
270 | } |
||
271 | |||
272 | /** |
||
273 | * Occurs after the delete article request has been processed. |
||
274 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/ArticleDeleteComplete |
||
275 | * |
||
276 | * @param WikiPage $wikiPage |
||
277 | * @param User $user |
||
278 | * @param string $reason |
||
279 | * @param int $id id of the article that was deleted |
||
280 | * @param Content|null $content |
||
281 | * @param LogEntry $logEntry |
||
282 | * |
||
283 | * @throws MWException |
||
284 | */ |
||
285 | public static function onArticleDeleteComplete( |
||
286 | WikiPage $wikiPage, |
||
0 ignored issues
–
show
|
|||
287 | User $user, |
||
288 | $reason, |
||
0 ignored issues
–
show
|
|||
289 | $id, |
||
0 ignored issues
–
show
|
|||
290 | ?Content $content, |
||
291 | LogEntry $logEntry |
||
292 | ) { |
||
293 | $wikibaseRepo = WikibaseRepo::getDefaultInstance(); |
||
294 | $entityContentFactory = $wikibaseRepo->getEntityContentFactory(); |
||
295 | |||
296 | // Bail out if we are not looking at an entity |
||
297 | if ( !$content || !$entityContentFactory->isEntityContentModel( $content->getModel() ) ) { |
||
298 | return; |
||
299 | } |
||
300 | |||
301 | /** @var EntityContent $content */ |
||
302 | '@phan-var EntityContent $content'; |
||
303 | |||
304 | // Notify storage/lookup services that the entity was deleted. Needed to track page-level deletion. |
||
305 | // May be redundant in some cases. Take care not to cause infinite regress. |
||
306 | $wikibaseRepo->getEntityStoreWatcher()->entityDeleted( $content->getEntityId() ); |
||
307 | |||
308 | $notifier = $wikibaseRepo->getChangeNotifier(); |
||
309 | $notifier->notifyOnPageDeleted( $content, $user, $logEntry->getTimestamp() ); |
||
310 | } |
||
311 | |||
312 | /** |
||
313 | * Handle changes for undeletions |
||
314 | * |
||
315 | * @param Title $title |
||
316 | * @param bool $created |
||
317 | * @param string $comment |
||
318 | */ |
||
319 | public static function onArticleUndelete( Title $title, $created, $comment ) { |
||
0 ignored issues
–
show
|
|||
320 | $wikibaseRepo = WikibaseRepo::getDefaultInstance(); |
||
321 | $entityContentFactory = $wikibaseRepo->getEntityContentFactory(); |
||
322 | |||
323 | // Bail out if we are not looking at an entity |
||
324 | if ( !$entityContentFactory->isEntityContentModel( $title->getContentModel() ) ) { |
||
325 | return; |
||
326 | } |
||
327 | |||
328 | $revisionId = $title->getLatestRevID(); |
||
329 | $revisionRecord = MediaWikiServices::getInstance() |
||
330 | ->getRevisionLookup() |
||
331 | ->getRevisionById( $revisionId ); |
||
332 | $content = $revisionRecord ? $revisionRecord->getContent( SlotRecord::MAIN ) : null; |
||
333 | |||
334 | if ( !( $content instanceof EntityContent ) ) { |
||
335 | return; |
||
336 | } |
||
337 | |||
338 | $notifier = $wikibaseRepo->getChangeNotifier(); |
||
339 | $notifier->notifyOnPageUndeleted( $revisionRecord ); |
||
340 | } |
||
341 | |||
342 | /** |
||
343 | * Nasty hack to inject information from RC into the change notification saved earlier |
||
344 | * by the onRevisionFromEditComplete hook handler. |
||
345 | * |
||
346 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/RecentChange_save |
||
347 | * |
||
348 | * @todo find a better way to do this! |
||
349 | * |
||
350 | * @param RecentChange $recentChange |
||
351 | */ |
||
352 | public static function onRecentChangeSave( RecentChange $recentChange ) { |
||
353 | $logType = $recentChange->getAttribute( 'rc_log_type' ); |
||
354 | $logAction = $recentChange->getAttribute( 'rc_log_action' ); |
||
355 | $revId = $recentChange->getAttribute( 'rc_this_oldid' ); |
||
356 | |||
357 | if ( $revId <= 0 ) { |
||
358 | // If we don't have a revision ID, we have no chance to find the right change to update. |
||
359 | // NOTE: As of February 2015, RC entries for undeletion have rc_this_oldid = 0. |
||
360 | return; |
||
361 | } |
||
362 | |||
363 | if ( $logType === null || ( $logType === 'delete' && $logAction === 'restore' ) ) { |
||
364 | $changeLookup = WikibaseRepo::getDefaultInstance()->getStore()->getEntityChangeLookup(); |
||
365 | |||
366 | /** @var RepoEntityChange $change */ |
||
367 | $change = $changeLookup->loadByRevisionId( $revId, EntityChangeLookup::FROM_MASTER ); |
||
368 | '@phan-var RepoEntityChange $change'; |
||
369 | |||
370 | if ( $change ) { |
||
371 | $changeStore = WikibaseRepo::getDefaultInstance()->getStore()->getChangeStore(); |
||
372 | |||
373 | $centralIdLookup = CentralIdLookup::factoryNonLocal(); |
||
374 | if ( $centralIdLookup === null ) { |
||
375 | $centralUserId = 0; |
||
376 | } else { |
||
377 | $repoUser = $recentChange->getPerformer(); |
||
378 | $centralUserId = $centralIdLookup->centralIdFromLocalUser( |
||
379 | $repoUser |
||
380 | ); |
||
381 | } |
||
382 | |||
383 | $change->setMetadataFromRC( $recentChange, $centralUserId ); |
||
384 | $changeStore->saveChange( $change ); |
||
385 | } |
||
386 | } |
||
387 | } |
||
388 | |||
389 | /** |
||
390 | * Allows to add user preferences. |
||
391 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/GetPreferences |
||
392 | * |
||
393 | * NOTE: Might make sense to put the inner functionality into a well structured Preferences file once this |
||
394 | * becomes more. |
||
395 | * |
||
396 | * @param User $user |
||
397 | * @param array[] &$preferences |
||
398 | */ |
||
399 | public static function onGetPreferences( User $user, array &$preferences ) { |
||
0 ignored issues
–
show
|
|||
400 | $preferences['wb-acknowledgedcopyrightversion'] = [ |
||
401 | 'type' => 'api' |
||
402 | ]; |
||
403 | |||
404 | $preferences['wb-dismissleavingsitenotice'] = [ |
||
405 | 'type' => 'api' |
||
406 | ]; |
||
407 | |||
408 | $preferences['wikibase-entitytermsview-showEntitytermslistview'] = [ |
||
409 | 'type' => 'toggle', |
||
410 | 'label-message' => 'wikibase-setting-entitytermsview-showEntitytermslistview', |
||
411 | 'help-message' => 'wikibase-setting-entitytermsview-showEntitytermslistview-help', |
||
412 | 'section' => 'rendering/advancedrendering', |
||
413 | 'default' => '1', |
||
414 | ]; |
||
415 | } |
||
416 | |||
417 | /** |
||
418 | * Called after fetching the core default user options. |
||
419 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/UserGetDefaultOptions |
||
420 | * |
||
421 | * @param array &$defaultOptions |
||
422 | */ |
||
423 | public static function onUserGetDefaultOptions( array &$defaultOptions ) { |
||
424 | // pre-select default language in the list of fallback languages |
||
425 | $defaultLang = $defaultOptions['language']; |
||
426 | $defaultOptions[ 'wb-languages-' . $defaultLang ] = 1; |
||
427 | } |
||
428 | |||
429 | /** |
||
430 | * Modify line endings on history page. |
||
431 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/PageHistoryLineEnding |
||
432 | * |
||
433 | * @param HistoryPager $history |
||
434 | * @param object $row |
||
435 | * @param string &$html |
||
436 | * @param array $classes |
||
437 | */ |
||
438 | public static function onPageHistoryLineEnding( HistoryPager $history, $row, &$html, array $classes ) { |
||
0 ignored issues
–
show
|
|||
439 | // Note: This assumes that HistoryPager::getTitle returns a Title. |
||
440 | $entityContentFactory = WikibaseRepo::getDefaultInstance()->getEntityContentFactory(); |
||
441 | |||
442 | $wikiPage = $history->getWikiPage(); |
||
443 | $services = MediaWikiServices::getInstance(); |
||
444 | |||
445 | $revisionRecord = $services->getRevisionFactory()->newRevisionFromRow( $row ); |
||
446 | $linkTarget = $revisionRecord->getPageAsLinkTarget(); |
||
447 | |||
448 | if ( $entityContentFactory->isEntityContentModel( $history->getTitle()->getContentModel() ) |
||
449 | && $wikiPage->getLatest() !== $revisionRecord->getId() |
||
450 | && $services->getPermissionManager()->quickUserCan( |
||
451 | 'edit', |
||
452 | $history->getUser(), |
||
453 | $linkTarget |
||
454 | ) |
||
455 | && !$revisionRecord->isDeleted( RevisionRecord::DELETED_TEXT ) |
||
456 | ) { |
||
457 | $link = $services->getLinkRenderer()->makeKnownLink( |
||
458 | $linkTarget, |
||
459 | $history->msg( 'wikibase-restoreold' )->text(), |
||
460 | [], |
||
461 | [ |
||
462 | 'action' => 'edit', |
||
463 | 'restore' => $revisionRecord->getId() |
||
464 | ] |
||
465 | ); |
||
466 | |||
467 | $html .= ' ' . $history->msg( 'parentheses' )->rawParams( $link )->escaped(); |
||
468 | } |
||
469 | } |
||
470 | |||
471 | /** |
||
472 | * Alter the structured navigation links in SkinTemplates. |
||
473 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/SkinTemplateNavigation |
||
474 | * |
||
475 | * @param SkinTemplate $skinTemplate |
||
476 | * @param array[] &$links |
||
477 | */ |
||
478 | public static function onPageTabs( SkinTemplate $skinTemplate, array &$links ) { |
||
479 | $entityContentFactory = WikibaseRepo::getDefaultInstance()->getEntityContentFactory(); |
||
480 | |||
481 | $title = $skinTemplate->getRelevantTitle(); |
||
482 | |||
483 | if ( $entityContentFactory->isEntityContentModel( $title->getContentModel() ) ) { |
||
484 | unset( $links['views']['edit'] ); |
||
485 | unset( $links['views']['viewsource'] ); |
||
486 | |||
487 | if ( MediaWikiServices::getInstance()->getPermissionManager() |
||
488 | ->quickUserCan( 'edit', $skinTemplate->getUser(), $title ) |
||
489 | ) { |
||
490 | $out = $skinTemplate->getOutput(); |
||
491 | $request = $skinTemplate->getRequest(); |
||
492 | $old = !$out->isRevisionCurrent() |
||
493 | && !$request->getCheck( 'diff' ); |
||
494 | |||
495 | $restore = $request->getCheck( 'restore' ); |
||
496 | |||
497 | if ( $old || $restore ) { |
||
498 | // insert restore tab into views array, at the second position |
||
499 | |||
500 | $revid = $restore |
||
501 | ? $request->getText( 'restore' ) |
||
502 | : $out->getRevisionId(); |
||
503 | |||
504 | $rev = MediaWikiServices::getInstance() |
||
505 | ->getRevisionLookup() |
||
506 | ->getRevisionById( $revid ); |
||
507 | if ( !$rev || $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) { |
||
508 | return; |
||
509 | } |
||
510 | |||
511 | $head = array_slice( $links['views'], 0, 1 ); |
||
512 | $tail = array_slice( $links['views'], 1 ); |
||
513 | $neck = [ |
||
514 | 'restore' => [ |
||
515 | 'class' => $restore ? 'selected' : false, |
||
516 | 'text' => $skinTemplate->getLanguage()->ucfirst( |
||
517 | wfMessage( 'wikibase-restoreold' )->text() |
||
518 | ), |
||
519 | 'href' => $title->getLocalURL( [ |
||
520 | 'action' => 'edit', |
||
521 | 'restore' => $revid |
||
522 | ] ), |
||
523 | ] |
||
524 | ]; |
||
525 | |||
526 | $links['views'] = array_merge( $head, $neck, $tail ); |
||
527 | } |
||
528 | } |
||
529 | } |
||
530 | } |
||
531 | |||
532 | /** |
||
533 | * Reorder the groups for the special pages |
||
534 | * |
||
535 | * @param array &$groups |
||
536 | * @param bool $moveOther |
||
537 | */ |
||
538 | public static function onSpecialPageReorderPages( &$groups, $moveOther ) { |
||
0 ignored issues
–
show
|
|||
539 | $groups = array_merge( [ 'wikibaserepo' => null ], $groups ); |
||
540 | } |
||
541 | |||
542 | /** |
||
543 | * Used to append a css class to the body, so the page can be identified as Wikibase item page. |
||
544 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/OutputPageBodyAttributes |
||
545 | * |
||
546 | * @param OutputPage $out |
||
547 | * @param Skin $skin |
||
548 | * @param array &$bodyAttrs |
||
549 | */ |
||
550 | public static function onOutputPageBodyAttributes( OutputPage $out, Skin $skin, array &$bodyAttrs ) { |
||
551 | $wikibaseRepo = WikibaseRepo::getDefaultInstance(); |
||
552 | $outputPageEntityIdReader = new OutputPageEntityIdReader( |
||
553 | new OutputPageEntityViewChecker( $wikibaseRepo->getEntityContentFactory() ), |
||
554 | $wikibaseRepo->getEntityIdParser() |
||
555 | ); |
||
556 | |||
557 | $entityId = $outputPageEntityIdReader->getEntityIdFromOutputPage( $out ); |
||
558 | |||
559 | if ( $entityId === null ) { |
||
560 | return; |
||
561 | } |
||
562 | |||
563 | // TODO: preg_replace kind of ridiculous here, should probably change the ENTITY_TYPE constants instead |
||
564 | $entityType = preg_replace( '/^wikibase-/i', '', $entityId->getEntityType() ); |
||
565 | |||
566 | // add class to body so it's clear this is a wb item: |
||
567 | $bodyAttrs['class'] .= ' wb-entitypage wb-' . $entityType . 'page'; |
||
568 | // add another class with the ID of the item: |
||
569 | $bodyAttrs['class'] .= ' wb-' . $entityType . 'page-' . $entityId->getSerialization(); |
||
570 | |||
571 | if ( $skin->getRequest()->getCheck( 'diff' ) ) { |
||
572 | $bodyAttrs['class'] .= ' wb-diffpage'; |
||
573 | } |
||
574 | |||
575 | if ( $out->getTitle() && $out->getRevisionId() !== $out->getTitle()->getLatestRevID() ) { |
||
576 | $bodyAttrs['class'] .= ' wb-oldrevpage'; |
||
577 | } |
||
578 | } |
||
579 | |||
580 | /** |
||
581 | * Handler for the ApiCheckCanExecute hook in ApiMain. |
||
582 | * |
||
583 | * This implementation causes the execution of ApiEditPage (action=edit) to fail |
||
584 | * for all namespaces reserved for Wikibase entities. This prevents direct text-level editing |
||
585 | * of structured data, and it also prevents other types of content being created in these |
||
586 | * namespaces. |
||
587 | * |
||
588 | * @param ApiBase $module The API module being called |
||
589 | * @param User $user The user calling the API |
||
590 | * @param array|string|null &$message Output-parameter holding for the message the call should fail with. |
||
591 | * This can be a message key or an array as expected by ApiBase::dieUsageMsg(). |
||
592 | * |
||
593 | * @return bool true to continue execution, false to abort and with $message as an error message. |
||
594 | */ |
||
595 | public static function onApiCheckCanExecute( ApiBase $module, User $user, &$message ) { |
||
0 ignored issues
–
show
|
|||
596 | if ( $module instanceof ApiEditPage ) { |
||
0 ignored issues
–
show
|
|||
597 | $params = $module->extractRequestParams(); |
||
598 | $pageObj = $module->getTitleOrPageId( $params ); |
||
599 | $namespace = $pageObj->getTitle()->getNamespace(); |
||
600 | |||
601 | // XXX FIXME: ApiEditPage doesn't expose the slot, but this 'magically' works if the edit is |
||
602 | // to a MAIN slot and the entity is stored in a non-MAIN slot, because it falls back. |
||
603 | // To be verified that this keeps working once T200570 is done in MediaWiki itself. |
||
604 | $slots = $params['slots'] ?? [ SlotRecord::MAIN ]; |
||
605 | |||
606 | $wikibaseRepo = WikibaseRepo::getDefaultInstance(); |
||
607 | |||
608 | /** |
||
609 | * Don't make Wikibase check if a user can execute when the namespace in question does |
||
610 | * not refer to a namespace used locally for Wikibase entities. |
||
611 | */ |
||
612 | $localEntitySource = $wikibaseRepo->getLocalEntitySource(); |
||
613 | if ( !in_array( $namespace, $localEntitySource->getEntityNamespaceIds() ) ) { |
||
614 | return true; |
||
615 | } |
||
616 | |||
617 | $entityContentFactory = $wikibaseRepo->getEntityContentFactory(); |
||
618 | $entityTypes = $wikibaseRepo->getEnabledEntityTypes(); |
||
619 | |||
620 | foreach ( $entityContentFactory->getEntityContentModels() as $contentModel ) { |
||
621 | /** @var EntityHandler $handler */ |
||
622 | $handler = ContentHandler::getForModelID( $contentModel ); |
||
623 | '@phan-var EntityHandler $handler'; |
||
624 | |||
625 | if ( !in_array( $handler->getEntityType(), $entityTypes ) ) { |
||
626 | // If the entity type isn't enabled then Wikibase shouldn't be checking anything. |
||
627 | continue; |
||
628 | } |
||
629 | |||
630 | if ( |
||
631 | $handler->getEntityNamespace() === $namespace && |
||
632 | in_array( $handler->getEntitySlotRole(), $slots, true ) |
||
633 | ) { |
||
634 | // XXX: This is most probably redundant with setting |
||
635 | // ContentHandler::supportsDirectApiEditing to false. |
||
636 | // trying to use ApiEditPage on an entity namespace |
||
637 | $params = $module->extractRequestParams(); |
||
638 | |||
639 | // allow undo |
||
640 | if ( $params['undo'] > 0 ) { |
||
641 | return true; |
||
642 | } |
||
643 | |||
644 | // fail |
||
645 | $message = [ |
||
646 | 'wikibase-no-direct-editing', |
||
647 | $pageObj->getTitle()->getNsText() |
||
648 | ]; |
||
649 | |||
650 | return false; |
||
651 | } |
||
652 | } |
||
653 | } |
||
654 | |||
655 | return true; |
||
656 | } |
||
657 | |||
658 | /** |
||
659 | * Handler for the TitleGetRestrictionTypes hook. |
||
660 | * |
||
661 | * Implemented to prevent people from protecting pages from being |
||
662 | * created or moved in an entity namespace (which is pointless). |
||
663 | * |
||
664 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/TitleGetRestrictionTypes |
||
665 | * |
||
666 | * @param Title $title |
||
667 | * @param string[] &$types The types of protection available |
||
668 | */ |
||
669 | public static function onTitleGetRestrictionTypes( Title $title, array &$types ) { |
||
670 | $namespaceLookup = WikibaseRepo::getDefaultInstance()->getLocalEntityNamespaceLookup(); |
||
671 | |||
672 | if ( $namespaceLookup->isEntityNamespace( $title->getNamespace() ) ) { |
||
673 | // Remove create and move protection for Wikibase namespaces |
||
674 | $types = array_diff( $types, [ 'create', 'move' ] ); |
||
675 | } |
||
676 | } |
||
677 | |||
678 | /** |
||
679 | * Hook handler for AbuseFilter's AbuseFilter-contentToString hook, implemented |
||
680 | * to provide a custom text representation of Entities for filtering. |
||
681 | * |
||
682 | * @param Content $content |
||
683 | * @param string &$text The resulting text |
||
684 | * |
||
685 | * @return bool |
||
686 | */ |
||
687 | public static function onAbuseFilterContentToString( Content $content, &$text ) { |
||
688 | if ( $content instanceof EntityContent ) { |
||
689 | $text = $content->getTextForFilters(); |
||
690 | |||
691 | return false; |
||
692 | } |
||
693 | |||
694 | return true; |
||
695 | } |
||
696 | |||
697 | /** |
||
698 | * Handler for the FormatAutocomments hook, implementing localized formatting |
||
699 | * for machine readable autocomments generated by SummaryFormatter. |
||
700 | * |
||
701 | * @param string &$comment reference to the autocomment text |
||
702 | * @param bool $pre true if there is content before the autocomment |
||
703 | * @param string $auto the autocomment unformatted |
||
704 | * @param bool $post true if there is content after the autocomment |
||
705 | * @param Title|null $title use for further information |
||
706 | * @param bool $local shall links be generated locally or globally |
||
707 | */ |
||
708 | public static function onFormat( &$comment, $pre, $auto, $post, $title, $local ) { |
||
0 ignored issues
–
show
|
|||
709 | global $wgLang, $wgTitle; |
||
710 | |||
711 | // If it is possible to avoid loading the whole page then the code will be lighter on the server. |
||
712 | if ( !( $title instanceof Title ) ) { |
||
0 ignored issues
–
show
|
|||
713 | $title = $wgTitle; |
||
714 | } |
||
715 | |||
716 | if ( !( $title instanceof Title ) ) { |
||
0 ignored issues
–
show
|
|||
717 | return; |
||
718 | } |
||
719 | |||
720 | $namespaceLookup = WikibaseRepo::getDefaultInstance()->getEntityNamespaceLookup(); |
||
721 | $entityType = $namespaceLookup->getEntityType( $title->getNamespace() ); |
||
722 | if ( $entityType === null ) { |
||
723 | return; |
||
724 | } |
||
725 | |||
726 | if ( $wgLang instanceof StubUserLang ) { |
||
0 ignored issues
–
show
|
|||
727 | StubUserLang::unstub( $wgLang ); |
||
728 | } |
||
729 | |||
730 | $formatter = new AutoCommentFormatter( $wgLang, [ 'wikibase-' . $entityType, 'wikibase-entity' ] ); |
||
731 | $formattedComment = $formatter->formatAutoComment( $auto ); |
||
732 | |||
733 | if ( is_string( $formattedComment ) ) { |
||
734 | $comment = $formatter->wrapAutoComment( $pre, $formattedComment, $post ); |
||
735 | } |
||
736 | } |
||
737 | |||
738 | /** |
||
739 | * Called when pushing meta-info from the ParserOutput into OutputPage. |
||
740 | * Used to transfer 'wikibase-view-chunks' and entity data from ParserOutput to OutputPage. |
||
741 | * |
||
742 | * @param OutputPage $out |
||
743 | * @param ParserOutput $parserOutput |
||
744 | */ |
||
745 | public static function onOutputPageParserOutput( OutputPage $out, ParserOutput $parserOutput ) { |
||
746 | // Set in EntityParserOutputGenerator. |
||
747 | $placeholders = $parserOutput->getExtensionData( 'wikibase-view-chunks' ); |
||
748 | if ( $placeholders !== null ) { |
||
749 | $out->setProperty( 'wikibase-view-chunks', $placeholders ); |
||
750 | } |
||
751 | |||
752 | // Set in EntityParserOutputGenerator. |
||
753 | $termsListItems = $parserOutput->getExtensionData( 'wikibase-terms-list-items' ); |
||
754 | if ( $termsListItems !== null ) { |
||
755 | $out->setProperty( 'wikibase-terms-list-items', $termsListItems ); |
||
756 | } |
||
757 | |||
758 | // Used in ViewEntityAction and EditEntityAction to override the page HTML title |
||
759 | // with the label, if available, or else the id. Passed via parser output |
||
760 | // and output page to save overhead of fetching content and accessing an entity |
||
761 | // on page view. |
||
762 | $meta = $parserOutput->getExtensionData( 'wikibase-meta-tags' ); |
||
763 | $out->setProperty( 'wikibase-meta-tags', $meta ); |
||
764 | |||
765 | $out->setProperty( |
||
766 | TermboxView::TERMBOX_MARKUP, |
||
767 | $parserOutput->getExtensionData( TermboxView::TERMBOX_MARKUP ) |
||
768 | ); |
||
769 | |||
770 | // Array with <link rel="alternate"> tags for the page HEAD. |
||
771 | $alternateLinks = $parserOutput->getExtensionData( 'wikibase-alternate-links' ); |
||
772 | if ( $alternateLinks !== null ) { |
||
773 | foreach ( $alternateLinks as $link ) { |
||
774 | $out->addLink( $link ); |
||
775 | } |
||
776 | } |
||
777 | } |
||
778 | |||
779 | /** |
||
780 | * Handler for the ContentModelCanBeUsedOn hook, used to prevent pages of inappropriate type |
||
781 | * to be placed in an entity namespace. |
||
782 | * |
||
783 | * @param string $contentModel |
||
784 | * @param LinkTarget $title Actually a Title object, but we only require getNamespace |
||
785 | * @param bool &$ok |
||
786 | * |
||
787 | * @return bool |
||
788 | */ |
||
789 | public static function onContentModelCanBeUsedOn( $contentModel, LinkTarget $title, &$ok ) { |
||
790 | $wikibaseRepo = WikibaseRepo::getDefaultInstance(); |
||
791 | |||
792 | $namespaceLookup = $wikibaseRepo->getEntityNamespaceLookup(); |
||
793 | $contentModelIds = $wikibaseRepo->getContentModelMappings(); |
||
794 | |||
795 | // Find any entity type that is mapped to the title namespace |
||
796 | $expectedEntityType = $namespaceLookup->getEntityType( $title->getNamespace() ); |
||
797 | |||
798 | // If we don't expect an entity type, then don't check anything else. |
||
799 | if ( $expectedEntityType === null ) { |
||
800 | return true; |
||
801 | } |
||
802 | |||
803 | // If the entity type is not from the local source, don't check anything else |
||
804 | $entitySource = $wikibaseRepo->getEntitySourceDefinitions()->getSourceForEntityType( $expectedEntityType ); |
||
805 | if ( $entitySource->getSourceName() !== $wikibaseRepo->getLocalEntitySource()->getSourceName() ) { |
||
806 | return true; |
||
807 | } |
||
808 | |||
809 | // XXX: If the slot is not the main slot, then assume someone isn't somehow trying |
||
810 | // to add another content type there. We want to actually check per slot type here. |
||
811 | // This should be fixed with https://gerrit.wikimedia.org/r/#/c/mediawiki/core/+/434544/ |
||
812 | $expectedSlot = $namespaceLookup->getEntitySlotRole( $expectedEntityType ); |
||
813 | if ( $expectedSlot !== 'main' ) { |
||
814 | return true; |
||
815 | } |
||
816 | |||
817 | // If the namespace is an entity namespace, the content model |
||
818 | // must be the model assigned to that namespace. |
||
819 | $expectedModel = $contentModelIds[$expectedEntityType]; |
||
820 | if ( $expectedModel !== $contentModel ) { |
||
821 | $ok = false; |
||
822 | return false; |
||
823 | } |
||
824 | |||
825 | return true; |
||
826 | } |
||
827 | |||
828 | /** |
||
829 | * Exposes configuration values to the action=query&meta=siteinfo API, including lists of |
||
830 | * property and data value types, sparql endpoint, and several base URLs and URIs. |
||
831 | * |
||
832 | * @param ApiQuerySiteinfo $api |
||
833 | * @param array &$data |
||
834 | */ |
||
835 | public static function onAPIQuerySiteInfoGeneralInfo( ApiQuerySiteinfo $api, array &$data ) { |
||
0 ignored issues
–
show
|
|||
836 | $wikibaseRepo = WikibaseRepo::getDefaultInstance(); |
||
837 | $dataTypes = $wikibaseRepo->getDataTypeFactory()->getTypes(); |
||
838 | $propertyTypes = []; |
||
839 | |||
840 | foreach ( $dataTypes as $id => $type ) { |
||
841 | $propertyTypes[$id] = [ 'valuetype' => $type->getDataValueType() ]; |
||
842 | } |
||
843 | |||
844 | $data['wikibase-propertytypes'] = $propertyTypes; |
||
845 | |||
846 | $conceptBaseUri = $wikibaseRepo->getSettings()->getSetting( 'conceptBaseUri' ); |
||
847 | $data['wikibase-conceptbaseuri'] = $conceptBaseUri; |
||
848 | |||
849 | $geoShapeStorageBaseUrl = $wikibaseRepo->getSettings()->getSetting( 'geoShapeStorageBaseUrl' ); |
||
850 | $data['wikibase-geoshapestoragebaseurl'] = $geoShapeStorageBaseUrl; |
||
851 | |||
852 | $tabularDataStorageBaseUrl = $wikibaseRepo->getSettings()->getSetting( 'tabularDataStorageBaseUrl' ); |
||
853 | $data['wikibase-tabulardatastoragebaseurl'] = $tabularDataStorageBaseUrl; |
||
854 | |||
855 | $sparqlEndpoint = $wikibaseRepo->getSettings()->getSetting( 'sparqlEndpoint' ); |
||
856 | if ( is_string( $sparqlEndpoint ) ) { |
||
857 | $data['wikibase-sparql'] = $sparqlEndpoint; |
||
858 | } |
||
859 | } |
||
860 | |||
861 | /** |
||
862 | * Helper for onAPIQuerySiteInfoStatisticsInfo |
||
863 | * |
||
864 | * @param object $row |
||
865 | * @return array |
||
866 | */ |
||
867 | private static function formatDispatchRow( $row ) { |
||
868 | $data = [ |
||
869 | 'pending' => $row->chd_pending, |
||
870 | 'lag' => $row->chd_lag, |
||
871 | ]; |
||
872 | if ( isset( $row->chd_site ) ) { |
||
873 | $data['site'] = $row->chd_site; |
||
874 | } |
||
875 | if ( isset( $row->chd_seen ) ) { |
||
876 | $data['position'] = $row->chd_seen; |
||
877 | } |
||
878 | if ( isset( $row->chd_touched ) ) { |
||
879 | $data['touched'] = wfTimestamp( TS_ISO_8601, $row->chd_touched ); |
||
880 | } |
||
881 | |||
882 | return $data; |
||
883 | } |
||
884 | |||
885 | /** |
||
886 | * Adds DispatchStats info to the API |
||
887 | * |
||
888 | * @param array[] &$data |
||
889 | */ |
||
890 | public static function onAPIQuerySiteInfoStatisticsInfo( array &$data ) { |
||
891 | $stats = new DispatchStats(); |
||
892 | $stats->load(); |
||
893 | if ( $stats->hasStats() ) { |
||
894 | $data['dispatch'] = [ |
||
895 | 'oldest' => [ |
||
896 | 'id' => $stats->getMinChangeId(), |
||
897 | 'timestamp' => $stats->getMinChangeTimestamp(), |
||
898 | ], |
||
899 | 'newest' => [ |
||
900 | 'id' => $stats->getMaxChangeId(), |
||
901 | 'timestamp' => $stats->getMaxChangeTimestamp(), |
||
902 | ], |
||
903 | 'freshest' => self::formatDispatchRow( $stats->getFreshest() ), |
||
0 ignored issues
–
show
It seems like
$stats->getFreshest() can be null ; however, formatDispatchRow() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
904 | 'median' => self::formatDispatchRow( $stats->getMedian() ), |
||
0 ignored issues
–
show
It seems like
$stats->getMedian() can be null ; however, formatDispatchRow() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
905 | 'stalest' => self::formatDispatchRow( $stats->getStalest() ), |
||
0 ignored issues
–
show
It seems like
$stats->getStalest() can be null ; however, formatDispatchRow() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
906 | 'average' => self::formatDispatchRow( $stats->getAverage() ), |
||
0 ignored issues
–
show
It seems like
$stats->getAverage() can be null ; however, formatDispatchRow() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
![]() |
|||
907 | ]; |
||
908 | } |
||
909 | } |
||
910 | |||
911 | /** |
||
912 | * Called by Import.php. Implemented to prevent the import of entities. |
||
913 | * |
||
914 | * @param object $importer unclear, see Bug T66657 |
||
915 | * @param array $pageInfo |
||
916 | * @param array $revisionInfo |
||
917 | * |
||
918 | * @throws MWException |
||
919 | */ |
||
920 | public static function onImportHandleRevisionXMLTag( $importer, $pageInfo, $revisionInfo ) { |
||
0 ignored issues
–
show
|
|||
921 | if ( isset( $revisionInfo['model'] ) ) { |
||
922 | $wikibaseRepo = WikibaseRepo::getDefaultInstance(); |
||
923 | $contentModels = $wikibaseRepo->getContentModelMappings(); |
||
924 | $allowImport = $wikibaseRepo->getSettings()->getSetting( 'allowEntityImport' ); |
||
925 | |||
926 | if ( !$allowImport && in_array( $revisionInfo['model'], $contentModels ) ) { |
||
927 | // Skip entities. |
||
928 | // XXX: This is rather rough. |
||
929 | throw new MWException( |
||
930 | 'To avoid ID conflicts, the import of Wikibase entities is not supported.' |
||
931 | . ' You can enable imports using the "allowEntityImport" setting.' |
||
932 | ); |
||
933 | } |
||
934 | } |
||
935 | } |
||
936 | |||
937 | /** |
||
938 | * Add Concept URI link to the toolbox section of the sidebar. |
||
939 | * |
||
940 | * @param Skin $skin |
||
941 | * @param string[] &$sidebar |
||
942 | * @return void |
||
943 | */ |
||
944 | public static function onSidebarBeforeOutput( Skin $skin, array &$sidebar ): void { |
||
945 | $repo = WikibaseRepo::getDefaultInstance(); |
||
946 | $hookHandler = new SidebarBeforeOutputHookHandler( |
||
947 | $repo->getSettings()->getSetting( 'conceptBaseUri' ), |
||
948 | $repo->getEntityIdLookup(), |
||
949 | $repo->getEntityLookup(), |
||
950 | $repo->getEntityNamespaceLookup(), |
||
951 | $repo->getLogger() |
||
952 | ); |
||
953 | |||
954 | $conceptUriLink = $hookHandler->buildConceptUriLink( $skin ); |
||
955 | |||
956 | if ( $conceptUriLink === null ) { |
||
957 | return; |
||
958 | } |
||
959 | |||
960 | $sidebar['TOOLBOX']['wb-concept-uri'] = $conceptUriLink; |
||
961 | } |
||
962 | |||
963 | /** |
||
964 | * Register ResourceLoader modules with dynamic dependencies. |
||
965 | * |
||
966 | * @param ResourceLoader $resourceLoader |
||
967 | */ |
||
968 | public static function onResourceLoaderRegisterModules( ResourceLoader $resourceLoader ) { |
||
969 | $moduleTemplate = [ |
||
970 | 'localBasePath' => __DIR__ . '/..', |
||
971 | 'remoteExtPath' => 'Wikibase/repo', |
||
972 | ]; |
||
973 | |||
974 | $modules = [ |
||
975 | 'wikibase.WikibaseContentLanguages' => $moduleTemplate + [ |
||
976 | 'scripts' => [ |
||
977 | 'resources/wikibase.WikibaseContentLanguages.js', |
||
978 | ], |
||
979 | 'dependencies' => [ |
||
980 | 'util.ContentLanguages', |
||
981 | 'util.inherit', |
||
982 | 'wikibase', |
||
983 | ], |
||
984 | 'targets' => [ 'desktop', 'mobile' ], |
||
985 | ], |
||
986 | 'wikibase.special.languageLabelDescriptionAliases' => $moduleTemplate + [ |
||
987 | 'scripts' => [ |
||
988 | 'resources/wikibase.special/wikibase.special.languageLabelDescriptionAliases.js', |
||
989 | ], |
||
990 | 'dependencies' => [ |
||
991 | 'oojs-ui', |
||
992 | ], |
||
993 | 'messages' => [ |
||
994 | 'wikibase-label-edit-placeholder', |
||
995 | 'wikibase-label-edit-placeholder-language-aware', |
||
996 | 'wikibase-description-edit-placeholder', |
||
997 | 'wikibase-description-edit-placeholder-language-aware', |
||
998 | 'wikibase-aliases-edit-placeholder', |
||
999 | 'wikibase-aliases-edit-placeholder-language-aware', |
||
1000 | ], |
||
1001 | ], |
||
1002 | ]; |
||
1003 | |||
1004 | $isUlsLoaded = ExtensionRegistry::getInstance()->isLoaded( 'UniversalLanguageSelector' ); |
||
1005 | if ( $isUlsLoaded ) { |
||
1006 | $modules['wikibase.WikibaseContentLanguages']['dependencies'][] = 'ext.uls.languagenames'; |
||
1007 | $modules['wikibase.special.languageLabelDescriptionAliases']['dependencies'][] = 'ext.uls.mediawiki'; |
||
1008 | } |
||
1009 | |||
1010 | $resourceLoader->register( $modules ); |
||
1011 | } |
||
1012 | |||
1013 | /** |
||
1014 | * Adds the Wikis using the entity in action=info |
||
1015 | * |
||
1016 | * @param IContextSource $context |
||
1017 | * @param array[] &$pageInfo |
||
1018 | */ |
||
1019 | public static function onInfoAction( IContextSource $context, array &$pageInfo ) { |
||
1020 | $wikibaseRepo = WikibaseRepo::getDefaultInstance(); |
||
1021 | |||
1022 | $namespaceChecker = $wikibaseRepo->getEntityNamespaceLookup(); |
||
1023 | $title = $context->getTitle(); |
||
1024 | |||
1025 | if ( !$title || !$namespaceChecker->isNamespaceWithEntities( $title->getNamespace() ) ) { |
||
1026 | // shorten out |
||
1027 | return; |
||
1028 | } |
||
1029 | |||
1030 | $mediaWikiServices = MediaWikiServices::getInstance(); |
||
1031 | $loadBalancer = $mediaWikiServices->getDBLoadBalancer(); |
||
1032 | $subscriptionLookup = new SqlSubscriptionLookup( $loadBalancer ); |
||
1033 | $entityIdLookup = $wikibaseRepo->getEntityIdLookup(); |
||
1034 | |||
1035 | $siteLookup = $mediaWikiServices->getSiteLookup(); |
||
1036 | |||
1037 | $infoActionHookHandler = new InfoActionHookHandler( |
||
1038 | $namespaceChecker, |
||
1039 | $subscriptionLookup, |
||
1040 | $siteLookup, |
||
1041 | $entityIdLookup, |
||
1042 | $context, |
||
1043 | PageProps::getInstance() |
||
1044 | ); |
||
1045 | |||
1046 | $pageInfo = $infoActionHookHandler->handle( $context, $pageInfo ); |
||
1047 | } |
||
1048 | |||
1049 | /** |
||
1050 | * Handler for the ApiMaxLagInfo to add dispatching lag stats |
||
1051 | * |
||
1052 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/ApiMaxLagInfo |
||
1053 | * |
||
1054 | * @param array &$lagInfo |
||
1055 | */ |
||
1056 | public static function onApiMaxLagInfo( array &$lagInfo ) { |
||
1057 | |||
1058 | $dispatchLagToMaxLagFactor = WikibaseRepo::getDefaultInstance()->getSettings()->getSetting( |
||
1059 | 'dispatchLagToMaxLagFactor' |
||
1060 | ); |
||
1061 | |||
1062 | if ( $dispatchLagToMaxLagFactor <= 0 ) { |
||
1063 | return; |
||
1064 | } |
||
1065 | |||
1066 | $stats = new DispatchStats(); |
||
1067 | $stats->load(); |
||
1068 | $median = $stats->getMedian(); |
||
1069 | |||
1070 | if ( $median ) { |
||
1071 | $maxDispatchLag = $median->chd_lag / (float)$dispatchLagToMaxLagFactor; |
||
1072 | if ( $maxDispatchLag > $lagInfo['lag'] ) { |
||
1073 | $lagInfo = [ |
||
1074 | 'host' => $median->chd_site, |
||
1075 | 'lag' => $maxDispatchLag, |
||
1076 | 'type' => 'wikibase-dispatching', |
||
1077 | 'dispatchLag' => $median->chd_lag, |
||
1078 | ]; |
||
1079 | } |
||
1080 | } |
||
1081 | } |
||
1082 | |||
1083 | /** |
||
1084 | * Handler for the ParserOptionsRegister hook to add a "wb" option for cache-splitting |
||
1085 | * |
||
1086 | * This registers a lazy-loaded parser option with its value being the EntityHandler |
||
1087 | * parser version. Non-Wikibase parses will ignore this option, while Wikibase parses |
||
1088 | * will trigger its loading via ParserOutput::recordOption() and thereby include it |
||
1089 | * in the cache key to fragment the cache by EntityHandler::PARSER_VERSION. |
||
1090 | * |
||
1091 | * @param array &$defaults Options and their defaults |
||
1092 | * @param array &$inCacheKey Whether each option splits the parser cache |
||
1093 | * @param array &$lazyOptions Initializers for lazy-loaded options |
||
1094 | */ |
||
1095 | public static function onParserOptionsRegister( &$defaults, &$inCacheKey, &$lazyOptions ) { |
||
1096 | $defaults['wb'] = null; |
||
1097 | $inCacheKey['wb'] = true; |
||
1098 | $lazyOptions['wb'] = function () { |
||
1099 | return EntityHandler::PARSER_VERSION; |
||
1100 | }; |
||
1101 | $defaults['termboxVersion'] = null; |
||
1102 | $inCacheKey['termboxVersion'] = true; |
||
1103 | $lazyOptions['termboxVersion'] = function () { |
||
1104 | return TermboxFlag::getInstance()->shouldRenderTermbox() ? |
||
1105 | TermboxView::TERMBOX_VERSION . TermboxView::CACHE_VERSION : |
||
1106 | PlaceholderEmittingEntityTermsView::TERMBOX_VERSION . PlaceholderEmittingEntityTermsView::CACHE_VERSION; |
||
1107 | }; |
||
1108 | } |
||
1109 | |||
1110 | public static function onRejectParserCacheValue( ParserOutput $parserValue, WikiPage $wikiPage, ParserOptions $parserOpts ) { |
||
0 ignored issues
–
show
|
|||
1111 | $rejector = new TermboxVersionParserCacheValueRejector( TermboxFlag::getInstance() ); |
||
1112 | return $rejector->keepCachedValue( $parserValue, $parserOpts ); |
||
1113 | } |
||
1114 | |||
1115 | public static function onApiQueryModuleManager( ApiModuleManager $moduleManager ) { |
||
1116 | global $wgWBRepoSettings; |
||
1117 | |||
1118 | if ( isset( $wgWBRepoSettings['dataBridgeEnabled'] ) && $wgWBRepoSettings['dataBridgeEnabled'] ) { |
||
1119 | $moduleManager->addModule( |
||
1120 | 'wbdatabridgeconfig', |
||
1121 | 'meta', |
||
1122 | [ |
||
1123 | 'class' => MetaDataBridgeConfig::class, |
||
1124 | 'factory' => function( ApiQuery $apiQuery, $moduleName ) { |
||
1125 | $repo = WikibaseRepo::getDefaultInstance(); |
||
1126 | |||
1127 | return new MetaDataBridgeConfig( |
||
1128 | $repo->getSettings(), |
||
1129 | $apiQuery, |
||
1130 | $moduleName, |
||
1131 | function ( string $pagename ): ?string { |
||
1132 | $pageTitle = Title::newFromText( $pagename ); |
||
1133 | return $pageTitle ? $pageTitle->getFullURL() : null; |
||
1134 | } |
||
1135 | ); |
||
1136 | }, |
||
1137 | ] |
||
1138 | ); |
||
1139 | } |
||
1140 | } |
||
1141 | |||
1142 | public static function onMediaWikiPHPUnitTestStartTest( $test ) { |
||
0 ignored issues
–
show
|
|||
1143 | WikibaseRepo::resetClassStatics(); |
||
1144 | } |
||
1145 | |||
1146 | /** |
||
1147 | * Register the parser functions. |
||
1148 | * |
||
1149 | * @param Parser $parser |
||
1150 | */ |
||
1151 | public static function onParserFirstCallInit( Parser $parser ) { |
||
1152 | $parser->setFunctionHook( |
||
1153 | CommaSeparatedList::NAME, |
||
1154 | [ CommaSeparatedList::class, 'handle' ] |
||
1155 | ); |
||
1156 | } |
||
1157 | |||
1158 | public static function onRegistration() { |
||
1159 | global $wgResourceModules; |
||
1160 | |||
1161 | LibHooks::onRegistration(); |
||
1162 | ViewHooks::onRegistration(); |
||
1163 | |||
1164 | $wgResourceModules = array_merge( |
||
1165 | $wgResourceModules, |
||
1166 | require __DIR__ . '/../resources/Resources.php' |
||
1167 | ); |
||
1168 | } |
||
1169 | } |
||
1170 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.