/** * Display a page stating that the Wiki is in read-only mode, * and optionally show the source of the page that the user * was trying to edit. Should only be called (for this * purpose) after wfReadOnly() has returned true. * * For historical reasons, this function is _also_ used to * show the error message when a user tries to edit a page * they are not allowed to edit. (Unless it's because they're * blocked, then we show blockedPage() instead.) In this * case, the second parameter should be set to true and a list * of reasons supplied as the third parameter. * * @todo Needs to be split into multiple functions. * * @param string $source Source code to show (or null). * @param bool $protected Is this a permissions error? * @param array $reasons List of reasons for this error, as returned by Title::getUserPermissionsErrors(). * @param string $action Action that was denied or null if unknown * @throws ReadOnlyError */ public function readOnlyPage($source = null, $protected = false, $reasons = array(), $action = null) { $this->setRobotPolicy('noindex,nofollow'); $this->setArticleRelated(false); // If no reason is given, just supply a default "I can't let you do // that, Dave" message. Should only occur if called by legacy code. if ($protected && empty($reasons)) { $reasons[] = array('badaccess-group0'); } if (!empty($reasons)) { // Permissions error if ($source) { $this->setPageTitle($this->msg('viewsource-title', $this->getTitle()->getPrefixedText())); $this->addBacklinkSubtitle($this->getTitle()); } else { $this->setPageTitle($this->msg('badaccess')); } $this->addWikiText($this->formatPermissionsErrorMessage($reasons, $action)); } else { // Wiki is read only throw new ReadOnlyError(); } // Show source, if supplied if (is_string($source)) { $this->addWikiMsg('viewsourcetext'); $pageLang = $this->getTitle()->getPageLanguage(); $params = array('id' => 'wpTextbox1', 'name' => 'wpTextbox1', 'cols' => $this->getUser()->getOption('cols'), 'rows' => $this->getUser()->getOption('rows'), 'readonly' => 'readonly', 'lang' => $pageLang->getHtmlCode(), 'dir' => $pageLang->getDir()); $this->addHTML(Html::element('textarea', $params, $source)); // Show templates used by this article $templates = Linker::formatTemplates($this->getTitle()->getTemplateLinksFrom()); $this->addHTML("<div class='templatesUsed'>\n{$templates}\n</div>\n"); } # If the title doesn't exist, it's fairly pointless to print a return # link to it. After all, you just tried editing it and couldn't, so # what's there to do there? if ($this->getTitle()->exists()) { $this->returnToMain(null, $this->getTitle()); } }
/** * Send the edit form and related headers to $wgOut * @param $formCallback Callback that takes an OutputPage parameter; will be called * during form output near the top, for captchas and the like. */ function showEditForm($formCallback = null) { global $wgOut, $wgUser; wfProfileIn(__METHOD__); #need to parse the preview early so that we know which templates are used, #otherwise users with "show preview after edit box" will get a blank list #we parse this near the beginning so that setHeaders can do the title #setting work instead of leaving it in getPreviewText $previewOutput = ''; if ($this->formtype == 'preview') { $previewOutput = $this->getPreviewText(); } wfRunHooks('EditPage::showEditForm:initial', array(&$this)); $this->setHeaders(); if ($this->showHeader() === false) { wfProfileOut(__METHOD__); return; } $wgOut->addHTML($this->editFormPageTop); if ($wgUser->getOption('previewontop')) { $this->displayPreviewArea($previewOutput, true); } $wgOut->addHTML($this->editFormTextTop); $showToolbar = true; if ($this->wasDeletedSinceLastEdit()) { if ($this->formtype == 'save') { // Hide the toolbar and edit area, user can click preview to get it back // Add an confirmation checkbox and explanation. $showToolbar = false; } else { $wgOut->wrapWikiMsg("<div class='error mw-deleted-while-editing'>\n\$1\n</div>", 'deletedwhileediting'); } } $wgOut->addHTML(Html::openElement('form', array('id' => 'editform', 'name' => 'editform', 'method' => 'post', 'action' => $this->getActionURL($this->getContextTitle()), 'enctype' => 'multipart/form-data'))); if (is_callable($formCallback)) { call_user_func_array($formCallback, array(&$wgOut)); } wfRunHooks('EditPage::showEditForm:fields', array(&$this, &$wgOut)); // Put these up at the top to ensure they aren't lost on early form submission $this->showFormBeforeText(); if ($this->wasDeletedSinceLastEdit() && 'save' == $this->formtype) { $username = $this->lastDelete->user_name; $comment = $this->lastDelete->log_comment; // It is better to not parse the comment at all than to have templates expanded in the middle // TODO: can the checkLabel be moved outside of the div so that wrapWikiMsg could be used? $key = $comment === '' ? 'confirmrecreate-noreason' : 'confirmrecreate'; $wgOut->addHTML('<div class="mw-confirm-recreate">' . wfMsgExt($key, 'parseinline', $username, "<nowiki>{$comment}</nowiki>") . Xml::checkLabel(wfMsg('recreate'), 'wpRecreate', 'wpRecreate', false, array('title' => Linker::titleAttrib('recreate'), 'tabindex' => 1, 'id' => 'wpRecreate')) . '</div>'); } # If a blank edit summary was previously provided, and the appropriate # user preference is active, pass a hidden tag as wpIgnoreBlankSummary. This will stop the # user being bounced back more than once in the event that a summary # is not required. ##### # For a bit more sophisticated detection of blank summaries, hash the # automatic one and pass that in the hidden field wpAutoSummary. if ($this->missingSummary || $this->section == 'new' && $this->nosummary) { $wgOut->addHTML(Html::hidden('wpIgnoreBlankSummary', true)); } $autosumm = $this->autoSumm ? $this->autoSumm : md5($this->summary); $wgOut->addHTML(Html::hidden('wpAutoSummary', $autosumm)); $wgOut->addHTML(Html::hidden('oldid', $this->oldid)); if ($this->section == 'new') { $this->showSummaryInput(true, $this->summary); $wgOut->addHTML($this->getSummaryPreview(true, $this->summary)); } $wgOut->addHTML($this->editFormTextBeforeContent); if (!$this->isCssJsSubpage && $showToolbar && $wgUser->getOption('showtoolbar')) { $wgOut->addHTML(EditPage::getEditToolbar()); } if ($this->isConflict) { // In an edit conflict bypass the overrideable content form method // and fallback to the raw wpTextbox1 since editconflicts can't be // resolved between page source edits and custom ui edits using the // custom edit ui. $this->textbox2 = $this->textbox1; $this->textbox1 = $this->getCurrentText(); $this->showTextbox1(); } else { $this->showContentForm(); } $wgOut->addHTML($this->editFormTextAfterContent); $wgOut->addWikiText($this->getCopywarn()); $wgOut->addHTML($this->editFormTextAfterWarn); $this->showStandardInputs(); $this->showFormAfterText(); $this->showTosSummary(); $this->showEditTools(); $wgOut->addHTML($this->editFormTextAfterTools . "\n"); $wgOut->addHTML(Html::rawElement('div', array('class' => 'templatesUsed'), Linker::formatTemplates($this->getTemplates(), $this->preview, $this->section != ''))); $wgOut->addHTML(Html::rawElement('div', array('class' => 'hiddencats'), Linker::formatHiddenCategories($this->mArticle->getHiddenCategories()))); if ($this->isConflict) { $this->showConflict(); } $wgOut->addHTML($this->editFormTextBottom . "\n</form>\n"); if (!$wgUser->getOption('previewontop')) { $this->displayPreviewArea($previewOutput, false); } wfProfileOut(__METHOD__); }
public function formatTemplates($templates, $preview = false, $section = false, $more = null) { return Linker::formatTemplates($templates, $preview, $section, $more); }
/** * Shows page information on GET request. * * @return string Page information that will be added to the output */ public function onView() { global $wgContLang, $wgDisableCounters, $wgRCMaxAge, $wgRestrictionTypes; $user = $this->getUser(); $lang = $this->getLanguage(); $title = $this->getTitle(); $id = $title->getArticleID(); // Get page information that would be too "expensive" to retrieve by normal means $userCanViewUnwatchedPages = $user->isAllowed('unwatchedpages'); $pageInfo = self::pageCountInfo($title, $userCanViewUnwatchedPages, $wgDisableCounters); // Get page properties $dbr = wfGetDB(DB_SLAVE); $result = $dbr->select('page_props', array('pp_propname', 'pp_value'), array('pp_page' => $id), __METHOD__); $pageProperties = array(); foreach ($result as $row) { $pageProperties[$row->pp_propname] = $row->pp_value; } $content = ''; $table = ''; // Header if (!$this->msg('pageinfo-header')->isDisabled()) { $content .= $this->msg('pageinfo-header ')->parse(); } // Basic information $content = $this->addHeader($content, $this->msg('pageinfo-header-basic')->text()); // Display title $displayTitle = $title->getPrefixedText(); if (!empty($pageProperties['displaytitle'])) { $displayTitle = $pageProperties['displaytitle']; } $table = $this->addRow($table, $this->msg('pageinfo-display-title')->escaped(), $displayTitle); // Default sort key $sortKey = $title->getCategorySortKey(); if (!empty($pageProperties['defaultsort'])) { $sortKey = $pageProperties['defaultsort']; } $table = $this->addRow($table, $this->msg('pageinfo-default-sort')->escaped(), $sortKey); // Page length (in bytes) $table = $this->addRow($table, $this->msg('pageinfo-length')->escaped(), $lang->formatNum($title->getLength())); // Page ID (number not localised, as it's a database ID.) $table = $this->addRow($table, $this->msg('pageinfo-article-id')->escaped(), $id); // Search engine status $pOutput = new ParserOutput(); if (isset($pageProperties['noindex'])) { $pOutput->setIndexPolicy('noindex'); } // Use robot policy logic $policy = $this->page->getRobotPolicy('view', $pOutput); $table = $this->addRow($table, $this->msg('pageinfo-robot-policy')->escaped(), $this->msg("pageinfo-robot-{$policy['index']}")->escaped()); if (!$wgDisableCounters) { // Number of views $table = $this->addRow($table, $this->msg('pageinfo-views')->escaped(), $lang->formatNum($pageInfo['views'])); } if ($userCanViewUnwatchedPages) { // Number of page watchers $table = $this->addRow($table, $this->msg('pageinfo-watchers')->escaped(), $lang->formatNum($pageInfo['watchers'])); } // Redirects to this page $whatLinksHere = SpecialPage::getTitleFor('Whatlinkshere', $title->getPrefixedText()); $table = $this->addRow($table, Linker::link($whatLinksHere, $this->msg('pageinfo-redirects-name')->escaped(), array(), array('hidelinks' => 1, 'hidetrans' => 1)), $this->msg('pageinfo-redirects-value')->numParams(count($title->getRedirectsHere()))->escaped()); // Subpages of this page, if subpages are enabled for the current NS if (MWNamespace::hasSubpages($title->getNamespace())) { $prefixIndex = SpecialPage::getTitleFor('Prefixindex', $title->getPrefixedText() . '/'); $table = $this->addRow($table, Linker::link($prefixIndex, $this->msg('pageinfo-subpages-name')->escaped()), $this->msg('pageinfo-subpages-value')->numParams($pageInfo['subpages']['total'], $pageInfo['subpages']['redirects'], $pageInfo['subpages']['nonredirects'])->escaped()); } // Page protection $content = $this->addTable($content, $table); $content = $this->addHeader($content, $this->msg('pageinfo-header-restrictions')->text()); $table = ''; // Page protection foreach ($wgRestrictionTypes as $restrictionType) { $protectionLevel = implode(', ', $title->getRestrictions($restrictionType)); if ($protectionLevel == '') { // Allow all users $message = $this->msg('protect-default')->escaped(); } else { // Administrators only $message = $this->msg("protect-level-{$protectionLevel}"); if ($message->isDisabled()) { // Require "$1" permission $message = $this->msg("protect-fallback", $protectionLevel)->parse(); } else { $message = $message->escaped(); } } $table = $this->addRow($table, $this->msg('pageinfo-restriction', $this->msg("restriction-{$restrictionType}")->plain())->parse(), $message); } // Edit history $content = $this->addTable($content, $table); $content = $this->addHeader($content, $this->msg('pageinfo-header-edits')->text()); $table = ''; $firstRev = $this->page->getOldestRevision(); // Page creator $table = $this->addRow($table, $this->msg('pageinfo-firstuser')->escaped(), $firstRev->getUserText(Revision::FOR_THIS_USER, $user)); // Date of page creation $table = $this->addRow($table, $this->msg('pageinfo-firsttime')->escaped(), Linker::linkKnown($title, $lang->userTimeAndDate($firstRev->getTimestamp(), $user), array(), array('oldid' => $firstRev->getId()))); // Latest editor $table = $this->addRow($table, $this->msg('pageinfo-lastuser')->escaped(), $this->page->getUserText(Revision::FOR_THIS_USER, $user)); // Date of latest edit $table = $this->addRow($table, $this->msg('pageinfo-lasttime')->escaped(), Linker::linkKnown($title, $lang->userTimeAndDate($this->page->getTimestamp(), $user), array(), array('oldid' => $this->page->getLatest()))); // Total number of edits $table = $this->addRow($table, $this->msg('pageinfo-edits')->escaped(), $lang->formatNum($pageInfo['edits'])); // Total number of distinct authors $table = $this->addRow($table, $this->msg('pageinfo-authors')->escaped(), $lang->formatNum($pageInfo['authors'])); // Recent number of edits (within past 30 days) $table = $this->addRow($table, $this->msg('pageinfo-recent-edits', $lang->formatDuration($wgRCMaxAge))->escaped(), $lang->formatNum($pageInfo['recent_edits'])); // Recent number of distinct authors $table = $this->addRow($table, $this->msg('pageinfo-recent-authors')->escaped(), $lang->formatNum($pageInfo['recent_authors'])); $content = $this->addTable($content, $table); // 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 = array(); foreach ($pageProperties as $property => $value) { if (in_array($property, $wordIDs)) { $listItems[] = Html::element('li', array(), $localizedWords[$property][1]); } } $localizedList = Html::rawElement('ul', array(), implode('', $listItems)); $hiddenCategories = $this->page->getHiddenCategories(); $transcludedTemplates = $title->getTemplateLinksFrom(); if (count($listItems) > 0 || count($hiddenCategories) > 0 || count($transcludedTemplates) > 0) { // Page properties $content = $this->addHeader($content, $this->msg('pageinfo-header-properties')->text()); $table = ''; // Magic words if (count($listItems) > 0) { $table = $this->addRow($table, $this->msg('pageinfo-magic-words')->numParams(count($listItems))->escaped(), $localizedList); } // Hide "This page is a member of # hidden categories explanation $content .= Html::element('style', array(), '.mw-hiddenCategoriesExplanation { display: none; }'); // Hidden categories if (count($hiddenCategories) > 0) { $table = $this->addRow($table, $this->msg('pageinfo-hidden-categories')->numParams(count($hiddenCategories))->escaped(), Linker::formatHiddenCategories($hiddenCategories)); } // Hide "Templates used on this page:" explanation $content .= Html::element('style', array(), '.mw-templatesUsedExplanation { display: none; }'); // Transcluded templates if (count($transcludedTemplates) > 0) { $table = $this->addRow($table, $this->msg('pageinfo-templates')->numParams(count($transcludedTemplates))->escaped(), Linker::formatTemplates($transcludedTemplates)); } $content = $this->addTable($content, $table); } // Footer if (!$this->msg('pageinfo-footer')->isDisabled()) { $content .= $this->msg('pageinfo-footer')->parse(); } return $content; }
/** * 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(); $cache = ObjectCache::getMainWANInstance(); $memcKey = wfMemcKey('infoaction', sha1($title->getPrefixedText()), $this->page->getLatest()); $pageCounts = $cache->get($memcKey); $version = isset($pageCounts['cacheversion']) ? $pageCounts['cacheversion'] : false; if ($pageCounts === false || $version !== self::CACHE_VERSION) { // Get page information that would be too "expensive" to retrieve by normal means $pageCounts = $this->pageCounts($title); $pageCounts['cacheversion'] = self::CACHE_VERSION; $cache->set($memcKey, $pageCounts); } // Get page properties $dbr = wfGetDB(DB_SLAVE); $result = $dbr->select('page_props', array('pp_propname', 'pp_value'), array('pp_page' => $id), __METHOD__); $pageProperties = array(); foreach ($result as $row) { $pageProperties[$row->pp_propname] = $row->pp_value; } // Basic information $pageInfo = array(); $pageInfo['header-basic'] = array(); // Display title $displayTitle = $title->getPrefixedText(); if (isset($pageProperties['displaytitle'])) { $displayTitle = $pageProperties['displaytitle']; } $pageInfo['header-basic'][] = array($this->msg('pageinfo-display-title'), $displayTitle); // Is it a redirect? If so, where to? if ($title->isRedirect()) { $pageInfo['header-basic'][] = array($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(), array(), array('action' => 'info')))->escaped()); } // Default sort key $sortKey = $title->getCategorySortkey(); if (isset($pageProperties['defaultsort'])) { $sortKey = $pageProperties['defaultsort']; } $sortKey = htmlspecialchars($sortKey); $pageInfo['header-basic'][] = array($this->msg('pageinfo-default-sort'), $sortKey); // Page length (in bytes) $pageInfo['header-basic'][] = array($this->msg('pageinfo-length'), $lang->formatNum($title->getLength())); // Page ID (number not localised, as it's a database ID) $pageInfo['header-basic'][] = array($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'][] = array($langDisp, Language::fetchLanguageName($pageLang, $lang->getCode()) . ' ' . $this->msg('parentheses', $pageLang)->escaped()); // Content model of the page $pageInfo['header-basic'][] = array($this->msg('pageinfo-content-model'), htmlspecialchars(ContentHandler::getLocalizedName($title->getContentModel()))); // 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'][] = array($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'][] = array($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'][] = array($this->msg('pageinfo-visiting-watchers'), $lang->formatNum($pageCounts['visitingWatchers'])); } else { $pageInfo['header-basic'][] = array($this->msg('pageinfo-visiting-watchers'), $this->msg('pageinfo-few-visiting-watchers')); } } } elseif ($unwatchedPageThreshold !== false) { $pageInfo['header-basic'][] = array($this->msg('pageinfo-watchers'), $this->msg('pageinfo-few-watchers')->numParams($unwatchedPageThreshold)); } // Redirects to this page $whatLinksHere = SpecialPage::getTitleFor('Whatlinkshere', $title->getPrefixedText()); $pageInfo['header-basic'][] = array(Linker::link($whatLinksHere, $this->msg('pageinfo-redirects-name')->escaped(), array(), array('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'][] = array($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'][] = array(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'] = array(array($this->msg('pageinfo-category-total'), $lang->formatNum($allCount)), array($this->msg('pageinfo-category-pages'), $lang->formatNum($pagesCount)), array($this->msg('pageinfo-category-subcats'), $lang->formatNum($subcatCount)), array($this->msg('pageinfo-category-files'), $lang->formatNum($fileCount))); } // Page protection $pageInfo['header-restrictions'] = array(); // Is this page affected by the cascading protection of something which includes it? if ($title->isCascadeProtected()) { $cascadingFrom = ''; $sources = $title->getCascadeProtectionSources(); // Array deferencing is in PHP 5.4 :( foreach ($sources[0] as $sourceTitle) { $cascadingFrom .= Html::rawElement('li', array(), Linker::linkKnown($sourceTitle)); } $cascadingFrom = Html::rawElement('ul', array(), $cascadingFrom); $pageInfo['header-restrictions'][] = array($this->msg('pageinfo-protect-cascading-from'), $cascadingFrom); } // Is out protection set to cascade to other pages? if ($title->areRestrictionsCascading()) { $pageInfo['header-restrictions'][] = array($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'][] = array($this->msg("restriction-{$restrictionType}"), $message); } if (!$this->page->exists()) { return $pageInfo; } // Edit history $pageInfo['header-edits'] = array(); $firstRev = $this->page->getOldestRevision(); $lastRev = $this->page->getRevision(); $batch = new LinkBatch(); if ($firstRev) { $firstRevUser = $firstRev->getUserText(Revision::FOR_THIS_USER); if ($firstRevUser !== '') { $batch->add(NS_USER, $firstRevUser); $batch->add(NS_USER_TALK, $firstRevUser); } } if ($lastRev) { $lastRevUser = $lastRev->getUserText(Revision::FOR_THIS_USER); if ($lastRevUser !== '') { $batch->add(NS_USER, $lastRevUser); $batch->add(NS_USER_TALK, $lastRevUser); } } $batch->execute(); if ($firstRev) { // Page creator $pageInfo['header-edits'][] = array($this->msg('pageinfo-firstuser'), Linker::revUserTools($firstRev)); // Date of page creation $pageInfo['header-edits'][] = array($this->msg('pageinfo-firsttime'), Linker::linkKnown($title, htmlspecialchars($lang->userTimeAndDate($firstRev->getTimestamp(), $user)), array(), array('oldid' => $firstRev->getId()))); } if ($lastRev) { // Latest editor $pageInfo['header-edits'][] = array($this->msg('pageinfo-lastuser'), Linker::revUserTools($lastRev)); // Date of latest edit $pageInfo['header-edits'][] = array($this->msg('pageinfo-lasttime'), Linker::linkKnown($title, htmlspecialchars($lang->userTimeAndDate($this->page->getTimestamp(), $user)), array(), array('oldid' => $this->page->getLatest()))); } // Total number of edits $pageInfo['header-edits'][] = array($this->msg('pageinfo-edits'), $lang->formatNum($pageCounts['edits'])); // Total number of distinct authors if ($pageCounts['authors'] > 0) { $pageInfo['header-edits'][] = array($this->msg('pageinfo-authors'), $lang->formatNum($pageCounts['authors'])); } // Recent number of edits (within past 30 days) $pageInfo['header-edits'][] = array($this->msg('pageinfo-recent-edits', $lang->formatDuration($config->get('RCMaxAge'))), $lang->formatNum($pageCounts['recent_edits'])); // Recent number of distinct authors $pageInfo['header-edits'][] = array($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 = array(); foreach ($pageProperties as $property => $value) { if (in_array($property, $wordIDs)) { $listItems[] = Html::element('li', array(), $localizedWords[$property][1]); } } $localizedList = Html::rawElement('ul', array(), implode('', $listItems)); $hiddenCategories = $this->page->getHiddenCategories(); if (count($listItems) > 0 || count($hiddenCategories) > 0 || $pageCounts['transclusion']['from'] > 0 || $pageCounts['transclusion']['to'] > 0) { $options = array('LIMIT' => $config->get('PageInfoTransclusionLimit')); $transcludedTemplates = $title->getTemplateLinksFrom($options); if ($config->get('MiserMode')) { $transcludedTargets = array(); } else { $transcludedTargets = $title->getTemplateLinksTo($options); } // Page properties $pageInfo['header-properties'] = array(); // Magic words if (count($listItems) > 0) { $pageInfo['header-properties'][] = array($this->msg('pageinfo-magic-words')->numParams(count($listItems)), $localizedList); } // Hidden categories if (count($hiddenCategories) > 0) { $pageInfo['header-properties'][] = array($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; } $pageInfo['header-properties'][] = array($this->msg('pageinfo-templates')->numParams($pageCounts['transclusion']['from']), Linker::formatTemplates($transcludedTemplates, false, false, $more)); } if (!$config->get('MiserMode') && $pageCounts['transclusion']['to'] > 0) { if ($pageCounts['transclusion']['to'] > count($transcludedTargets)) { $more = Linker::link($whatLinksHere, $this->msg('moredotdotdot')->escaped(), array(), array('hidelinks' => 1, 'hideredirs' => 1)); } else { $more = null; } $pageInfo['header-properties'][] = array($this->msg('pageinfo-transclusions')->numParams($pageCounts['transclusion']['to']), Linker::formatTemplates($transcludedTargets, false, false, $more)); } } return $pageInfo; }
/** * Send the edit form and related headers to $wgOut * @param callable|null $formCallback That takes an OutputPage parameter; will be called * during form output near the top, for captchas and the like. */ function showEditForm($formCallback = null) { global $wgOut, $wgUser; wfProfileIn(__METHOD__); # need to parse the preview early so that we know which templates are used, # otherwise users with "show preview after edit box" will get a blank list # we parse this near the beginning so that setHeaders can do the title # setting work instead of leaving it in getPreviewText $previewOutput = ''; if ($this->formtype == 'preview') { $previewOutput = $this->getPreviewText(); } wfRunHooks('EditPage::showEditForm:initial', array(&$this, &$wgOut)); $this->setHeaders(); if ($this->showHeader() === false) { wfProfileOut(__METHOD__); return; } $wgOut->addHTML($this->editFormPageTop); if ($wgUser->getOption('previewontop')) { $this->displayPreviewArea($previewOutput, true); } $wgOut->addHTML($this->editFormTextTop); $showToolbar = true; if ($this->wasDeletedSinceLastEdit()) { if ($this->formtype == 'save') { // Hide the toolbar and edit area, user can click preview to get it back // Add an confirmation checkbox and explanation. $showToolbar = false; } else { $wgOut->wrapWikiMsg("<div class='error mw-deleted-while-editing'>\n\$1\n</div>", 'deletedwhileediting'); } } // @todo add EditForm plugin interface and use it here! // search for textarea1 and textares2, and allow EditForm to override all uses. $wgOut->addHTML(Html::openElement('form', array('id' => self::EDITFORM_ID, 'name' => self::EDITFORM_ID, 'method' => 'post', 'action' => $this->getActionURL($this->getContextTitle()), 'enctype' => 'multipart/form-data'))); if (is_callable($formCallback)) { call_user_func_array($formCallback, array(&$wgOut)); } // Add an empty field to trip up spambots $wgOut->addHTML(Xml::openElement('div', array('id' => 'antispam-container', 'style' => 'display: none;')) . Html::rawElement('label', array('for' => 'wpAntiSpam'), wfMessage('simpleantispam-label')->parse()) . Xml::element('input', array('type' => 'text', 'name' => 'wpAntispam', 'id' => 'wpAntispam', 'value' => '')) . Xml::closeElement('div')); wfRunHooks('EditPage::showEditForm:fields', array(&$this, &$wgOut)); // Put these up at the top to ensure they aren't lost on early form submission $this->showFormBeforeText(); if ($this->wasDeletedSinceLastEdit() && 'save' == $this->formtype) { $username = $this->lastDelete->user_name; $comment = $this->lastDelete->log_comment; // It is better to not parse the comment at all than to have templates expanded in the middle // TODO: can the checkLabel be moved outside of the div so that wrapWikiMsg could be used? $key = $comment === '' ? 'confirmrecreate-noreason' : 'confirmrecreate'; $wgOut->addHTML('<div class="mw-confirm-recreate">' . wfMessage($key, $username, "<nowiki>{$comment}</nowiki>")->parse() . Xml::checkLabel(wfMessage('recreate')->text(), 'wpRecreate', 'wpRecreate', false, array('title' => Linker::titleAttrib('recreate'), 'tabindex' => 1, 'id' => 'wpRecreate')) . '</div>'); } # When the summary is hidden, also hide them on preview/show changes if ($this->nosummary) { $wgOut->addHTML(Html::hidden('nosummary', true)); } # If a blank edit summary was previously provided, and the appropriate # user preference is active, pass a hidden tag as wpIgnoreBlankSummary. This will stop the # user being bounced back more than once in the event that a summary # is not required. ##### # For a bit more sophisticated detection of blank summaries, hash the # automatic one and pass that in the hidden field wpAutoSummary. if ($this->missingSummary || $this->section == 'new' && $this->nosummary) { $wgOut->addHTML(Html::hidden('wpIgnoreBlankSummary', true)); } if ($this->undidRev) { $wgOut->addHTML(Html::hidden('wpUndidRevision', $this->undidRev)); } if ($this->hasPresetSummary) { // If a summary has been preset using &summary= we don't want to prompt for // a different summary. Only prompt for a summary if the summary is blanked. // (Bug 17416) $this->autoSumm = md5(''); } $autosumm = $this->autoSumm ? $this->autoSumm : md5($this->summary); $wgOut->addHTML(Html::hidden('wpAutoSummary', $autosumm)); $wgOut->addHTML(Html::hidden('oldid', $this->oldid)); $wgOut->addHTML(Html::hidden('format', $this->contentFormat)); $wgOut->addHTML(Html::hidden('model', $this->contentModel)); if ($this->section == 'new') { $this->showSummaryInput(true, $this->summary); $wgOut->addHTML($this->getSummaryPreview(true, $this->summary)); } $wgOut->addHTML($this->editFormTextBeforeContent); if (!$this->isCssJsSubpage && $showToolbar && $wgUser->getOption('showtoolbar')) { $wgOut->addHTML(EditPage::getEditToolbar()); } if ($this->blankArticle) { $wgOut->addHTML(Html::hidden('wpIgnoreBlankArticle', true)); } if ($this->isConflict) { // In an edit conflict bypass the overridable content form method // and fallback to the raw wpTextbox1 since editconflicts can't be // resolved between page source edits and custom ui edits using the // custom edit ui. $this->textbox2 = $this->textbox1; $content = $this->getCurrentContent(); $this->textbox1 = $this->toEditText($content); $this->showTextbox1(); } else { $this->showContentForm(); } $wgOut->addHTML($this->editFormTextAfterContent); $this->showStandardInputs(); $this->showFormAfterText(); $this->showTosSummary(); $this->showEditTools(); $wgOut->addHTML($this->editFormTextAfterTools . "\n"); $wgOut->addHTML(Html::rawElement('div', array('class' => 'templatesUsed'), Linker::formatTemplates($this->getTemplates(), $this->preview, $this->section != ''))); $wgOut->addHTML(Html::rawElement('div', array('class' => 'hiddencats'), Linker::formatHiddenCategories($this->mArticle->getHiddenCategories()))); $wgOut->addHTML(Html::rawElement('div', array('class' => 'limitreport'), self::getPreviewLimitReport($this->mParserOutput))); $wgOut->addModules('mediawiki.action.edit.collapsibleFooter'); if ($this->isConflict) { try { $this->showConflict(); } catch (MWContentSerializationException $ex) { // this can't really happen, but be nice if it does. $msg = wfMessage('content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage()); $wgOut->addWikiText('<div class="error">' . $msg->text() . '</div>'); } } // Marker for detecting truncated form data. This must be the last // parameter sent in order to be of use, so do not move me. $wgOut->addHTML(Html::hidden('wpUltimateParam', true)); $wgOut->addHTML($this->editFormTextBottom . "\n</form>\n"); if (!$wgUser->getOption('previewontop')) { $this->displayPreviewArea($previewOutput, false); } wfProfileOut(__METHOD__); }
/** * 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, $wgRCMaxAge; $user = $this->getUser(); $lang = $this->getLanguage(); $title = $this->getTitle(); $id = $title->getArticleID(); // Get page information that would be too "expensive" to retrieve by normal means $pageCounts = self::pageCounts($title, $user); // Get page properties $dbr = wfGetDB(DB_SLAVE); $result = $dbr->select('page_props', array('pp_propname', 'pp_value'), array('pp_page' => $id), __METHOD__); $pageProperties = array(); foreach ($result as $row) { $pageProperties[$row->pp_propname] = $row->pp_value; } // Basic information $pageInfo = array(); $pageInfo['header-basic'] = array(); // Display title $displayTitle = $title->getPrefixedText(); if (!empty($pageProperties['displaytitle'])) { $displayTitle = $pageProperties['displaytitle']; } $pageInfo['header-basic'][] = array($this->msg('pageinfo-display-title'), $displayTitle); // Is it a redirect? If so, where to? if ($title->isRedirect()) { $pageInfo['header-basic'][] = array($this->msg('pageinfo-redirectsto'), Linker::link($this->page->getRedirectTarget()) . $this->msg('word-separator')->text() . $this->msg('parentheses', Linker::link($this->page->getRedirectTarget(), $this->msg('pageinfo-redirectsto-info')->escaped(), array(), array('action' => 'info')))->text()); } // Default sort key $sortKey = $title->getCategorySortKey(); if (!empty($pageProperties['defaultsort'])) { $sortKey = $pageProperties['defaultsort']; } $pageInfo['header-basic'][] = array($this->msg('pageinfo-default-sort'), $sortKey); // Page length (in bytes) $pageInfo['header-basic'][] = array($this->msg('pageinfo-length'), $lang->formatNum($title->getLength())); // Page ID (number not localised, as it's a database ID) $pageInfo['header-basic'][] = array($this->msg('pageinfo-article-id'), $id); // Language in which the page content is (supposed to be) written $pageLang = $title->getPageLanguage()->getCode(); $pageInfo['header-basic'][] = array($this->msg('pageinfo-language'), Language::fetchLanguageName($pageLang, $lang->getCode()) . ' ' . $this->msg('parentheses', $pageLang)); // Search engine status $pOutput = new ParserOutput(); if (isset($pageProperties['noindex'])) { $pOutput->setIndexPolicy('noindex'); } // Use robot policy logic $policy = $this->page->getRobotPolicy('view', $pOutput); $pageInfo['header-basic'][] = array($this->msg('pageinfo-robot-policy'), $this->msg("pageinfo-robot-{$policy['index']}")); if (isset($pageCounts['views'])) { // Number of views $pageInfo['header-basic'][] = array($this->msg('pageinfo-views'), $lang->formatNum($pageCounts['views'])); } if (isset($pageCounts['watchers'])) { // Number of page watchers $pageInfo['header-basic'][] = array($this->msg('pageinfo-watchers'), $lang->formatNum($pageCounts['watchers'])); } // Redirects to this page $whatLinksHere = SpecialPage::getTitleFor('Whatlinkshere', $title->getPrefixedText()); $pageInfo['header-basic'][] = array(Linker::link($whatLinksHere, $this->msg('pageinfo-redirects-name')->escaped(), array(), array('hidelinks' => 1, 'hidetrans' => 1)), $this->msg('pageinfo-redirects-value')->numParams(count($title->getRedirectsHere()))); // Is it counted as a content page? if ($this->page->isCountable()) { $pageInfo['header-basic'][] = array($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'][] = array(Linker::link($prefixIndex, $this->msg('pageinfo-subpages-name')->escaped()), $this->msg('pageinfo-subpages-value')->numParams($pageCounts['subpages']['total'], $pageCounts['subpages']['redirects'], $pageCounts['subpages']['nonredirects'])); } // Page protection $pageInfo['header-restrictions'] = array(); // Is this page effected by the cascading protection of something which includes it? if ($title->isCascadeProtected()) { $cascadingFrom = ''; $sources = $title->getCascadeProtectionSources(); // Array deferencing is in PHP 5.4 :( foreach ($sources[0] as $sourceTitle) { $cascadingFrom .= Html::rawElement('li', array(), Linker::linkKnown($sourceTitle)); } $cascadingFrom = Html::rawElement('ul', array(), $cascadingFrom); $pageInfo['header-restrictions'][] = array($this->msg('pageinfo-protect-cascading-from'), $cascadingFrom); } // Is out protection set to cascade to other pages? if ($title->areRestrictionsCascading()) { $pageInfo['header-restrictions'][] = array($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 $message = $this->msg("protect-level-{$protectionLevel}"); if ($message->isDisabled()) { // Require "$1" permission $message = $this->msg("protect-fallback", $protectionLevel)->parse(); } else { $message = $message->escaped(); } } $pageInfo['header-restrictions'][] = array($this->msg("restriction-{$restrictionType}"), $message); } if (!$this->page->exists()) { return $pageInfo; } // Edit history $pageInfo['header-edits'] = array(); $firstRev = $this->page->getOldestRevision(); // Page creator $pageInfo['header-edits'][] = array($this->msg('pageinfo-firstuser'), Linker::revUserTools($firstRev)); // Date of page creation $pageInfo['header-edits'][] = array($this->msg('pageinfo-firsttime'), Linker::linkKnown($title, $lang->userTimeAndDate($firstRev->getTimestamp(), $user), array(), array('oldid' => $firstRev->getId()))); // Latest editor $pageInfo['header-edits'][] = array($this->msg('pageinfo-lastuser'), Linker::revUserTools($this->page->getRevision())); // Date of latest edit $pageInfo['header-edits'][] = array($this->msg('pageinfo-lasttime'), Linker::linkKnown($title, $lang->userTimeAndDate($this->page->getTimestamp(), $user), array(), array('oldid' => $this->page->getLatest()))); // Total number of edits $pageInfo['header-edits'][] = array($this->msg('pageinfo-edits'), $lang->formatNum($pageCounts['edits'])); // Total number of distinct authors $pageInfo['header-edits'][] = array($this->msg('pageinfo-authors'), $lang->formatNum($pageCounts['authors'])); // Recent number of edits (within past 30 days) $pageInfo['header-edits'][] = array($this->msg('pageinfo-recent-edits', $lang->formatDuration($wgRCMaxAge)), $lang->formatNum($pageCounts['recent_edits'])); // Recent number of distinct authors $pageInfo['header-edits'][] = array($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 = array(); foreach ($pageProperties as $property => $value) { if (in_array($property, $wordIDs)) { $listItems[] = Html::element('li', array(), $localizedWords[$property][1]); } } $localizedList = Html::rawElement('ul', array(), implode('', $listItems)); $hiddenCategories = $this->page->getHiddenCategories(); $transcludedTemplates = $title->getTemplateLinksFrom(); if (count($listItems) > 0 || count($hiddenCategories) > 0 || count($transcludedTemplates) > 0) { // Page properties $pageInfo['header-properties'] = array(); // Magic words if (count($listItems) > 0) { $pageInfo['header-properties'][] = array($this->msg('pageinfo-magic-words')->numParams(count($listItems)), $localizedList); } // Hidden categories if (count($hiddenCategories) > 0) { $pageInfo['header-properties'][] = array($this->msg('pageinfo-hidden-categories')->numParams(count($hiddenCategories)), Linker::formatHiddenCategories($hiddenCategories)); } // Transcluded templates if (count($transcludedTemplates) > 0) { $pageInfo['header-properties'][] = array($this->msg('pageinfo-templates')->numParams(count($transcludedTemplates)), Linker::formatTemplates($transcludedTemplates)); } } return $pageInfo; }
/** * Show a read-only error * Parameters are the same as OutputPage:readOnlyPage() * Redirect to the article page if redlink=1 */ function displayPermissionsError(array $permErrors) { $this->mIsReadOnlyPage = true; $this->helper->addJsVariable('wgEditPageIsReadOnly', true); $formatPermissionsErrorMessage = $this->app->wg->Out->formatPermissionsErrorMessage($permErrors, 'edit'); $content = $this->app->wg->Out->parse($formatPermissionsErrorMessage); $this->mEditPagePreloads['PermissionsError'] = array('content' => $content, 'class' => 'permissions-errors'); // All of the following is pasted from EditPage:displayPermissionsError and pruned if ($this->app->wg->Request->getBool('redlink')) { // The edit page was reached via a red link. // Redirect to the article page and let them click the edit tab if // they really want a permission error. $this->app->wg->Out->redirect($this->mTitle->getFullUrl()); return; } $content = $this->getContent(); $this->app->wg->Out->setPageTitle(wfMessage('viewsource-title', $this->getContextTitle()->getPrefixedText())); $this->app->wg->Out->addBacklinkSubtitle($this->getContextTitle()); # If the user made changes, preserve them when showing the markup # (This happens when a user is blocked during edit, for instance) if (!$this->firsttime) { $content = $this->textbox1; $this->app->wg->Out->addWikiMsg('viewyourtext'); } else { $this->app->wg->Out->addWikiMsg('viewsourcetext'); } $this->showTextbox($content, 'wpTextbox1', array('readonly')); $this->app->wg->Out->addHTML(Html::rawElement('div', array('class' => 'templatesUsed'), Linker::formatTemplates($this->getTemplates()))); if ($this->mTitle->exists()) { $this->app->wg->Out->returnToMain(null, $this->mTitle); } }
public function getTemplatesList() { wfProfileIn(__METHOD__); $templates = $this->mTitle->getTemplateLinksFrom(); $html = Linker::formatTemplates($templates); wfProfileOut(__METHOD__); return $html; }
/** * @deprecated since 1.28, use TemplatesOnThisPageFormatter directly */ public function formatTemplates($templates, $preview = false, $section = false, $more = null) { wfDeprecated(__METHOD__, '1.28'); return Linker::formatTemplates($templates, $preview, $section, $more); }