/**
  * 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&section=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);
 }
示例#4
0
 /**
  * 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();
 }
示例#5
0
 /**
  * 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);
 }
示例#6
0
 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);
 }