359 $id =
$title->getArticleID();
360 $config = $this->context->getConfig();
361 $linkRenderer = $this->linkRenderer;
363 $pageCounts = $this->pageCounts();
365 $props = $this->pageProps->getAllProperties(
$title );
366 $pageProperties = $props[$id] ?? [];
370 $pageInfo[
'header-basic'] = [];
373 $displayTitle = $pageProperties[
'displaytitle'] ??
374 htmlspecialchars(
$title->getPrefixedText(), ENT_NOQUOTES );
376 $pageInfo[
'header-basic'][] = [
377 $this->
msg(
'pageinfo-display-title' ),
382 $redirectTarget = $this->redirectLookup->getRedirectTarget( $this->
getWikiPage() );
383 if ( $redirectTarget !==
null ) {
384 $pageInfo[
'header-basic'][] = [
385 $this->
msg(
'pageinfo-redirectsto' ),
386 $linkRenderer->
makeLink( $redirectTarget ) .
387 $this->
msg(
'word-separator' )->escaped() .
388 $this->
msg(
'parentheses' )->rawParams( $linkRenderer->
makeLink(
390 $this->msg(
'pageinfo-redirectsto-info' )->text(),
392 [
'action' =>
'info' ]
398 $sortKey = $pageProperties[
'defaultsort'] ??
$title->getCategorySortkey();
400 $sortKey = htmlspecialchars( $sortKey );
401 $pageInfo[
'header-basic'][] = [ $this->
msg(
'pageinfo-default-sort' ), $sortKey ];
404 $pageInfo[
'header-basic'][] = [
405 $this->
msg(
'pageinfo-length' ),
410 $pageNamespace =
$title->getNsText();
411 if ( $pageNamespace ) {
412 $pageInfo[
'header-basic'][] = [ $this->
msg(
'pageinfo-namespace' ), $pageNamespace ];
416 $pageInfo[
'header-basic'][] = [ $this->
msg(
'pageinfo-article-id' ), $id ];
419 $pageLang =
$title->getPageLanguage()->getCode();
421 $pageLangHtml = $pageLang .
' - ' .
422 $this->languageNameUtils->getLanguageName( $pageLang,
$lang->getCode() );
424 if ( $config->get( MainConfigNames::PageLanguageUseDB )
425 && $this->getAuthority()->probablyCan(
'pagelang',
$title )
427 $pageLangHtml .=
' ' . $this->
msg(
'parentheses' )->rawParams( $linkRenderer->
makeLink(
429 $this->msg(
'pageinfo-language-change' )->text()
433 $pageInfo[
'header-basic'][] = [
434 $this->
msg(
'pageinfo-language' )->escaped(),
439 $modelHtml = htmlspecialchars( ContentHandler::getLocalizedName(
$title->getContentModel() ) );
442 $modelHtml .=
' ' . $this->
msg(
'parentheses' )->rawParams( $linkRenderer->
makeLink(
444 $this->msg(
'pageinfo-content-model-change' )->text()
448 $pageInfo[
'header-basic'][] = [
449 $this->
msg(
'pageinfo-content-model' ),
455 if ( $pageUser && $pageUser->getId() && !$pageUser->isHidden() ) {
456 $pageInfo[
'header-basic'][] = [
457 $this->
msg(
'pageinfo-user-id' ),
465 if ( isset( $pageProperties[
'noindex'] ) ) {
466 $parserOutput->setIndexPolicy(
'noindex' );
468 if ( isset( $pageProperties[
'index'] ) ) {
469 $parserOutput->setIndexPolicy(
'index' );
473 $policy = $this->
getArticle()->getRobotPolicy(
'view', $parserOutput );
474 $pageInfo[
'header-basic'][] = [
476 $this->
msg(
'pageinfo-robot-policy' ),
477 $this->
msg(
"pageinfo-robot-{$policy['index']}" )
480 $unwatchedPageThreshold = $config->get( MainConfigNames::UnwatchedPageThreshold );
481 if ( $this->
getAuthority()->isAllowed(
'unwatchedpages' ) ||
482 ( $unwatchedPageThreshold !==
false &&
483 $pageCounts[
'watchers'] >= $unwatchedPageThreshold )
486 $pageInfo[
'header-basic'][] = [
487 $this->
msg(
'pageinfo-watchers' ),
488 $lang->formatNum( $pageCounts[
'watchers'] )
491 $config->get( MainConfigNames::ShowUpdatedMarker ) &&
492 isset( $pageCounts[
'visitingWatchers'] )
494 $minToDisclose = $config->get( MainConfigNames::UnwatchedPageSecret );
495 if ( $pageCounts[
'visitingWatchers'] > $minToDisclose ||
496 $this->
getAuthority()->isAllowed(
'unwatchedpages' ) ) {
497 $pageInfo[
'header-basic'][] = [
498 $this->
msg(
'pageinfo-visiting-watchers' ),
499 $lang->formatNum( $pageCounts[
'visitingWatchers'] )
502 $pageInfo[
'header-basic'][] = [
503 $this->
msg(
'pageinfo-visiting-watchers' ),
504 $this->
msg(
'pageinfo-few-visiting-watchers' )
508 } elseif ( $unwatchedPageThreshold !==
false ) {
509 $pageInfo[
'header-basic'][] = [
510 $this->
msg(
'pageinfo-watchers' ),
511 $this->
msg(
'pageinfo-few-watchers' )->numParams( $unwatchedPageThreshold )
517 $pageInfo[
'header-basic'][] = [
520 $this->
msg(
'pageinfo-redirects-name' )->text(),
528 $this->
msg(
'pageinfo-redirects-value' )
529 ->numParams( count(
$title->getRedirectsHere() ) )
534 $pageInfo[
'header-basic'][] = [
535 $this->
msg(
'pageinfo-contentpage' ),
536 $this->
msg(
'pageinfo-contentpage-yes' )
541 if ( $this->namespaceInfo->hasSubpages(
$title->getNamespace() ) ) {
544 $title->getPrefixedText() .
'/'
546 $pageInfo[
'header-basic'][] = [
549 $this->
msg(
'pageinfo-subpages-name' )->text()
551 $this->
msg(
'pageinfo-subpages-value' )
553 $pageCounts[
'subpages'][
'total'],
554 $pageCounts[
'subpages'][
'redirects'],
555 $pageCounts[
'subpages'][
'nonredirects']
561 $category = Category::newFromTitle(
$title );
563 $allCount = $category->getMemberCount();
564 $subcatCount = $category->getSubcatCount();
565 $fileCount = $category->getFileCount();
566 $pageCount = $category->getPageCount( Category::COUNT_CONTENT_PAGES );
568 $pageInfo[
'category-info'] = [
570 $this->
msg(
'pageinfo-category-total' ),
571 $lang->formatNum( $allCount )
574 $this->
msg(
'pageinfo-category-pages' ),
575 $lang->formatNum( $pageCount )
578 $this->
msg(
'pageinfo-category-subcats' ),
579 $lang->formatNum( $subcatCount )
582 $this->
msg(
'pageinfo-category-files' ),
583 $lang->formatNum( $fileCount )
590 $fileObj = $this->repoGroup->findFile(
$title );
591 if ( $fileObj !==
false ) {
593 $output = Wikimedia\base_convert( $fileObj->getSha1(), 36, 16, 40 );
594 $pageInfo[
'header-basic'][] = [
595 $this->
msg(
'pageinfo-file-hash' ),
602 $pageInfo[
'header-restrictions'] = [];
605 if ( $this->restrictionStore->isCascadeProtected(
$title ) ) {
607 $sources = $this->restrictionStore->getCascadeProtectionSources(
$title )[0];
609 foreach ( $sources as $sourcePageIdentity ) {
610 $cascadingFrom .= Html::rawElement(
617 $cascadingFrom = Html::rawElement(
'ul', [], $cascadingFrom );
618 $pageInfo[
'header-restrictions'][] = [
619 $this->
msg(
'pageinfo-protect-cascading-from' ),
625 if ( $this->restrictionStore->areRestrictionsCascading(
$title ) ) {
626 $pageInfo[
'header-restrictions'][] = [
627 $this->
msg(
'pageinfo-protect-cascading' ),
628 $this->
msg(
'pageinfo-protect-cascading-yes' )
633 foreach ( $this->restrictionStore->listApplicableRestrictionTypes(
$title ) as $restrictionType ) {
634 $protections = $this->restrictionStore->getRestrictions(
$title, $restrictionType );
636 switch ( count( $protections ) ) {
639 if ( $message ===
null ) {
641 $message = $this->
msg(
'protect-default' )->escaped();
647 $message = $this->
msg(
'protect-level-' . $protections[0] );
648 if ( !$message->isDisabled() ) {
649 $message = $message->escaped();
656 $message = $this->
msg(
"protect-fallback",
$lang->commaList( $protections ) )->parse();
659 $expiry = $this->restrictionStore->getRestrictionExpiry(
$title, $restrictionType );
660 $formattedexpiry = $expiry ===
null ?
'' : $this->
msg(
662 $lang->formatExpiry( $expiry,
true,
'infinity', $user )
664 $message .= $this->
msg(
'word-separator' )->escaped() . $formattedexpiry;
668 $pageInfo[
'header-restrictions'][] = [
669 $this->
msg(
"restriction-$restrictionType" ), $message
673 $pageInfo[
'header-restrictions'][] = [
677 $this->
msg(
'pageinfo-view-protect-log' )->text(),
679 [
'type' =>
'protect',
'page' =>
$title->getPrefixedText() ]
688 $pageInfo[
'header-edits'] = [];
690 $firstRev = $this->revisionLookup->getFirstRevision( $this->
getTitle() );
691 $lastRev = $this->
getWikiPage()->getRevisionRecord();
692 $batch = $this->linkBatchFactory->newLinkBatch();
694 $firstRevUser = $firstRev->getUser( RevisionRecord::FOR_THIS_USER, $user );
695 if ( $firstRevUser ) {
696 $batch->add(
NS_USER, $firstRevUser->getName() );
702 $lastRevUser = $lastRev->getUser( RevisionRecord::FOR_THIS_USER, $user );
703 if ( $lastRevUser ) {
704 $batch->add(
NS_USER, $lastRevUser->getName() );
713 $pageInfo[
'header-edits'][] = [
714 $this->
msg(
'pageinfo-firstuser' ),
719 $pageInfo[
'header-edits'][] = [
720 $this->
msg(
'pageinfo-firsttime' ),
723 $lang->userTimeAndDate( $firstRev->getTimestamp(), $user ),
725 [
'oldid' => $firstRev->getId() ]
732 $pageInfo[
'header-edits'][] = [
733 $this->
msg(
'pageinfo-lastuser' ),
738 $pageInfo[
'header-edits'][] = [
739 $this->
msg(
'pageinfo-lasttime' ),
742 $lang->userTimeAndDate( $this->getWikiPage()->getTimestamp(), $user ),
744 [
'oldid' => $this->getWikiPage()->getLatest() ]
750 $pageInfo[
'header-edits'][] = [
751 $this->
msg(
'pageinfo-edits' ),
752 $lang->formatNum( $pageCounts[
'edits'] )
756 if ( $pageCounts[
'authors'] > 0 ) {
757 $pageInfo[
'header-edits'][] = [
758 $this->
msg(
'pageinfo-authors' ),
759 $lang->formatNum( $pageCounts[
'authors'] )
764 $pageInfo[
'header-edits'][] = [
766 'pageinfo-recent-edits',
767 $lang->formatDuration( $config->get( MainConfigNames::RCMaxAge ) )
769 $lang->formatNum( $pageCounts[
'recent_edits'] )
773 $pageInfo[
'header-edits'][] = [
774 $this->
msg(
'pageinfo-recent-authors' ),
775 $lang->formatNum( $pageCounts[
'recent_authors'] )
779 $magicWords = $this->magicWordFactory->getDoubleUnderscoreArray();
785 $localizedWords = $this->contentLanguage->getMagicWords();
788 foreach ( $pageProperties as $property => $value ) {
789 if ( in_array( $property, $wordIDs ) ) {
790 $listItems[] = Html::element(
'li', [], $localizedWords[$property][1] );
794 $localizedList = Html::rawElement(
'ul', [], implode(
'', $listItems ) );
795 $hiddenCategories = $this->
getWikiPage()->getHiddenCategories();
798 count( $listItems ) > 0 ||
799 count( $hiddenCategories ) > 0 ||
800 $pageCounts[
'transclusion'][
'from'] > 0 ||
801 $pageCounts[
'transclusion'][
'to'] > 0
803 $options = [
'LIMIT' => $config->get( MainConfigNames::PageInfoTransclusionLimit ) ];
804 $transcludedTemplates =
$title->getTemplateLinksFrom( $options );
805 if ( $config->get( MainConfigNames::MiserMode ) ) {
806 $transcludedTargets = [];
808 $transcludedTargets =
$title->getTemplateLinksTo( $options );
812 $pageInfo[
'header-properties'] = [];
815 if ( count( $listItems ) > 0 ) {
816 $pageInfo[
'header-properties'][] = [
817 $this->
msg(
'pageinfo-magic-words' )->numParams( count( $listItems ) ),
823 if ( count( $hiddenCategories ) > 0 ) {
824 $pageInfo[
'header-properties'][] = [
825 $this->
msg(
'pageinfo-hidden-categories' )
826 ->numParams( count( $hiddenCategories ) ),
832 if ( $pageCounts[
'transclusion'][
'from'] > 0 ) {
833 if ( $pageCounts[
'transclusion'][
'from'] > count( $transcludedTemplates ) ) {
834 $more = $this->
msg(
'morenotlisted' )->escaped();
842 $this->linkBatchFactory,
843 $this->restrictionStore
846 $pageInfo[
'header-properties'][] = [
847 $this->
msg(
'pageinfo-templates' )
848 ->numParams( $pageCounts[
'transclusion'][
'from'] ),
849 $templateListFormatter->format( $transcludedTemplates,
false, $more )
853 if ( !$config->get( MainConfigNames::MiserMode ) && $pageCounts[
'transclusion'][
'to'] > 0 ) {
854 if ( $pageCounts[
'transclusion'][
'to'] > count( $transcludedTargets ) ) {
857 $this->
msg(
'moredotdotdot' )->text(),
859 [
'hidelinks' => 1,
'hideredirs' => 1 ]
868 $this->linkBatchFactory,
869 $this->restrictionStore
872 $pageInfo[
'header-properties'][] = [
873 $this->
msg(
'pageinfo-transclusions' )
874 ->numParams( $pageCounts[
'transclusion'][
'to'] ),
875 $templateListFormatter->format( $transcludedTargets,
false, $more )