/** * Fetch initial editing page content. * * @param $def_text string * @return mixed string on success, $def_text for invalid sections * @private */ function getContent($def_text = '') { global $wgOut, $wgRequest, $wgParser; wfProfileIn(__METHOD__); $text = false; // For message page not locally set, use the i18n message. // For other non-existent articles, use preload text if any. if (!$this->mTitle->exists() || $this->section == 'new') { if ($this->mTitle->getNamespace() == NS_MEDIAWIKI && $this->section != 'new') { # If this is a system message, get the default text. $text = $this->mTitle->getDefaultMessageText(); } if ($text === false) { # If requested, preload some text. $preload = $wgRequest->getVal('preload', $this->section === 'new' ? 'MediaWiki:addsection-preload' : ''); $text = $this->getPreloadedText($preload); } // For existing pages, get text based on "undo" or section parameters. } else { if ($this->section != '') { // Get section edit text (returns $def_text for invalid sections) $text = $wgParser->getSection($this->getOriginalContent(), $this->section, $def_text); } else { $undoafter = $wgRequest->getInt('undoafter'); $undo = $wgRequest->getInt('undo'); if ($undo > 0 && $undoafter > 0) { if ($undo < $undoafter) { # If they got undoafter and undo round the wrong way, switch them list($undo, $undoafter) = array($undoafter, $undo); } $undorev = Revision::newFromId($undo); $oldrev = Revision::newFromId($undoafter); # Sanity check, make sure it's the right page, # the revisions exist and they were not deleted. # Otherwise, $text will be left as-is. if (!is_null($undorev) && !is_null($oldrev) && $undorev->getPage() == $oldrev->getPage() && $undorev->getPage() == $this->mTitle->getArticleId() && !$undorev->isDeleted(Revision::DELETED_TEXT) && !$oldrev->isDeleted(Revision::DELETED_TEXT)) { $text = $this->mArticle->getUndoText($undorev, $oldrev); if ($text === false) { # Warn the user that something went wrong $undoMsg = 'failure'; } else { # Inform the user of our success and set an automatic edit summary $undoMsg = 'success'; # If we just undid one rev, use an autosummary $firstrev = $oldrev->getNext(); if ($firstrev->getId() == $undo) { $undoSummary = wfMsgForContent('undo-summary', $undo, $undorev->getUserText()); if ($this->summary === '') { $this->summary = $undoSummary; } else { $this->summary = $undoSummary . wfMsgForContent('colon-separator') . $this->summary; } $this->undidRev = $undo; } $this->formtype = 'diff'; } } else { // Failed basic sanity checks. // Older revisions may have been removed since the link // was created, or we may simply have got bogus input. $undoMsg = 'norev'; } $class = ($undoMsg == 'success' ? '' : 'error ') . "mw-undo-{$undoMsg}"; $this->editFormPageTop .= $wgOut->parse("<div class=\"{$class}\">" . wfMsgNoTrans('undo-' . $undoMsg) . '</div>', true, true); } if ($text === false) { $text = $this->getOriginalContent(); } } } wfProfileOut(__METHOD__); return $text; }
public function execute() { global $wgUser; $params = $this->extractRequestParams(); if (is_null($params['text']) && is_null($params['appendtext']) && is_null($params['prependtext']) && $params['undo'] == 0) { $this->dieUsageMsg(array('missingtext')); } $titleObj = Title::newFromText($params['title']); if (!$titleObj || $titleObj->isExternal()) { $this->dieUsageMsg(array('invalidtitle', $params['title'])); } if ($params['redirect']) { if ($titleObj->isRedirect()) { $oldTitle = $titleObj; $titles = Title::newFromRedirectArray(Revision::newFromTitle($oldTitle)->getText(Revision::FOR_THIS_USER)); $redirValues = array(); foreach ($titles as $id => $newTitle) { if (!isset($titles[$id - 1])) { $titles[$id - 1] = $oldTitle; } $redirValues[] = array('from' => $titles[$id - 1]->getPrefixedText(), 'to' => $newTitle->getPrefixedText()); $titleObj = $newTitle; } $this->getResult()->setIndexedTagName($redirValues, 'r'); $this->getResult()->addValue(null, 'redirects', $redirValues); } } // Some functions depend on $wgTitle == $ep->mTitle global $wgTitle; $wgTitle = $titleObj; if ($params['createonly'] && $titleObj->exists()) { $this->dieUsageMsg(array('createonly-exists')); } if ($params['nocreate'] && !$titleObj->exists()) { $this->dieUsageMsg(array('nocreate-missing')); } // Now let's check whether we're even allowed to do this $errors = $titleObj->getUserPermissionsErrors('edit', $wgUser); if (!$titleObj->exists()) { $errors = array_merge($errors, $titleObj->getUserPermissionsErrors('create', $wgUser)); } if (count($errors)) { $this->dieUsageMsg($errors[0]); } $articleObj = new Article($titleObj); $toMD5 = $params['text']; if (!is_null($params['appendtext']) || !is_null($params['prependtext'])) { // For non-existent pages, Article::getContent() // returns an interface message rather than '' // We do want getContent()'s behavior for non-existent // MediaWiki: pages, though if ($articleObj->getID() == 0 && $titleObj->getNamespace() != NS_MEDIAWIKI) { $content = ''; } else { $content = $articleObj->getContent(); } if (!is_null($params['section'])) { // Process the content for section edits global $wgParser; $section = intval($params['section']); $content = $wgParser->getSection($content, $section, false); if ($content === false) { $this->dieUsage("There is no section {$section}.", 'nosuchsection'); } } $params['text'] = $params['prependtext'] . $content . $params['appendtext']; $toMD5 = $params['prependtext'] . $params['appendtext']; } if ($params['undo'] > 0) { if ($params['undoafter'] > 0) { if ($params['undo'] < $params['undoafter']) { list($params['undo'], $params['undoafter']) = array($params['undoafter'], $params['undo']); } $undoafterRev = Revision::newFromID($params['undoafter']); } $undoRev = Revision::newFromID($params['undo']); if (is_null($undoRev) || $undoRev->isDeleted(Revision::DELETED_TEXT)) { $this->dieUsageMsg(array('nosuchrevid', $params['undo'])); } if ($params['undoafter'] == 0) { $undoafterRev = $undoRev->getPrevious(); } if (is_null($undoafterRev) || $undoafterRev->isDeleted(Revision::DELETED_TEXT)) { $this->dieUsageMsg(array('nosuchrevid', $params['undoafter'])); } if ($undoRev->getPage() != $articleObj->getID()) { $this->dieUsageMsg(array('revwrongpage', $undoRev->getID(), $titleObj->getPrefixedText())); } if ($undoafterRev->getPage() != $articleObj->getID()) { $this->dieUsageMsg(array('revwrongpage', $undoafterRev->getID(), $titleObj->getPrefixedText())); } $newtext = $articleObj->getUndoText($undoRev, $undoafterRev); if ($newtext === false) { $this->dieUsageMsg(array('undo-failure')); } $params['text'] = $newtext; // If no summary was given and we only undid one rev, // use an autosummary if (is_null($params['summary']) && $titleObj->getNextRevisionID($undoafterRev->getID()) == $params['undo']) { $params['summary'] = wfMsgForContent('undo-summary', $params['undo'], $undoRev->getUserText()); } } // See if the MD5 hash checks out if (!is_null($params['md5']) && md5($toMD5) !== $params['md5']) { $this->dieUsageMsg(array('hashcheckfailed')); } $ep = new EditPage($articleObj); // EditPage wants to parse its stuff from a WebRequest // That interface kind of sucks, but it's workable $reqArr = array('wpTextbox1' => $params['text'], 'wpEditToken' => $params['token'], 'wpIgnoreBlankSummary' => ''); if (!is_null($params['summary'])) { $reqArr['wpSummary'] = $params['summary']; } // Watch out for basetimestamp == '' // wfTimestamp() treats it as NOW, almost certainly causing an edit conflict if (!is_null($params['basetimestamp']) && $params['basetimestamp'] != '') { $reqArr['wpEdittime'] = wfTimestamp(TS_MW, $params['basetimestamp']); } else { $reqArr['wpEdittime'] = $articleObj->getTimestamp(); } if (!is_null($params['starttimestamp']) && $params['starttimestamp'] != '') { $reqArr['wpStarttime'] = wfTimestamp(TS_MW, $params['starttimestamp']); } else { $reqArr['wpStarttime'] = wfTimestampNow(); // Fake wpStartime } if ($params['minor'] || !$params['notminor'] && $wgUser->getOption('minordefault')) { $reqArr['wpMinoredit'] = ''; } if ($params['recreate']) { $reqArr['wpRecreate'] = ''; } if (!is_null($params['section'])) { $section = intval($params['section']); if ($section == 0 && $params['section'] != '0' && $params['section'] != 'new') { $this->dieUsage("The section parameter must be set to an integer or 'new'", "invalidsection"); } $reqArr['wpSection'] = $params['section']; } else { $reqArr['wpSection'] = ''; } $watch = $this->getWatchlistValue($params['watchlist'], $titleObj); // Deprecated parameters if ($params['watch']) { $watch = true; } elseif ($params['unwatch']) { $watch = false; } if ($watch) { $reqArr['wpWatchthis'] = ''; } $req = new FauxRequest($reqArr, true); $ep->importFormData($req); // Run hooks // Handle CAPTCHA parameters global $wgRequest; if (!is_null($params['captchaid'])) { $wgRequest->setVal('wpCaptchaId', $params['captchaid']); } if (!is_null($params['captchaword'])) { $wgRequest->setVal('wpCaptchaWord', $params['captchaword']); } $r = array(); if (!wfRunHooks('APIEditBeforeSave', array($ep, $ep->textbox1, &$r))) { if (count($r)) { $r['result'] = 'Failure'; $this->getResult()->addValue(null, $this->getModuleName(), $r); return; } else { $this->dieUsageMsg(array('hookaborted')); } } // Do the actual save $oldRevId = $articleObj->getRevIdFetched(); $result = null; // Fake $wgRequest for some hooks inside EditPage // FIXME: This interface SUCKS $oldRequest = $wgRequest; $wgRequest = $req; $retval = $ep->internalAttemptSave($result, $wgUser->isAllowed('bot') && $params['bot']); $wgRequest = $oldRequest; global $wgMaxArticleSize; switch ($retval) { case EditPage::AS_HOOK_ERROR: case EditPage::AS_HOOK_ERROR_EXPECTED: $this->dieUsageMsg(array('hookaborted')); case EditPage::AS_IMAGE_REDIRECT_ANON: $this->dieUsageMsg(array('noimageredirect-anon')); case EditPage::AS_IMAGE_REDIRECT_LOGGED: $this->dieUsageMsg(array('noimageredirect-logged')); case EditPage::AS_SPAM_ERROR: $this->dieUsageMsg(array('spamdetected', $result['spam'])); case EditPage::AS_FILTERING: $this->dieUsageMsg(array('filtered')); case EditPage::AS_BLOCKED_PAGE_FOR_USER: $this->dieUsageMsg(array('blockedtext')); case EditPage::AS_MAX_ARTICLE_SIZE_EXCEEDED: case EditPage::AS_CONTENT_TOO_BIG: $this->dieUsageMsg(array('contenttoobig', $wgMaxArticleSize)); case EditPage::AS_READ_ONLY_PAGE_ANON: $this->dieUsageMsg(array('noedit-anon')); case EditPage::AS_READ_ONLY_PAGE_LOGGED: $this->dieUsageMsg(array('noedit')); case EditPage::AS_READ_ONLY_PAGE: $this->dieReadOnly(); case EditPage::AS_RATE_LIMITED: $this->dieUsageMsg(array('actionthrottledtext')); case EditPage::AS_ARTICLE_WAS_DELETED: $this->dieUsageMsg(array('wasdeleted')); case EditPage::AS_NO_CREATE_PERMISSION: $this->dieUsageMsg(array('nocreate-loggedin')); case EditPage::AS_BLANK_ARTICLE: $this->dieUsageMsg(array('blankpage')); case EditPage::AS_CONFLICT_DETECTED: $this->dieUsageMsg(array('editconflict')); // case EditPage::AS_SUMMARY_NEEDED: Can't happen since we set wpIgnoreBlankSummary // case EditPage::AS_SUMMARY_NEEDED: Can't happen since we set wpIgnoreBlankSummary case EditPage::AS_TEXTBOX_EMPTY: $this->dieUsageMsg(array('emptynewsection')); case EditPage::AS_SUCCESS_NEW_ARTICLE: $r['new'] = ''; case EditPage::AS_SUCCESS_UPDATE: $r['result'] = 'Success'; $r['pageid'] = intval($titleObj->getArticleID()); $r['title'] = $titleObj->getPrefixedText(); // HACK: We create a new Article object here because getRevIdFetched() // refuses to be run twice, and because Title::getLatestRevId() // won't fetch from the master unless we select for update, which we // don't want to do. $newArticle = new Article($titleObj); $newRevId = $newArticle->getRevIdFetched(); if ($newRevId == $oldRevId) { $r['nochange'] = ''; } else { $r['oldrevid'] = intval($oldRevId); $r['newrevid'] = intval($newRevId); $r['newtimestamp'] = wfTimestamp(TS_ISO_8601, $newArticle->getTimestamp()); } break; case EditPage::AS_SUMMARY_NEEDED: $this->dieUsageMsg(array('summaryrequired')); case EditPage::AS_END: // This usually means some kind of race condition // or DB weirdness occurred. Fall through to throw an unknown // error. // This needs fixing higher up, as Article::doEdit should be // used rather than Article::updateArticle, so that specific // error conditions can be returned // This usually means some kind of race condition // or DB weirdness occurred. Fall through to throw an unknown // error. // This needs fixing higher up, as Article::doEdit should be // used rather than Article::updateArticle, so that specific // error conditions can be returned default: $this->dieUsageMsg(array('unknownerror', $retval)); } $this->getResult()->addValue(null, $this->getModuleName(), $r); }
/** * Fetch initial editing page content. * * @param $def_text string * @returns mixed string on success, $def_text for invalid sections * @private */ function getContent($def_text = '') { global $wgOut, $wgRequest, $wgParser; wfProfileIn(__METHOD__); # Get variables from query string :P $section = $wgRequest->getVal('section'); $preload = $wgRequest->getVal('preload', $section === 'new' ? 'MediaWiki:addsection-preload' : ''); $undoafter = $wgRequest->getVal('undoafter'); $undo = $wgRequest->getVal('undo'); // For message page not locally set, use the i18n message. // For other non-existent articles, use preload text if any. if (!$this->mTitle->exists()) { if ($this->mTitle->getNamespace() == NS_MEDIAWIKI) { # If this is a system message, get the default text. $text = $this->mTitle->getDefaultMessageText(); if ($text === false) { $text = $this->getPreloadedText($preload); } } else { # If requested, preload some text. $text = $this->getPreloadedText($preload); } // For existing pages, get text based on "undo" or section parameters. } else { $text = $this->mArticle->getContent(); if ($undo > 0 && $undoafter > 0 && $undo < $undoafter) { # If they got undoafter and undo round the wrong way, switch them list($undo, $undoafter) = array($undoafter, $undo); } if ($undo > 0 && $undo > $undoafter) { # Undoing a specific edit overrides section editing; section-editing # doesn't work with undoing. if ($undoafter) { $undorev = Revision::newFromId($undo); $oldrev = Revision::newFromId($undoafter); } else { $undorev = Revision::newFromId($undo); $oldrev = $undorev ? $undorev->getPrevious() : null; } # Sanity check, make sure it's the right page, # the revisions exist and they were not deleted. # Otherwise, $text will be left as-is. if (!is_null($undorev) && !is_null($oldrev) && $undorev->getPage() == $oldrev->getPage() && $undorev->getPage() == $this->mArticle->getID() && !$undorev->isDeleted(Revision::DELETED_TEXT) && !$oldrev->isDeleted(Revision::DELETED_TEXT)) { $undotext = $this->mArticle->getUndoText($undorev, $oldrev); if ($undotext === false) { # Warn the user that something went wrong $this->editFormPageTop .= $wgOut->parse('<div class="error mw-undo-failure">' . wfMsgNoTrans('undo-failure') . '</div>'); } else { $text = $undotext; # Inform the user of our success and set an automatic edit summary $this->editFormPageTop .= $wgOut->parse('<div class="mw-undo-success">' . wfMsgNoTrans('undo-success') . '</div>'); $firstrev = $oldrev->getNext(); # If we just undid one rev, use an autosummary if ($firstrev->mId == $undo) { $this->summary = wfMsgForContent('undo-summary', $undo, $undorev->getUserText()); $this->undidRev = $undo; } $this->formtype = 'diff'; } } else { // Failed basic sanity checks. // Older revisions may have been removed since the link // was created, or we may simply have got bogus input. $this->editFormPageTop .= $wgOut->parse('<div class="error mw-undo-norev">' . wfMsgNoTrans('undo-norev') . '</div>'); } } elseif ($section != '') { if ($section == 'new') { $text = $this->getPreloadedText($preload); } else { // Get section edit text (returns $def_text for invalid sections) $text = $wgParser->getSection($text, $section, $def_text); } } } wfProfileOut(__METHOD__); return $text; }