221 $services = MediaWikiServices::getInstance();
226 $id =
$title->getArticleID();
227 $config = $this->context->getConfig();
228 $linkRenderer = $services->getLinkRenderer();
233 $pageProperties = $props[$id] ?? [];
237 $pageInfo[
'header-basic'] = [];
240 $displayTitle = $pageProperties[
'displaytitle'] ??
$title->getPrefixedText();
242 $pageInfo[
'header-basic'][] = [
243 $this->
msg(
'pageinfo-display-title' ), $displayTitle
247 $redirectTarget = $this->
getWikiPage()->getRedirectTarget();
248 if ( $redirectTarget !==
null ) {
249 $pageInfo[
'header-basic'][] = [
250 $this->
msg(
'pageinfo-redirectsto' ),
251 $linkRenderer->makeLink( $redirectTarget ) .
252 $this->
msg(
'word-separator' )->escaped() .
253 $this->
msg(
'parentheses' )->rawParams( $linkRenderer->makeLink(
255 $this->msg(
'pageinfo-redirectsto-info' )->text(),
257 [
'action' =>
'info' ]
263 $sortKey = $pageProperties[
'defaultsort'] ??
$title->getCategorySortkey();
265 $sortKey = htmlspecialchars( $sortKey );
266 $pageInfo[
'header-basic'][] = [ $this->
msg(
'pageinfo-default-sort' ), $sortKey ];
269 $pageInfo[
'header-basic'][] = [
270 $this->
msg(
'pageinfo-length' ),
$lang->formatNum(
$title->getLength() )
274 $pageNamespace =
$title->getNsText();
275 if ( $pageNamespace ) {
276 $pageInfo[
'header-basic'][] = [ $this->
msg(
'pageinfo-namespace' ), $pageNamespace ];
280 $pageInfo[
'header-basic'][] = [ $this->
msg(
'pageinfo-article-id' ), $id ];
283 $pageLang =
$title->getPageLanguage()->getCode();
285 $pageLangHtml = $pageLang .
' - ' .
286 $services->getLanguageNameUtils()->getLanguageName( $pageLang,
$lang->getCode() );
288 $permissionManager = $services->getPermissionManager();
289 if ( $config->get(
'PageLanguageUseDB' )
290 && $permissionManager->userCan(
'pagelang', $user,
$title )
292 $pageLangHtml .=
' ' . $this->
msg(
'parentheses' )->rawParams( $linkRenderer->makeLink(
294 $this->msg(
'pageinfo-language-change' )->text()
298 $pageInfo[
'header-basic'][] = [
299 $this->
msg(
'pageinfo-language' )->escaped(),
304 $modelHtml = htmlspecialchars( ContentHandler::getLocalizedName(
$title->getContentModel() ) );
306 if ( $permissionManager->userCan(
'editcontentmodel', $user,
$title ) ) {
307 $modelHtml .=
' ' . $this->
msg(
'parentheses' )->rawParams( $linkRenderer->makeLink(
309 $this->msg(
'pageinfo-content-model-change' )->text()
313 $pageInfo[
'header-basic'][] = [
314 $this->
msg(
'pageinfo-content-model' ),
320 if ( $pageUser && $pageUser->getId() && !$pageUser->isHidden() ) {
321 $pageInfo[
'header-basic'][] = [
322 $this->
msg(
'pageinfo-user-id' ),
330 if ( isset( $pageProperties[
'noindex'] ) ) {
331 $pOutput->setIndexPolicy(
'noindex' );
333 if ( isset( $pageProperties[
'index'] ) ) {
334 $pOutput->setIndexPolicy(
'index' );
338 $policy = $this->
getArticle()->getRobotPolicy(
'view', $pOutput );
339 $pageInfo[
'header-basic'][] = [
341 $this->
msg(
'pageinfo-robot-policy' ),
342 $this->
msg(
"pageinfo-robot-{$policy['index']}" )
345 $unwatchedPageThreshold = $config->get(
'UnwatchedPageThreshold' );
346 if ( $permissionManager->userHasRight( $user,
'unwatchedpages' ) ||
347 ( $unwatchedPageThreshold !==
false &&
348 $pageCounts[
'watchers'] >= $unwatchedPageThreshold )
351 $pageInfo[
'header-basic'][] = [
352 $this->
msg(
'pageinfo-watchers' ),
353 $lang->formatNum( $pageCounts[
'watchers'] )
356 $config->get(
'ShowUpdatedMarker' ) &&
357 isset( $pageCounts[
'visitingWatchers'] )
359 $minToDisclose = $config->get(
'UnwatchedPageSecret' );
360 if ( $pageCounts[
'visitingWatchers'] > $minToDisclose ||
361 $permissionManager->userHasRight( $user,
'unwatchedpages' ) ) {
362 $pageInfo[
'header-basic'][] = [
363 $this->
msg(
'pageinfo-visiting-watchers' ),
364 $lang->formatNum( $pageCounts[
'visitingWatchers'] )
367 $pageInfo[
'header-basic'][] = [
368 $this->
msg(
'pageinfo-visiting-watchers' ),
369 $this->
msg(
'pageinfo-few-visiting-watchers' )
373 } elseif ( $unwatchedPageThreshold !==
false ) {
374 $pageInfo[
'header-basic'][] = [
375 $this->
msg(
'pageinfo-watchers' ),
376 $this->
msg(
'pageinfo-few-watchers' )->numParams( $unwatchedPageThreshold )
382 $pageInfo[
'header-basic'][] = [
383 $linkRenderer->makeLink(
385 $this->
msg(
'pageinfo-redirects-name' )->text(),
393 $this->
msg(
'pageinfo-redirects-value' )
394 ->numParams( count(
$title->getRedirectsHere() ) )
399 $pageInfo[
'header-basic'][] = [
400 $this->
msg(
'pageinfo-contentpage' ),
401 $this->
msg(
'pageinfo-contentpage-yes' )
406 if ( $services->getNamespaceInfo()->hasSubpages(
$title->getNamespace() ) ) {
408 'Prefixindex',
$title->getPrefixedText() .
'/' );
409 $pageInfo[
'header-basic'][] = [
410 $linkRenderer->makeLink(
412 $this->
msg(
'pageinfo-subpages-name' )->text()
414 $this->
msg(
'pageinfo-subpages-value' )
416 $pageCounts[
'subpages'][
'total'],
417 $pageCounts[
'subpages'][
'redirects'],
418 $pageCounts[
'subpages'][
'nonredirects'] )
423 $category = Category::newFromTitle(
$title );
427 $allCount = (int)$category->getPageCount();
428 $subcatCount = (int)$category->getSubcatCount();
429 $fileCount = (int)$category->getFileCount();
430 $pagesCount = $allCount - $subcatCount - $fileCount;
432 $pageInfo[
'category-info'] = [
434 $this->
msg(
'pageinfo-category-total' ),
435 $lang->formatNum( $allCount )
438 $this->
msg(
'pageinfo-category-pages' ),
439 $lang->formatNum( $pagesCount )
442 $this->
msg(
'pageinfo-category-subcats' ),
443 $lang->formatNum( $subcatCount )
446 $this->
msg(
'pageinfo-category-files' ),
447 $lang->formatNum( $fileCount )
454 $fileObj = $services->getRepoGroup()->findFile(
$title );
455 if ( $fileObj !==
false ) {
457 $output = Wikimedia\base_convert( $fileObj->getSha1(), 36, 16, 40 );
458 $pageInfo[
'header-basic'][] = [
459 $this->
msg(
'pageinfo-file-hash' ),
466 $pageInfo[
'header-restrictions'] = [];
469 if (
$title->isCascadeProtected() ) {
471 $sources =
$title->getCascadeProtectionSources()[0];
473 foreach ( $sources as $sourceTitle ) {
474 $cascadingFrom .= Html::rawElement(
475 'li', [], $linkRenderer->makeKnownLink( $sourceTitle ) );
478 $cascadingFrom = Html::rawElement(
'ul', [], $cascadingFrom );
479 $pageInfo[
'header-restrictions'][] = [
480 $this->
msg(
'pageinfo-protect-cascading-from' ),
486 if (
$title->areRestrictionsCascading() ) {
487 $pageInfo[
'header-restrictions'][] = [
488 $this->
msg(
'pageinfo-protect-cascading' ),
489 $this->
msg(
'pageinfo-protect-cascading-yes' )
494 foreach (
$title->getRestrictionTypes() as $restrictionType ) {
495 $protectionLevel = implode(
', ',
$title->getRestrictions( $restrictionType ) );
497 if ( $protectionLevel ==
'' ) {
499 $message = $this->
msg(
'protect-default' )->escaped();
503 $message = $this->
msg(
"protect-level-$protectionLevel" );
504 if ( $message->isDisabled() ) {
506 $message = $this->
msg(
"protect-fallback", $protectionLevel )->parse();
508 $message = $message->escaped();
511 $expiry =
$title->getRestrictionExpiry( $restrictionType );
512 $formattedexpiry = $this->
msg(
'parentheses',
513 $lang->formatExpiry( $expiry ) )->escaped();
514 $message .= $this->
msg(
'word-separator' )->escaped() . $formattedexpiry;
518 $pageInfo[
'header-restrictions'][] = [
519 $this->
msg(
"restriction-$restrictionType" ), $message
523 $pageInfo[
'header-restrictions'][] = [
525 $linkRenderer->makeKnownLink(
527 $this->
msg(
'pageinfo-view-protect-log' )->text(),
529 [
'type' =>
'protect',
'page' =>
$title->getPrefixedText() ]
538 $pageInfo[
'header-edits'] = [];
540 $firstRev = MediaWikiServices::getInstance()
541 ->getRevisionLookup()
542 ->getFirstRevision( $this->
getTitle() );
543 $lastRev = $this->
getWikiPage()->getRevisionRecord();
547 $firstRevUser = $firstRev->getUser( RevisionRecord::FOR_THIS_USER, $user );
548 if ( $firstRevUser ) {
549 $batch->
add(
NS_USER, $firstRevUser->getName() );
555 $lastRevUser = $lastRev->getUser( RevisionRecord::FOR_THIS_USER, $user );
556 if ( $lastRevUser ) {
557 $batch->add(
NS_USER, $lastRevUser->getName() );
566 $pageInfo[
'header-edits'][] = [
567 $this->
msg(
'pageinfo-firstuser' ),
572 $pageInfo[
'header-edits'][] = [
573 $this->
msg(
'pageinfo-firsttime' ),
574 $linkRenderer->makeKnownLink(
576 $lang->userTimeAndDate( $firstRev->getTimestamp(), $user ),
578 [
'oldid' => $firstRev->getId() ]
585 $pageInfo[
'header-edits'][] = [
586 $this->
msg(
'pageinfo-lastuser' ),
591 $pageInfo[
'header-edits'][] = [
592 $this->
msg(
'pageinfo-lasttime' ),
593 $linkRenderer->makeKnownLink(
595 $lang->userTimeAndDate( $this->getWikiPage()->getTimestamp(), $user ),
597 [
'oldid' => $this->getWikiPage()->getLatest() ]
603 $pageInfo[
'header-edits'][] = [
604 $this->
msg(
'pageinfo-edits' ),
$lang->formatNum( $pageCounts[
'edits'] )
608 if ( $pageCounts[
'authors'] > 0 ) {
609 $pageInfo[
'header-edits'][] = [
610 $this->
msg(
'pageinfo-authors' ),
$lang->formatNum( $pageCounts[
'authors'] )
615 $pageInfo[
'header-edits'][] = [
616 $this->
msg(
'pageinfo-recent-edits',
617 $lang->formatDuration( $config->get(
'RCMaxAge' ) ) ),
618 $lang->formatNum( $pageCounts[
'recent_edits'] )
622 $pageInfo[
'header-edits'][] = [
623 $this->
msg(
'pageinfo-recent-authors' ),
624 $lang->formatNum( $pageCounts[
'recent_authors'] )
628 $magicWords = $services->getMagicWordFactory()->getDoubleUnderscoreArray();
634 $localizedWords = $services->getContentLanguage()->getMagicWords();
637 foreach ( $pageProperties as $property => $value ) {
638 if ( in_array( $property, $wordIDs ) ) {
639 $listItems[] = Html::element(
'li', [], $localizedWords[$property][1] );
643 $localizedList = Html::rawElement(
'ul', [], implode(
'', $listItems ) );
644 $hiddenCategories = $this->
getWikiPage()->getHiddenCategories();
647 count( $listItems ) > 0 ||
648 count( $hiddenCategories ) > 0 ||
649 $pageCounts[
'transclusion'][
'from'] > 0 ||
650 $pageCounts[
'transclusion'][
'to'] > 0
652 $options = [
'LIMIT' => $config->get(
'PageInfoTransclusionLimit' ) ];
653 $transcludedTemplates =
$title->getTemplateLinksFrom( $options );
654 if ( $config->get(
'MiserMode' ) ) {
655 $transcludedTargets = [];
657 $transcludedTargets =
$title->getTemplateLinksTo( $options );
661 $pageInfo[
'header-properties'] = [];
664 if ( count( $listItems ) > 0 ) {
665 $pageInfo[
'header-properties'][] = [
666 $this->
msg(
'pageinfo-magic-words' )->numParams( count( $listItems ) ),
672 if ( count( $hiddenCategories ) > 0 ) {
673 $pageInfo[
'header-properties'][] = [
674 $this->
msg(
'pageinfo-hidden-categories' )
675 ->numParams( count( $hiddenCategories ) ),
681 if ( $pageCounts[
'transclusion'][
'from'] > 0 ) {
682 if ( $pageCounts[
'transclusion'][
'from'] > count( $transcludedTemplates ) ) {
683 $more = $this->
msg(
'morenotlisted' )->escaped();
693 $pageInfo[
'header-properties'][] = [
694 $this->
msg(
'pageinfo-templates' )
695 ->numParams( $pageCounts[
'transclusion'][
'from'] ),
696 $templateListFormatter->format( $transcludedTemplates,
false, $more )
700 if ( !$config->get(
'MiserMode' ) && $pageCounts[
'transclusion'][
'to'] > 0 ) {
701 if ( $pageCounts[
'transclusion'][
'to'] > count( $transcludedTargets ) ) {
702 $more = $linkRenderer->makeLink(
704 $this->
msg(
'moredotdotdot' )->text(),
706 [
'hidelinks' => 1,
'hideredirs' => 1 ]
717 $pageInfo[
'header-properties'][] = [
718 $this->
msg(
'pageinfo-transclusions' )
719 ->numParams( $pageCounts[
'transclusion'][
'to'] ),
720 $templateListFormatter->format( $transcludedTargets,
false, $more )