/** * Returns page information in an easily-manipulated format. Array keys are used so extensions * may add additional information in arbitrary positions. Array values are arrays with one * element to be rendered as a header, arrays with two elements to be rendered as a table row. * * @return array */ protected function pageInfo() { global $wgContLang; $user = $this->getUser(); $lang = $this->getLanguage(); $title = $this->getTitle(); $id = $title->getArticleID(); $config = $this->context->getConfig(); $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); $pageCounts = $this->pageCounts($this->page); $pageProperties = []; $props = PageProps::getInstance()->getAllProperties($title); if (isset($props[$id])) { $pageProperties = $props[$id]; } // Basic information $pageInfo = []; $pageInfo['header-basic'] = []; // Display title $displayTitle = $title->getPrefixedText(); if (isset($pageProperties['displaytitle'])) { $displayTitle = $pageProperties['displaytitle']; } $pageInfo['header-basic'][] = [$this->msg('pageinfo-display-title'), $displayTitle]; // Is it a redirect? If so, where to? if ($title->isRedirect()) { $pageInfo['header-basic'][] = [$this->msg('pageinfo-redirectsto'), Linker::link($this->page->getRedirectTarget()) . $this->msg('word-separator')->escaped() . $this->msg('parentheses')->rawParams(Linker::link($this->page->getRedirectTarget(), $this->msg('pageinfo-redirectsto-info')->escaped(), [], ['action' => 'info']))->escaped()]; } // Default sort key $sortKey = $title->getCategorySortkey(); if (isset($pageProperties['defaultsort'])) { $sortKey = $pageProperties['defaultsort']; } $sortKey = htmlspecialchars($sortKey); $pageInfo['header-basic'][] = [$this->msg('pageinfo-default-sort'), $sortKey]; // Page length (in bytes) $pageInfo['header-basic'][] = [$this->msg('pageinfo-length'), $lang->formatNum($title->getLength())]; // Page ID (number not localised, as it's a database ID) $pageInfo['header-basic'][] = [$this->msg('pageinfo-article-id'), $id]; // Language in which the page content is (supposed to be) written $pageLang = $title->getPageLanguage()->getCode(); if ($config->get('PageLanguageUseDB') && $this->getTitle()->userCan('pagelang', $this->getUser())) { // Link to Special:PageLanguage with pre-filled page title if user has permissions $titleObj = SpecialPage::getTitleFor('PageLanguage', $title->getPrefixedText()); $langDisp = Linker::link($titleObj, $this->msg('pageinfo-language')->escaped()); } else { // Display just the message $langDisp = $this->msg('pageinfo-language')->escaped(); } $pageInfo['header-basic'][] = [$langDisp, Language::fetchLanguageName($pageLang, $lang->getCode()) . ' ' . $this->msg('parentheses', $pageLang)->escaped()]; // Content model of the page $modelHtml = htmlspecialchars(ContentHandler::getLocalizedName($title->getContentModel())); // If the user can change it, add a link to Special:ChangeContentModel if ($title->quickUserCan('editcontentmodel')) { $modelHtml .= ' ' . $this->msg('parentheses')->rawParams($linkRenderer->makeLink(SpecialPage::getTitleValueFor('ChangeContentModel', $title->getPrefixedText()), $this->msg('pageinfo-content-model-change')->text()))->escaped(); } $pageInfo['header-basic'][] = [$this->msg('pageinfo-content-model'), $modelHtml]; if ($title->inNamespace(NS_USER)) { $pageUser = User::newFromName($title->getRootText()); if ($pageUser && $pageUser->getId() && !$pageUser->isHidden()) { $pageInfo['header-basic'][] = [$this->msg('pageinfo-user-id'), $pageUser->getId()]; } } // Search engine status $pOutput = new ParserOutput(); if (isset($pageProperties['noindex'])) { $pOutput->setIndexPolicy('noindex'); } if (isset($pageProperties['index'])) { $pOutput->setIndexPolicy('index'); } // Use robot policy logic $policy = $this->page->getRobotPolicy('view', $pOutput); $pageInfo['header-basic'][] = [$this->msg('pageinfo-robot-policy'), $this->msg("pageinfo-robot-{$policy['index']}")]; $unwatchedPageThreshold = $config->get('UnwatchedPageThreshold'); if ($user->isAllowed('unwatchedpages') || $unwatchedPageThreshold !== false && $pageCounts['watchers'] >= $unwatchedPageThreshold) { // Number of page watchers $pageInfo['header-basic'][] = [$this->msg('pageinfo-watchers'), $lang->formatNum($pageCounts['watchers'])]; if ($config->get('ShowUpdatedMarker') && isset($pageCounts['visitingWatchers'])) { $minToDisclose = $config->get('UnwatchedPageSecret'); if ($pageCounts['visitingWatchers'] > $minToDisclose || $user->isAllowed('unwatchedpages')) { $pageInfo['header-basic'][] = [$this->msg('pageinfo-visiting-watchers'), $lang->formatNum($pageCounts['visitingWatchers'])]; } else { $pageInfo['header-basic'][] = [$this->msg('pageinfo-visiting-watchers'), $this->msg('pageinfo-few-visiting-watchers')]; } } } elseif ($unwatchedPageThreshold !== false) { $pageInfo['header-basic'][] = [$this->msg('pageinfo-watchers'), $this->msg('pageinfo-few-watchers')->numParams($unwatchedPageThreshold)]; } // Redirects to this page $whatLinksHere = SpecialPage::getTitleFor('Whatlinkshere', $title->getPrefixedText()); $pageInfo['header-basic'][] = [Linker::link($whatLinksHere, $this->msg('pageinfo-redirects-name')->escaped(), [], ['hidelinks' => 1, 'hidetrans' => 1, 'hideimages' => $title->getNamespace() == NS_FILE]), $this->msg('pageinfo-redirects-value')->numParams(count($title->getRedirectsHere()))]; // Is it counted as a content page? if ($this->page->isCountable()) { $pageInfo['header-basic'][] = [$this->msg('pageinfo-contentpage'), $this->msg('pageinfo-contentpage-yes')]; } // Subpages of this page, if subpages are enabled for the current NS if (MWNamespace::hasSubpages($title->getNamespace())) { $prefixIndex = SpecialPage::getTitleFor('Prefixindex', $title->getPrefixedText() . '/'); $pageInfo['header-basic'][] = [Linker::link($prefixIndex, $this->msg('pageinfo-subpages-name')->escaped()), $this->msg('pageinfo-subpages-value')->numParams($pageCounts['subpages']['total'], $pageCounts['subpages']['redirects'], $pageCounts['subpages']['nonredirects'])]; } if ($title->inNamespace(NS_CATEGORY)) { $category = Category::newFromTitle($title); // $allCount is the total number of cat members, // not the count of how many members are normal pages. $allCount = (int) $category->getPageCount(); $subcatCount = (int) $category->getSubcatCount(); $fileCount = (int) $category->getFileCount(); $pagesCount = $allCount - $subcatCount - $fileCount; $pageInfo['category-info'] = [[$this->msg('pageinfo-category-total'), $lang->formatNum($allCount)], [$this->msg('pageinfo-category-pages'), $lang->formatNum($pagesCount)], [$this->msg('pageinfo-category-subcats'), $lang->formatNum($subcatCount)], [$this->msg('pageinfo-category-files'), $lang->formatNum($fileCount)]]; } // Page protection $pageInfo['header-restrictions'] = []; // Is this page affected by the cascading protection of something which includes it? if ($title->isCascadeProtected()) { $cascadingFrom = ''; $sources = $title->getCascadeProtectionSources()[0]; foreach ($sources as $sourceTitle) { $cascadingFrom .= Html::rawElement('li', [], Linker::linkKnown($sourceTitle)); } $cascadingFrom = Html::rawElement('ul', [], $cascadingFrom); $pageInfo['header-restrictions'][] = [$this->msg('pageinfo-protect-cascading-from'), $cascadingFrom]; } // Is out protection set to cascade to other pages? if ($title->areRestrictionsCascading()) { $pageInfo['header-restrictions'][] = [$this->msg('pageinfo-protect-cascading'), $this->msg('pageinfo-protect-cascading-yes')]; } // Page protection foreach ($title->getRestrictionTypes() as $restrictionType) { $protectionLevel = implode(', ', $title->getRestrictions($restrictionType)); if ($protectionLevel == '') { // Allow all users $message = $this->msg('protect-default')->escaped(); } else { // Administrators only // Messages: protect-level-autoconfirmed, protect-level-sysop $message = $this->msg("protect-level-{$protectionLevel}"); if ($message->isDisabled()) { // Require "$1" permission $message = $this->msg("protect-fallback", $protectionLevel)->parse(); } else { $message = $message->escaped(); } } $expiry = $title->getRestrictionExpiry($restrictionType); $formattedexpiry = $this->msg('parentheses', $this->getLanguage()->formatExpiry($expiry))->escaped(); $message .= $this->msg('word-separator')->escaped() . $formattedexpiry; // Messages: restriction-edit, restriction-move, restriction-create, // restriction-upload $pageInfo['header-restrictions'][] = [$this->msg("restriction-{$restrictionType}"), $message]; } if (!$this->page->exists()) { return $pageInfo; } // Edit history $pageInfo['header-edits'] = []; $firstRev = $this->page->getOldestRevision(); $lastRev = $this->page->getRevision(); $batch = new LinkBatch(); if ($firstRev) { $firstRevUser = $firstRev->getUserText(Revision::FOR_THIS_USER); if ($firstRevUser !== '') { $firstRevUserTitle = Title::makeTitle(NS_USER, $firstRevUser); $batch->addObj($firstRevUserTitle); $batch->addObj($firstRevUserTitle->getTalkPage()); } } if ($lastRev) { $lastRevUser = $lastRev->getUserText(Revision::FOR_THIS_USER); if ($lastRevUser !== '') { $lastRevUserTitle = Title::makeTitle(NS_USER, $lastRevUser); $batch->addObj($lastRevUserTitle); $batch->addObj($lastRevUserTitle->getTalkPage()); } } $batch->execute(); if ($firstRev) { // Page creator $pageInfo['header-edits'][] = [$this->msg('pageinfo-firstuser'), Linker::revUserTools($firstRev)]; // Date of page creation $pageInfo['header-edits'][] = [$this->msg('pageinfo-firsttime'), Linker::linkKnown($title, htmlspecialchars($lang->userTimeAndDate($firstRev->getTimestamp(), $user)), [], ['oldid' => $firstRev->getId()])]; } if ($lastRev) { // Latest editor $pageInfo['header-edits'][] = [$this->msg('pageinfo-lastuser'), Linker::revUserTools($lastRev)]; // Date of latest edit $pageInfo['header-edits'][] = [$this->msg('pageinfo-lasttime'), Linker::linkKnown($title, htmlspecialchars($lang->userTimeAndDate($this->page->getTimestamp(), $user)), [], ['oldid' => $this->page->getLatest()])]; } // Total number of edits $pageInfo['header-edits'][] = [$this->msg('pageinfo-edits'), $lang->formatNum($pageCounts['edits'])]; // Total number of distinct authors if ($pageCounts['authors'] > 0) { $pageInfo['header-edits'][] = [$this->msg('pageinfo-authors'), $lang->formatNum($pageCounts['authors'])]; } // Recent number of edits (within past 30 days) $pageInfo['header-edits'][] = [$this->msg('pageinfo-recent-edits', $lang->formatDuration($config->get('RCMaxAge'))), $lang->formatNum($pageCounts['recent_edits'])]; // Recent number of distinct authors $pageInfo['header-edits'][] = [$this->msg('pageinfo-recent-authors'), $lang->formatNum($pageCounts['recent_authors'])]; // Array of MagicWord objects $magicWords = MagicWord::getDoubleUnderscoreArray(); // Array of magic word IDs $wordIDs = $magicWords->names; // Array of IDs => localized magic words $localizedWords = $wgContLang->getMagicWords(); $listItems = []; foreach ($pageProperties as $property => $value) { if (in_array($property, $wordIDs)) { $listItems[] = Html::element('li', [], $localizedWords[$property][1]); } } $localizedList = Html::rawElement('ul', [], implode('', $listItems)); $hiddenCategories = $this->page->getHiddenCategories(); if (count($listItems) > 0 || count($hiddenCategories) > 0 || $pageCounts['transclusion']['from'] > 0 || $pageCounts['transclusion']['to'] > 0) { $options = ['LIMIT' => $config->get('PageInfoTransclusionLimit')]; $transcludedTemplates = $title->getTemplateLinksFrom($options); if ($config->get('MiserMode')) { $transcludedTargets = []; } else { $transcludedTargets = $title->getTemplateLinksTo($options); } // Page properties $pageInfo['header-properties'] = []; // Magic words if (count($listItems) > 0) { $pageInfo['header-properties'][] = [$this->msg('pageinfo-magic-words')->numParams(count($listItems)), $localizedList]; } // Hidden categories if (count($hiddenCategories) > 0) { $pageInfo['header-properties'][] = [$this->msg('pageinfo-hidden-categories')->numParams(count($hiddenCategories)), Linker::formatHiddenCategories($hiddenCategories)]; } // Transcluded templates if ($pageCounts['transclusion']['from'] > 0) { if ($pageCounts['transclusion']['from'] > count($transcludedTemplates)) { $more = $this->msg('morenotlisted')->escaped(); } else { $more = null; } $templateListFormatter = new TemplatesOnThisPageFormatter($this->getContext(), $linkRenderer); $pageInfo['header-properties'][] = [$this->msg('pageinfo-templates')->numParams($pageCounts['transclusion']['from']), $templateListFormatter->format($transcludedTemplates, false, $more)]; } if (!$config->get('MiserMode') && $pageCounts['transclusion']['to'] > 0) { if ($pageCounts['transclusion']['to'] > count($transcludedTargets)) { $more = Linker::link($whatLinksHere, $this->msg('moredotdotdot')->escaped(), [], ['hidelinks' => 1, 'hideredirs' => 1]); } else { $more = null; } $templateListFormatter = new TemplatesOnThisPageFormatter($this->getContext(), $linkRenderer); $pageInfo['header-properties'][] = [$this->msg('pageinfo-transclusions')->numParams($pageCounts['transclusion']['to']), $templateListFormatter->format($transcludedTargets, false, $more)]; } } return $pageInfo; }
/** * Wrapper around TemplatesOnThisPageFormatter to make * a "templates on this page" list. * * @param Title[] $templates * @return string HTML */ protected function makeTemplatesOnThisPageList(array $templates) { $templateListFormatter = new TemplatesOnThisPageFormatter($this->context, MediaWikiServices::getInstance()->getLinkRenderer()); // preview if preview, else section if section, else false $type = false; if ($this->preview) { $type = 'preview'; } elseif ($this->section != '') { $type = 'section'; } return Html::rawElement('div', ['class' => 'templatesUsed'], $templateListFormatter->format($templates, $type)); }
/** * @deprecated since 1.28, use TemplatesOnThisPageFormatter directly * * Returns HTML for the "templates used on this page" list. * * Make an HTML list of templates, and then add a "More..." link at * the bottom. If $more is null, do not add a "More..." link. If $more * is a Title, make a link to that title and use it. If $more is a string, * directly paste it in as the link (escaping needs to be done manually). * Finally, if $more is a Message, call toString(). * * @since 1.16.3. $more added in 1.21 * @param Title[] $templates Array of templates * @param bool $preview Whether this is for a preview * @param bool $section Whether this is for a section edit * @param Title|Message|string|null $more An escaped link for "More..." of the templates * @return string HTML output */ public static function formatTemplates($templates, $preview = false, $section = false, $more = null) { wfDeprecated(__METHOD__, '1.28'); $type = false; if ($preview) { $type = 'preview'; } elseif ($section) { $type = 'section'; } if ($more instanceof Message) { $more = $more->toString(); } $formatter = new TemplatesOnThisPageFormatter(RequestContext::getMain(), MediaWikiServices::getInstance()->getLinkRenderer()); return $formatter->format($templates, $type, $more); }