/** * Build protection level selector * * @param string $action action to protect * @param string $selected current protection level * @return String: HTML fragment */ function buildSelector($action, $selected) { global $wgUser; // If the form is disabled, display all relevant levels. Otherwise, // just show the ones this user can use. $levels = MWNamespace::getRestrictionLevels($this->mTitle->getNamespace(), $this->disabled ? null : $wgUser); $id = 'mwProtect-level-' . $action; $attribs = array('id' => $id, 'name' => $id, 'size' => count($levels), 'onchange' => 'ProtectionForm.updateLevels(this)') + $this->disabledAttrib; $out = Xml::openElement('select', $attribs); foreach ($levels as $key) { $out .= Xml::option($this->getOptionLabel($key), $key, $key == $selected); } $out .= Xml::closeElement('select'); return $out; }
/** * a structured array of links usually used for the tabs in a skin * * There are 4 standard sections * namespaces: Used for namespace tabs like special, page, and talk namespaces * views: Used for primary page views like read, edit, history * actions: Used for most extra page actions like deletion, protection, etc... * variants: Used to list the language variants for the page * * Each section's value is a key/value array of links for that section. * The links themselves have these common keys: * - class: The css classes to apply to the tab * - text: The text to display on the tab * - href: The href for the tab to point to * - rel: An optional rel= for the tab's link * - redundant: If true the tab will be dropped in skins using content_actions * this is useful for tabs like "Read" which only have meaning in skins that * take special meaning from the grouped structure of content_navigation * * Views also have an extra key which can be used: * - primary: If this is not true skins like vector may try to hide the tab * when the user has limited space in their browser window * * content_navigation using code also expects these ids to be present on the * links, however these are usually automatically generated by SkinTemplate * itself and are not necessary when using a hook. The only things these may * matter to are people modifying content_navigation after it's initial creation: * - id: A "preferred" id, most skins are best off outputting this preferred * id for best compatibility. * - tooltiponly: This is set to true for some tabs in cases where the system * believes that the accesskey should not be added to the tab. * * @return array */ protected function buildContentNavigationUrls() { global $wgDisableLangConversion; // Display tabs for the relevant title rather than always the title itself $title = $this->getRelevantTitle(); $onPage = $title->equals($this->getTitle()); $out = $this->getOutput(); $request = $this->getRequest(); $user = $this->getUser(); $content_navigation = array('namespaces' => array(), 'views' => array(), 'actions' => array(), 'variants' => array()); // parameters $action = $request->getVal('action', 'view'); $userCanRead = $title->quickUserCan('read', $user); $preventActiveTabs = false; Hooks::run('SkinTemplatePreventOtherActiveTabs', array(&$this, &$preventActiveTabs)); // Checks if page is some kind of content if ($title->canExist()) { // Gets page objects for the related namespaces $subjectPage = $title->getSubjectPage(); $talkPage = $title->getTalkPage(); // Determines if this is a talk page $isTalk = $title->isTalkPage(); // Generates XML IDs from namespace names $subjectId = $title->getNamespaceKey(''); if ($subjectId == 'main') { $talkId = 'talk'; } else { $talkId = "{$subjectId}_talk"; } $skname = $this->skinname; // Adds namespace links $subjectMsg = array("nstab-{$subjectId}"); if ($subjectPage->isMainPage()) { array_unshift($subjectMsg, 'mainpage-nstab'); } $content_navigation['namespaces'][$subjectId] = $this->tabAction($subjectPage, $subjectMsg, !$isTalk && !$preventActiveTabs, '', $userCanRead); $content_navigation['namespaces'][$subjectId]['context'] = 'subject'; $content_navigation['namespaces'][$talkId] = $this->tabAction($talkPage, array("nstab-{$talkId}", 'talk'), $isTalk && !$preventActiveTabs, '', $userCanRead); $content_navigation['namespaces'][$talkId]['context'] = 'talk'; if ($userCanRead) { $isForeignFile = $title->inNamespace(NS_FILE) && $this->canUseWikiPage() && $this->getWikiPage() instanceof WikiFilePage && !$this->getWikiPage()->isLocal(); // Adds view view link if ($title->exists() || $isForeignFile) { $content_navigation['views']['view'] = $this->tabAction($isTalk ? $talkPage : $subjectPage, array("{$skname}-view-view", 'view'), $onPage && ($action == 'view' || $action == 'purge'), '', true); // signal to hide this from simple content_actions $content_navigation['views']['view']['redundant'] = true; } // If it is a non-local file, show a link to the file in its own repository if ($isForeignFile) { $file = $this->getWikiPage()->getFile(); $content_navigation['views']['view-foreign'] = array('class' => '', 'text' => wfMessageFallback("{$skname}-view-foreign", 'view-foreign')->setContext($this->getContext())->params($file->getRepo()->getDisplayName())->text(), 'href' => $file->getDescriptionUrl(), 'primary' => false); } // Checks if user can edit the current page if it exists or create it otherwise if ($title->quickUserCan('edit', $user) && ($title->exists() || $title->quickUserCan('create', $user))) { // Builds CSS class for talk page links $isTalkClass = $isTalk ? ' istalk' : ''; // Whether the user is editing the page $isEditing = $onPage && ($action == 'edit' || $action == 'submit'); // Whether to show the "Add a new section" tab // Checks if this is a current rev of talk page and is not forced to be hidden $showNewSection = !$out->forceHideNewSectionLink() && ($isTalk && $this->isRevisionCurrent() || $out->showNewSectionLink()); $section = $request->getVal('section'); if ($title->exists() || $title->getNamespace() == NS_MEDIAWIKI && $title->getDefaultMessageText() !== false) { $msgKey = $isForeignFile ? 'edit-local' : 'edit'; } else { $msgKey = $isForeignFile ? 'create-local' : 'create'; } $content_navigation['views']['edit'] = array('class' => ($isEditing && ($section !== 'new' || !$showNewSection) ? 'selected' : '') . $isTalkClass, 'text' => wfMessageFallback("{$skname}-view-{$msgKey}", $msgKey)->setContext($this->getContext())->text(), 'href' => $title->getLocalURL($this->editUrlOptions()), 'primary' => !$isForeignFile); // section link if ($showNewSection) { // Adds new section link //$content_navigation['actions']['addsection'] $content_navigation['views']['addsection'] = array('class' => $isEditing && $section == 'new' ? 'selected' : false, 'text' => wfMessageFallback("{$skname}-action-addsection", 'addsection')->setContext($this->getContext())->text(), 'href' => $title->getLocalURL('action=edit§ion=new')); } // Checks if the page has some kind of viewable content } elseif ($title->hasSourceText()) { // Adds view source view link $content_navigation['views']['viewsource'] = array('class' => $onPage && $action == 'edit' ? 'selected' : false, 'text' => wfMessageFallback("{$skname}-action-viewsource", 'viewsource')->setContext($this->getContext())->text(), 'href' => $title->getLocalURL($this->editUrlOptions()), 'primary' => true); } // Checks if the page exists if ($title->exists()) { // Adds history view link $content_navigation['views']['history'] = array('class' => $onPage && $action == 'history' ? 'selected' : false, 'text' => wfMessageFallback("{$skname}-view-history", 'history_short')->setContext($this->getContext())->text(), 'href' => $title->getLocalURL('action=history')); if ($title->quickUserCan('delete', $user)) { $content_navigation['actions']['delete'] = array('class' => $onPage && $action == 'delete' ? 'selected' : false, 'text' => wfMessageFallback("{$skname}-action-delete", 'delete')->setContext($this->getContext())->text(), 'href' => $title->getLocalURL('action=delete')); } if ($title->quickUserCan('move', $user)) { $moveTitle = SpecialPage::getTitleFor('Movepage', $title->getPrefixedDBkey()); $content_navigation['actions']['move'] = array('class' => $this->getTitle()->isSpecial('Movepage') ? 'selected' : false, 'text' => wfMessageFallback("{$skname}-action-move", 'move')->setContext($this->getContext())->text(), 'href' => $moveTitle->getLocalURL()); } } else { // article doesn't exist or is deleted if ($user->isAllowed('deletedhistory')) { $n = $title->isDeleted(); if ($n) { $undelTitle = SpecialPage::getTitleFor('Undelete', $title->getPrefixedDBkey()); // If the user can't undelete but can view deleted // history show them a "View .. deleted" tab instead. $msgKey = $user->isAllowed('undelete') ? 'undelete' : 'viewdeleted'; $content_navigation['actions']['undelete'] = array('class' => $this->getTitle()->isSpecial('Undelete') ? 'selected' : false, 'text' => wfMessageFallback("{$skname}-action-{$msgKey}", "{$msgKey}_short")->setContext($this->getContext())->numParams($n)->text(), 'href' => $undelTitle->getLocalURL()); } } } if ($title->quickUserCan('protect', $user) && $title->getRestrictionTypes() && MWNamespace::getRestrictionLevels($title->getNamespace(), $user) !== array('')) { $mode = $title->isProtected() ? 'unprotect' : 'protect'; $content_navigation['actions'][$mode] = array('class' => $onPage && $action == $mode ? 'selected' : false, 'text' => wfMessageFallback("{$skname}-action-{$mode}", $mode)->setContext($this->getContext())->text(), 'href' => $title->getLocalURL("action={$mode}")); } // Checks if the user is logged in if ($this->loggedin && $user->isAllowedAll('viewmywatchlist', 'editmywatchlist')) { /** * The following actions use messages which, if made particular to * the any specific skins, would break the Ajax code which makes this * action happen entirely inline. OutputPage::getJSVars * defines a set of messages in a javascript object - and these * messages are assumed to be global for all skins. Without making * a change to that procedure these messages will have to remain as * the global versions. */ $mode = $user->isWatched($title) ? 'unwatch' : 'watch'; $token = WatchAction::getWatchToken($title, $user, $mode); $content_navigation['actions'][$mode] = array('class' => $onPage && ($action == 'watch' || $action == 'unwatch') ? 'selected' : false, 'text' => $this->msg($mode)->text(), 'href' => $title->getLocalURL(array('action' => $mode, 'token' => $token))); } } Hooks::run('SkinTemplateNavigation', array(&$this, &$content_navigation)); if ($userCanRead && !$wgDisableLangConversion) { $pageLang = $title->getPageLanguage(); // Gets list of language variants $variants = $pageLang->getVariants(); // Checks that language conversion is enabled and variants exist // And if it is not in the special namespace if (count($variants) > 1) { // Gets preferred variant (note that user preference is // only possible for wiki content language variant) $preferred = $pageLang->getPreferredVariant(); if (Action::getActionName($this) === 'view') { $params = $request->getQueryValues(); unset($params['title']); } else { $params = array(); } // Loops over each variant foreach ($variants as $code) { // Gets variant name from language code $varname = $pageLang->getVariantname($code); // Appends variant link $content_navigation['variants'][] = array('class' => $code == $preferred ? 'selected' : false, 'text' => $varname, 'href' => $title->getLocalURL(array('variant' => $code) + $params), 'lang' => wfBCP47($code), 'hreflang' => wfBCP47($code)); } } } } else { // If it's not content, it's got to be a special page $content_navigation['namespaces']['special'] = array('class' => 'selected', 'text' => $this->msg('nstab-special')->text(), 'href' => $request->getRequestURL(), 'context' => 'subject'); Hooks::run('SkinTemplateNavigation::SpecialPage', array(&$this, &$content_navigation)); } // Equiv to SkinTemplateContentActions Hooks::run('SkinTemplateNavigation::Universal', array(&$this, &$content_navigation)); // Setup xml ids and tooltip info foreach ($content_navigation as $section => &$links) { foreach ($links as $key => &$link) { $xmlID = $key; if (isset($link['context']) && $link['context'] == 'subject') { $xmlID = 'ca-nstab-' . $xmlID; } elseif (isset($link['context']) && $link['context'] == 'talk') { $xmlID = 'ca-talk'; } elseif ($section == 'variants') { $xmlID = 'ca-varlang-' . $xmlID; } else { $xmlID = 'ca-' . $xmlID; } $link['id'] = $xmlID; } } # We don't want to give the watch tab an accesskey if the # page is being edited, because that conflicts with the # accesskey on the watch checkbox. We also don't want to # give the edit tab an accesskey, because that's fairly # superfluous and conflicts with an accesskey (Ctrl-E) often # used for editing in Safari. if (in_array($action, array('edit', 'submit'))) { if (isset($content_navigation['views']['edit'])) { $content_navigation['views']['edit']['tooltiponly'] = true; } if (isset($content_navigation['actions']['watch'])) { $content_navigation['actions']['watch']['tooltiponly'] = true; } if (isset($content_navigation['actions']['unwatch'])) { $content_navigation['actions']['unwatch']['tooltiponly'] = true; } } return $content_navigation; }
public function execute() { $user = $this->getUser(); $params = $this->extractRequestParams(); $title = Title::newFromText($params['page']); if (!$title) { $this->dieUsageMsg('invalidtitle', $params['page']); } $isSafeAction = in_array($params['paction'], self::$SAFE_ACTIONS, true); $availableNamespaces = $this->veConfig->get('VisualEditorAvailableNamespaces'); if (!$isSafeAction && (!isset($availableNamespaces[$title->getNamespace()]) || !$availableNamespaces[$title->getNamespace()])) { $this->dieUsage("VisualEditor is not enabled in namespace " . $title->getNamespace(), 'novenamespace'); } $parserParams = array(); if (isset($params['oldid'])) { $parserParams['oldid'] = $params['oldid']; } $html = $params['html']; if (substr($html, 0, 11) === 'rawdeflate,') { $deflated = base64_decode(substr($html, 11)); wfSuppressWarnings(); $html = gzinflate($deflated); wfRestoreWarnings(); if ($deflated === $html || $html === false) { $this->dieUsage("HTML provided is not properly deflated", 'invaliddeflate'); } } wfDebugLog('visualeditor', "called on '{$title}' with paction: '{$params['paction']}'"); switch ($params['paction']) { case 'parse': case 'metadata': // Dirty hack to provide the correct context for edit notices global $wgTitle; // FIXME NOOOOOOOOES $wgTitle = $title; RequestContext::getMain()->setTitle($title); // Get information about current revision if ($title->exists()) { $latestRevision = Revision::newFromTitle($title); if ($latestRevision === null) { $this->dieUsage('Could not find latest revision for title', 'latestnotfound'); } $revision = null; if (!isset($parserParams['oldid']) || $parserParams['oldid'] === 0) { $parserParams['oldid'] = $latestRevision->getId(); $revision = $latestRevision; } else { $revision = Revision::newFromId($parserParams['oldid']); if ($revision === null) { $this->dieUsage('Could not find revision ID ' . $parserParams['oldid'], 'oldidnotfound'); } } $restoring = $revision && !$revision->isCurrent(); $baseTimestamp = $latestRevision->getTimestamp(); $oldid = intval($parserParams['oldid']); // If requested, request HTML from Parsoid/RESTBase if ($params['paction'] === 'parse') { $content = $this->requestRestbase('GET', 'page/html/' . urlencode($title->getPrefixedDBkey()) . '/' . $oldid, array()); if ($content === false) { $this->dieUsage('Error contacting the document server', 'docserver'); } } } else { $content = ''; $baseTimestamp = wfTimestampNow(); $oldid = 0; $restoring = false; } // Get edit notices $notices = $title->getEditNotices(); // Anonymous user notice if ($user->isAnon()) { $notices[] = $this->msg('anoneditwarning', '{{fullurl:Special:UserLogin|returnto={{FULLPAGENAMEE}}}}', '{{fullurl:Special:UserLogin/signup|returnto={{FULLPAGENAMEE}}}}')->parseAsBlock(); } // Old revision notice if ($restoring) { $notices[] = $this->msg('editingold')->parseAsBlock(); } // New page notices if (!$title->exists()) { $notices[] = $this->msg($user->isLoggedIn() ? 'newarticletext' : 'newarticletextanon', wfExpandUrl(Skin::makeInternalOrExternalUrl($this->msg('helppage')->inContentLanguage()->text())))->parseAsBlock(); // Page protected from creation if ($title->getRestrictions('create')) { $notices[] = $this->msg('titleprotectedwarning')->parseAsBlock(); } } // Look at protection status to set up notices + surface class(es) $protectedClasses = array(); if (MWNamespace::getRestrictionLevels($title->getNamespace()) !== array('')) { // Page protected from editing if ($title->isProtected('edit')) { # Is the title semi-protected? if ($title->isSemiProtected()) { $protectedClasses[] = 'mw-textarea-sprotected'; $noticeMsg = 'semiprotectedpagewarning'; } else { $protectedClasses[] = 'mw-textarea-protected'; # Then it must be protected based on static groups (regular) $noticeMsg = 'protectedpagewarning'; } $notices[] = $this->msg($noticeMsg)->parseAsBlock() . $this->getLastLogEntry($title, 'protect'); } // Deal with cascading edit protection list($sources, $restrictions) = $title->getCascadeProtectionSources(); if (isset($restrictions['edit'])) { $protectedClasses[] = ' mw-textarea-cprotected'; $notice = $this->msg('cascadeprotectedwarning')->parseAsBlock() . '<ul>'; // Unfortunately there's no nice way to get only the pages which cause // editing to be restricted foreach ($sources as $source) { $notice .= "<li>" . Linker::link($source) . "</li>"; } $notice .= '</ul>'; $notices[] = $notice; } } // Permission notice $permErrors = $title->getUserPermissionsErrors('create', $user); if ($permErrors && !$title->exists()) { $notices[] = $this->msg('permissionserrorstext-withaction', 1, $this->msg('action-createpage')) . "<br>" . call_user_func_array(array($this, 'msg'), $permErrors[0])->parse(); } // Show notice when editing user / user talk page of a user that doesn't exist // or who is blocked // HACK of course this code is partly duplicated from EditPage.php :( if ($title->getNamespace() == NS_USER || $title->getNamespace() == NS_USER_TALK) { $parts = explode('/', $title->getText(), 2); $targetUsername = $parts[0]; $targetUser = User::newFromName($targetUsername, false); if (!($targetUser && $targetUser->isLoggedIn()) && !User::isIP($targetUsername)) { // User does not exist $notices[] = "<div class=\"mw-userpage-userdoesnotexist error\">\n" . $this->msg('userpage-userdoesnotexist', wfEscapeWikiText($targetUsername)) . "\n</div>"; } elseif ($targetUser->isBlocked()) { // Show log extract if the user is currently blocked $notices[] = $this->msg('blocked-notice-logextract', $targetUser->getName())->parseAsBlock() . $this->getLastLogEntry($targetUser->getUserPage(), 'block'); } } // Blocked user notice if ($user->isBlockedFrom($title) && $user->getBlock()->prevents('edit') !== false) { $notices[] = call_user_func_array(array($this, 'msg'), $user->getBlock()->getPermissionsError($this->getContext()))->parseAsBlock(); } // Blocked user notice for global blocks if (class_exists('GlobalBlocking')) { $error = GlobalBlocking::getUserBlockErrors($user, $this->getRequest()->getIP()); if (count($error)) { $notices[] = call_user_func_array(array($this, 'msg'), $error)->parseAsBlock(); } } // HACK: Build a fake EditPage so we can get checkboxes from it $article = new Article($title); // Deliberately omitting ,0 so oldid comes from request $ep = new EditPage($article); $req = $this->getRequest(); $req->setVal('format', 'text/x-wiki'); $ep->importFormData($req); // By reference for some reason (bug 52466) $tabindex = 0; $states = array('minor' => false, 'watch' => false); $checkboxes = $ep->getCheckboxes($tabindex, $states); // HACK: Find out which red links are on the page // We do the lookup for the current version. This might not be entirely complete // if we're loading an oldid, but it'll probably be close enough, and LinkCache // will automatically request any additional data it needs. $links = array(); $wikipage = WikiPage::factory($title); $popts = $wikipage->makeParserOptions('canonical'); $cached = ParserCache::singleton()->get($article, $popts, true); $links = array('missing' => array(), 'known' => $restoring || !$cached ? array() : 1); if ($cached) { foreach ($cached->getLinks() as $namespace => $cachedTitles) { foreach ($cachedTitles as $cachedTitleText => $exists) { $cachedTitle = Title::makeTitle($namespace, $cachedTitleText); if (!$cachedTitle->isKnown()) { $links['missing'][] = $cachedTitle->getPrefixedText(); } elseif ($links['known'] !== 1) { $links['known'][] = $cachedTitle->getPrefixedText(); } } } } // Add information about current page if (!$title->isKnown()) { $links['missing'][] = $title->getPrefixedText(); } elseif ($links['known'] !== 1) { $links['known'][] = $title->getPrefixedText(); } // On parser cache miss, just don't bother populating red link data $result = array('result' => 'success', 'notices' => $notices, 'checkboxes' => $checkboxes, 'links' => $links, 'protectedClasses' => implode(' ', $protectedClasses), 'watched' => $user->isWatched($title), 'basetimestamp' => $baseTimestamp, 'starttimestamp' => wfTimestampNow(), 'oldid' => $oldid); if ($params['paction'] === 'parse') { $result['content'] = $content; } break; case 'parsefragment': $wikitext = $params['wikitext']; if ($params['pst']) { $wikitext = $this->pstWikitext($title, $wikitext); } $content = $this->parseWikitextFragment($title, $wikitext); if ($content === false) { $this->dieUsage('Error contacting the document server', 'docserver'); } else { $result = array('result' => 'success', 'content' => $content); } break; case 'serialize': if ($params['cachekey'] !== null) { $content = $this->trySerializationCache($params['cachekey']); if (!is_string($content)) { $this->dieUsage('No cached serialization found with that key', 'badcachekey'); } } else { if ($params['html'] === null) { $this->dieUsageMsg('missingparam', 'html'); } $content = $this->postHTML($title, $html, $parserParams, $params['etag']); if ($content === false) { $this->dieUsage('Error contacting the document server', 'docserver'); } } $result = array('result' => 'success', 'content' => $content); break; case 'diff': if ($params['cachekey'] !== null) { $wikitext = $this->trySerializationCache($params['cachekey']); if (!is_string($wikitext)) { $this->dieUsage('No cached serialization found with that key', 'badcachekey'); } } else { $wikitext = $this->postHTML($title, $html, $parserParams, $params['etag']); if ($wikitext === false) { $this->dieUsage('Error contacting the document server', 'docserver'); } } $diff = $this->diffWikitext($title, $wikitext); if ($diff['result'] === 'fail') { $this->dieUsage('Diff failed', 'difffailed'); } $result = $diff; break; case 'serializeforcache': if (!isset($parserParams['oldid'])) { $parserParams['oldid'] = Revision::newFromTitle($title)->getId(); } $key = $this->storeInSerializationCache($title, $parserParams['oldid'], $html, $params['etag']); $result = array('result' => 'success', 'cachekey' => $key); break; case 'getlanglinks': $langlinks = $this->getLangLinks($title); if ($langlinks === false) { $this->dieUsage('Error querying MediaWiki API', 'api-langlinks-error'); } else { $result = array('result' => 'success', 'langlinks' => $langlinks); } break; } $this->getResult()->addValue(null, $this->getModuleName(), $result); }
/** * Build protection level selector * * @param string $action Action to protect * @param string $selected Current protection level * @return string HTML fragment */ function buildSelector($action, $selected) { // If the form is disabled, display all relevant levels. Otherwise, // just show the ones this user can use. $levels = MWNamespace::getRestrictionLevels($this->mTitle->getNamespace(), $this->disabled ? null : $this->mContext->getUser()); $id = 'mwProtect-level-' . $action; $select = new XmlSelect($id, $id, $selected); $select->setAttribute('size', count($levels)); if ($this->disabled) { $select->setAttribute('disabled', 'disabled'); } foreach ($levels as $key) { $select->addOption($this->getOptionLabel($key), $key); } return $select->getHTML(); }
/** * Method to output wpTextbox1 * The $textoverride method can be used by subclasses overriding showContentForm * to pass back to this method. * * @param array $customAttribs Array of html attributes to use in the textarea * @param string $textoverride Optional text to override $this->textarea1 with */ protected function showTextbox1($customAttribs = null, $textoverride = null) { if ($this->wasDeletedSinceLastEdit() && $this->formtype == 'save') { $attribs = array('style' => 'display:none;'); } else { $classes = array(); // Textarea CSS if ($this->mTitle->isProtected('edit') && MWNamespace::getRestrictionLevels($this->mTitle->getNamespace()) !== array('')) { # Is the title semi-protected? if ($this->mTitle->isSemiProtected()) { $classes[] = 'mw-textarea-sprotected'; } else { # Then it must be protected based on static groups (regular) $classes[] = 'mw-textarea-protected'; } # Is the title cascade-protected? if ($this->mTitle->isCascadeProtected()) { $classes[] = 'mw-textarea-cprotected'; } } $attribs = array('tabindex' => 1); if (is_array($customAttribs)) { $attribs += $customAttribs; } if (count($classes)) { if (isset($attribs['class'])) { $classes[] = $attribs['class']; } $attribs['class'] = implode(' ', $classes); } } $this->showTextbox($textoverride !== null ? $textoverride : $this->textbox1, 'wpTextbox1', $attribs); }
public function execute() { global $wgVisualEditorNamespaces, $wgVisualEditorParsoidURL, $wgVisualEditorParsoidTimeout, $wgDevelEnvironment; $user = $this->getUser(); $params = $this->extractRequestParams(); $page = Title::newFromText($params['page']); if (!$page) { $this->dieUsageMsg('invalidtitle', $params['page']); } if (!in_array($page->getNamespace(), $wgVisualEditorNamespaces)) { $this->dieUsage("VisualEditor is not enabled in namespace " . $page->getNamespace(), 'novenamespace'); } $parserParams = array(); if (isset($params['oldwt'])) { $parserParams['oldwt'] = $params['oldwt']; } else { if (isset($params['oldid'])) { $parserParams['oldid'] = $params['oldid']; } } switch ($params['paction']) { case 'parsewt': // FIXME: Perhaps requestParsoid method should be used here $postData = array('wt' => $params['wikitext']); $content = Http::post($wgVisualEditorParsoidURL . '/' . urlencode($this->getApiSource()) . '/' . urlencode($page->getPrefixedDBkey()), array('postData' => $postData, 'timeout' => $wgVisualEditorParsoidTimeout, 'noProxy' => !empty($wgDevelEnvironment))); $result = array('result' => 'success', 'content' => $content); break; case 'parse': $parsed = $this->getHTML($page, $parserParams); // Dirty hack to provide the correct context for edit notices global $wgTitle; // FIXME NOOOOOOOOES $wgTitle = $page; RequestContext::getMain()->setTitle($page); // TODO: In MW 1.19.7 method getEditNotices does not exist so for now fallback to just an empty // but in future figure out what's the proper backward compatibility solution. // #back-compat // $notices = $page->getEditNotices(); $notices = array(); $anoneditwarning = false; $anoneditwarningMessage = $this->msg('VisualEditor-anoneditwarning'); if ($user->isAnon() && $anoneditwarningMessage->exists()) { $notices[] = $anoneditwarningMessage->parseAsBlock(); $anoneditwarning = true; } if ($parsed && $parsed['restoring']) { $notices[] = $this->msg('editingold')->parseAsBlock(); } // Creating new page if (!$page->exists()) { $notices[] = $this->msg($user->isLoggedIn() ? 'newarticletext' : 'newarticletextanon', Skin::makeInternalOrExternalUrl($this->msg('helppage')->inContentLanguage()->text()))->parseAsBlock(); // Page protected from creation if ($page->getRestrictions('create')) { $notices[] = $this->msg('titleprotectedwarning')->parseAsBlock(); } } // Look at protection status to set up notices + surface class(es) $protectedClasses = array(); if (MWNamespace::getRestrictionLevels($page->getNamespace()) !== array('')) { // Page protected from editing if ($page->isProtected('edit')) { # Is the title semi-protected? if ($page->isSemiProtected()) { $protectedClasses[] = 'mw-textarea-sprotected'; $noticeMsg = 'semiprotectedpagewarning'; } else { $protectedClasses[] = 'mw-textarea-protected'; # Then it must be protected based on static groups (regular) $noticeMsg = 'protectedpagewarning'; } $notices[] = $this->msg($noticeMsg)->parseAsBlock(); } // Deal with cascading edit protection list($sources, $restrictions) = $page->getCascadeProtectionSources(); if (isset($restrictions['edit'])) { $protectedClasses[] = ' mw-textarea-cprotected'; $notice = $this->msg('cascadeprotectedwarning')->parseAsBlock() . '<ul>'; // Unfortunately there's no nice way to get only the pages which cause // editing to be restricted foreach ($sources as $source) { $notice .= "<li>" . Linker::link($source) . "</li>"; } $notice .= '</ul>'; $notices[] = $notice; } } // Show notice when editing user / user talk page of a user that doesn't exist // or who is blocked // HACK of course this code is partly duplicated from EditPage.php :( if ($page->getNamespace() == NS_USER || $page->getNamespace() == NS_USER_TALK) { $parts = explode('/', $page->getText(), 2); $targetUsername = $parts[0]; $targetUser = User::newFromName($targetUsername, false); if (!($targetUser && $targetUser->isLoggedIn()) && !User::isIP($targetUsername)) { // User does not exist $notices[] = "<div class=\"mw-userpage-userdoesnotexist error\">\n" . $this->msg('userpage-userdoesnotexist', wfEscapeWikiText($targetUsername)) . "\n</div>"; } // Some upstream code is deleted from here, more information: // https://github.com/Wikia/app/commit/d54b481d3f6e5b092b212a2c98b2cb5452bee26c // https://github.com/Wikia/app/commit/681e7437078206460f7c0cb1837095e656d8ba85 } if (class_exists('GlobalBlocking')) { $error = GlobalBlocking::getUserBlockErrors($user, $this->getRequest()->getIP()); if (count($error)) { $notices[] = call_user_func_array(array($this, 'msg'), $error)->parseAsBlock(); } } // HACK: Build a fake EditPage so we can get checkboxes from it $article = new Article($page); // Deliberately omitting ,0 so oldid comes from request $ep = new EditPage($article); $req = $this->getRequest(); $req->setVal('format', 'text/x-wiki'); $ep->importFormData($req); // By reference for some reason (bug 52466) $tabindex = 0; $states = array('minor' => false, 'watch' => false); $checkboxes = $ep->getCheckboxes($tabindex, $states); // HACK: Find out which red links are on the page // We do the lookup for the current version. This might not be entirely complete // if we're loading an oldid, but it'll probably be close enough, and LinkCache // will automatically request any additional data it needs. $links = array(); $wikipage = WikiPage::factory($page); $popts = $wikipage->makeParserOptions('canonical'); $cached = ParserCache::singleton()->get($article, $popts, true); if ($cached) { foreach ($cached->getLinks() as $ns => $dbks) { foreach ($dbks as $dbk => $id) { $links[Title::makeTitle($ns, $dbk)->getPrefixedText()] = array('missing' => $id == 0); } } } // On parser cache miss, just don't bother populating red link data if ($parsed === false) { $this->dieUsage('Error contacting the Parsoid server', 'parsoidserver'); } else { $result = array_merge(array('result' => 'success', 'notices' => $notices, 'checkboxes' => $checkboxes, 'links' => $links, 'protectedClasses' => implode(' ', $protectedClasses), 'anoneditwarning' => $anoneditwarning), $parsed['result']); } break; case 'parsefragment': $content = $this->parseWikitextFragment($page, $params['wikitext']); if ($content === false) { $this->dieUsage('Error contacting the Parsoid server', 'parsoidserver'); } else { $result = array('result' => 'success', 'content' => $content); } break; case 'serialize': if ($params['cachekey'] !== null) { $content = $this->trySerializationCache($params['cachekey']); if (!is_string($content)) { $this->dieUsage('No cached serialization found with that key', 'badcachekey'); } } else { if ($params['html'] === null) { $this->dieUsageMsg('missingparam', 'html'); } $html = $params['html']; $content = $this->postHTML($page, $html, $parserParams); if ($content === false) { $this->dieUsage('Error contacting the Parsoid server', 'parsoidserver'); } } $result = array('result' => 'success', 'content' => $content); break; case 'diff': if ($params['cachekey'] !== null) { $wikitext = $this->trySerializationCache($params['cachekey']); if (!is_string($wikitext)) { $this->dieUsage('No cached serialization found with that key', 'badcachekey'); } } else { $wikitext = $this->postHTML($page, $params['html'], $parserParams); if ($wikitext === false) { $this->dieUsage('Error contacting the Parsoid server', 'parsoidserver'); } } $diff = $this->diffWikitext($page, $wikitext); if ($diff['result'] === 'fail') { $this->dieUsage('Diff failed', 'difffailed'); } $result = $diff; break; case 'serializeforcache': $key = $this->storeInSerializationCache($page, $parserParams['oldid'], $params['html']); $result = array('result' => 'success', 'cachekey' => $key); break; case 'getlanglinks': $langlinks = $this->getLangLinks($page); if ($langlinks === false) { $this->dieUsage('Error querying MediaWiki API', 'parsoidserver'); } else { $result = array('result' => 'success', 'langlinks' => $langlinks); } break; } $this->getResult()->addValue(null, $this->getModuleName(), $result); }