public function execute() { global $wgHooks; $out = $this->mSpecial->getOutput(); if (!ModerationCanSkip::canSkip($this->moderator)) { // In order to merge, moderator must also be automoderated throw new ModerationError('moderation-merge-not-automoderated'); } $dbw = wfGetDB(DB_MASTER); $row = $dbw->selectRow('moderation', array('mod_namespace AS namespace', 'mod_title AS title', 'mod_user_text AS user_text', 'mod_text AS text', 'mod_conflict AS conflict'), array('mod_id' => $this->id), __METHOD__); if (!$row) { throw new ModerationError('moderation-edit-not-found'); } if (!$row->conflict) { throw new ModerationError('moderation-merge-not-needed'); } $title = Title::makeTitle($row->namespace, $row->title); $article = new Article($title); ModerationEditHooks::$NewMergeID = $this->id; $editPage = new EditPage($article); $editPage->isConflict = true; $editPage->setContextTitle($title); $editPage->textbox1 = $row->text; $editPage->summary = wfMessage("moderation-merge-comment", $row->user_text)->inContentLanguage()->plain(); $editPage->showEditForm(); }
public function execute() { $user = $this->getUser(); $params = $this->extractRequestParams(); if (is_null($params['text']) && is_null($params['appendtext']) && is_null($params['prependtext']) && $params['undo'] == 0) { $this->dieUsageMsg('missingtext'); } $pageObj = $this->getTitleOrPageId($params); $titleObj = $pageObj->getTitle(); $apiResult = $this->getResult(); if ($params['redirect']) { if ($params['prependtext'] === null && $params['appendtext'] === null && $params['section'] !== 'new') { $this->dieUsage('You have attempted to edit using the "redirect"-following' . ' mode, which must be used in conjuction with section=new, prependtext' . ', or appendtext.', 'redirect-appendonly'); } if ($titleObj->isRedirect()) { $oldTitle = $titleObj; $titles = Revision::newFromTitle($oldTitle, false, Revision::READ_LATEST)->getContent(Revision::FOR_THIS_USER, $user)->getRedirectChain(); // array_shift( $titles ); $redirValues = array(); /** @var $newTitle Title */ 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; } ApiResult::setIndexedTagName($redirValues, 'r'); $apiResult->addValue(null, 'redirects', $redirValues); // Since the page changed, update $pageObj $pageObj = WikiPage::factory($titleObj); } } if (!isset($params['contentmodel']) || $params['contentmodel'] == '') { $contentHandler = $pageObj->getContentHandler(); } else { $contentHandler = ContentHandler::getForModelID($params['contentmodel']); } $name = $titleObj->getPrefixedDBkey(); $model = $contentHandler->getModelID(); if ($contentHandler->supportsDirectApiEditing() === false) { $this->dieUsage("Direct editing via API is not supported for content model {$model} used by {$name}", 'no-direct-editing'); } if (!isset($params['contentformat']) || $params['contentformat'] == '') { $params['contentformat'] = $contentHandler->getDefaultFormat(); } $contentFormat = $params['contentformat']; if (!$contentHandler->isSupportedFormat($contentFormat)) { $this->dieUsage("The requested format {$contentFormat} is not supported for content model " . " {$model} used by {$name}", 'badformat'); } if ($params['createonly'] && $titleObj->exists()) { $this->dieUsageMsg('createonly-exists'); } if ($params['nocreate'] && !$titleObj->exists()) { $this->dieUsageMsg('nocreate-missing'); } // Now let's check whether we're even allowed to do this $errors = $titleObj->getUserPermissionsErrors('edit', $user); if (!$titleObj->exists()) { $errors = array_merge($errors, $titleObj->getUserPermissionsErrors('create', $user)); } if (count($errors)) { if (is_array($errors[0])) { switch ($errors[0][0]) { case 'blockedtext': $this->dieUsage('You have been blocked from editing', 'blocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock()))); break; case 'autoblockedtext': $this->dieUsage('Your IP address has been blocked automatically, because it was used by a blocked user', 'autoblocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock()))); break; default: $this->dieUsageMsg($errors[0]); } } else { $this->dieUsageMsg($errors[0]); } } $toMD5 = $params['text']; if (!is_null($params['appendtext']) || !is_null($params['prependtext'])) { $content = $pageObj->getContent(); if (!$content) { if ($titleObj->getNamespace() == NS_MEDIAWIKI) { # If this is a MediaWiki:x message, then load the messages # and return the message value for x. $text = $titleObj->getDefaultMessageText(); if ($text === false) { $text = ''; } try { $content = ContentHandler::makeContent($text, $this->getTitle()); } catch (MWContentSerializationException $ex) { $this->dieUsage($ex->getMessage(), 'parseerror'); return; } } else { # Otherwise, make a new empty content. $content = $contentHandler->makeEmptyContent(); } } // @todo Add support for appending/prepending to the Content interface if (!$content instanceof TextContent) { $mode = $contentHandler->getModelID(); $this->dieUsage("Can't append to pages using content model {$mode}", 'appendnotsupported'); } if (!is_null($params['section'])) { if (!$contentHandler->supportsSections()) { $modelName = $contentHandler->getModelID(); $this->dieUsage("Sections are not supported for this content model: {$modelName}.", 'sectionsnotsupported'); } if ($params['section'] == 'new') { // DWIM if they're trying to prepend/append to a new section. $content = null; } else { // Process the content for section edits $section = $params['section']; $content = $content->getSection($section); if (!$content) { $this->dieUsage("There is no section {$section}.", 'nosuchsection'); } } } if (!$content) { $text = ''; } else { $text = $content->serialize($contentFormat); } $params['text'] = $params['prependtext'] . $text . $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() != $pageObj->getID()) { $this->dieUsageMsg(array('revwrongpage', $undoRev->getID(), $titleObj->getPrefixedText())); } if ($undoafterRev->getPage() != $pageObj->getID()) { $this->dieUsageMsg(array('revwrongpage', $undoafterRev->getID(), $titleObj->getPrefixedText())); } $newContent = $contentHandler->getUndoContent($pageObj->getRevision(), $undoRev, $undoafterRev); if (!$newContent) { $this->dieUsageMsg('undo-failure'); } $params['text'] = $newContent->serialize($params['contentformat']); // 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'] = wfMessage('undo-summary')->params($params['undo'], $undoRev->getUserText())->inContentLanguage()->text(); } } // See if the MD5 hash checks out if (!is_null($params['md5']) && md5($toMD5) !== $params['md5']) { $this->dieUsageMsg('hashcheckfailed'); } // EditPage wants to parse its stuff from a WebRequest // That interface kind of sucks, but it's workable $requestArray = array('wpTextbox1' => $params['text'], 'format' => $contentFormat, 'model' => $contentHandler->getModelID(), 'wpEditToken' => $params['token'], 'wpIgnoreBlankSummary' => true, 'wpIgnoreBlankArticle' => true, 'wpIgnoreSelfRedirect' => true, 'bot' => $params['bot']); if (!is_null($params['summary'])) { $requestArray['wpSummary'] = $params['summary']; } if (!is_null($params['sectiontitle'])) { $requestArray['wpSectionTitle'] = $params['sectiontitle']; } // TODO: Pass along information from 'undoafter' as well if ($params['undo'] > 0) { $requestArray['wpUndidRevision'] = $params['undo']; } // Watch out for basetimestamp == '' or '0' // It gets treated as NOW, almost certainly causing an edit conflict if ($params['basetimestamp'] !== null && (bool) $this->getMain()->getVal('basetimestamp')) { $requestArray['wpEdittime'] = $params['basetimestamp']; } else { $requestArray['wpEdittime'] = $pageObj->getTimestamp(); } if ($params['starttimestamp'] !== null) { $requestArray['wpStarttime'] = $params['starttimestamp']; } else { $requestArray['wpStarttime'] = wfTimestampNow(); // Fake wpStartime } if ($params['minor'] || !$params['notminor'] && $user->getOption('minordefault')) { $requestArray['wpMinoredit'] = ''; } if ($params['recreate']) { $requestArray['wpRecreate'] = ''; } if (!is_null($params['section'])) { $section = $params['section']; if (!preg_match('/^((T-)?\\d+|new)$/', $section)) { $this->dieUsage("The section parameter must be a valid section id or 'new'", "invalidsection"); } $content = $pageObj->getContent(); if ($section !== '0' && $section != 'new' && (!$content || !$content->getSection($section))) { $this->dieUsage("There is no section {$section}.", 'nosuchsection'); } $requestArray['wpSection'] = $params['section']; } else { $requestArray['wpSection'] = ''; } $watch = $this->getWatchlistValue($params['watchlist'], $titleObj); // Deprecated parameters if ($params['watch']) { $this->logFeatureUsage('action=edit&watch'); $watch = true; } elseif ($params['unwatch']) { $this->logFeatureUsage('action=edit&unwatch'); $watch = false; } if ($watch) { $requestArray['wpWatchthis'] = ''; } // Apply change tags if (count($params['tags'])) { if ($user->isAllowed('applychangetags')) { $requestArray['wpChangeTags'] = implode(',', $params['tags']); } else { $this->dieUsage('You don\'t have permission to set change tags.', 'taggingnotallowed'); } } // Pass through anything else we might have been given, to support extensions // This is kind of a hack but it's the best we can do to make extensions work $requestArray += $this->getRequest()->getValues(); global $wgTitle, $wgRequest; $req = new DerivativeRequest($this->getRequest(), $requestArray, true); // Some functions depend on $wgTitle == $ep->mTitle // TODO: Make them not or check if they still do $wgTitle = $titleObj; $articleContext = new RequestContext(); $articleContext->setRequest($req); $articleContext->setWikiPage($pageObj); $articleContext->setUser($this->getUser()); /** @var $articleObject Article */ $articleObject = Article::newFromWikiPage($pageObj, $articleContext); $ep = new EditPage($articleObject); $ep->setApiEditOverride(true); $ep->setContextTitle($titleObj); $ep->importFormData($req); $content = $ep->textbox1; // The following is needed to give the hook the full content of the // new revision rather than just the current section. (Bug 52077) if (!is_null($params['section']) && $contentHandler->supportsSections() && $titleObj->exists()) { // If sectiontitle is set, use it, otherwise use the summary as the section title (for // backwards compatibility with old forms/bots). if ($ep->sectiontitle !== '') { $sectionTitle = $ep->sectiontitle; } else { $sectionTitle = $ep->summary; } $contentObj = $contentHandler->unserializeContent($content, $contentFormat); $fullContentObj = $articleObject->replaceSectionContent($params['section'], $contentObj, $sectionTitle); if ($fullContentObj) { $content = $fullContentObj->serialize($contentFormat); } else { // This most likely means we have an edit conflict which means that the edit // wont succeed anyway. $this->dieUsageMsg('editconflict'); } } // Run hooks // Handle APIEditBeforeSave parameters $r = array(); if (!Hooks::run('APIEditBeforeSave', array($ep, $content, &$r))) { if (count($r)) { $r['result'] = 'Failure'; $apiResult->addValue(null, $this->getModuleName(), $r); return; } $this->dieUsageMsg('hookaborted'); } // Do the actual save $oldRevId = $articleObject->getRevIdFetched(); $result = null; // Fake $wgRequest for some hooks inside EditPage // @todo FIXME: This interface SUCKS $oldRequest = $wgRequest; $wgRequest = $req; $status = $ep->attemptSave($result); $wgRequest = $oldRequest; switch ($status->value) { case EditPage::AS_HOOK_ERROR: case EditPage::AS_HOOK_ERROR_EXPECTED: if (isset($status->apiHookResult)) { $r = $status->apiHookResult; $r['result'] = 'Failure'; $apiResult->addValue(null, $this->getModuleName(), $r); return; } else { $this->dieUsageMsg('hookaborted'); } case EditPage::AS_PARSE_ERROR: $this->dieUsage($status->getMessage(), 'parseerror'); case EditPage::AS_IMAGE_REDIRECT_ANON: $this->dieUsageMsg('noimageredirect-anon'); case EditPage::AS_IMAGE_REDIRECT_LOGGED: $this->dieUsageMsg('noimageredirect-logged'); case EditPage::AS_SPAM_ERROR: $this->dieUsageMsg(array('spamdetected', $result['spam'])); case EditPage::AS_BLOCKED_PAGE_FOR_USER: $this->dieUsage('You have been blocked from editing', 'blocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock()))); case EditPage::AS_MAX_ARTICLE_SIZE_EXCEEDED: case EditPage::AS_CONTENT_TOO_BIG: $this->dieUsageMsg(array('contenttoobig', $this->getConfig()->get('MaxArticleSize'))); case EditPage::AS_READ_ONLY_PAGE_ANON: $this->dieUsageMsg('noedit-anon'); case EditPage::AS_READ_ONLY_PAGE_LOGGED: $this->dieUsageMsg('noedit'); case EditPage::AS_READ_ONLY_PAGE: $this->dieReadOnly(); case EditPage::AS_RATE_LIMITED: $this->dieUsageMsg('actionthrottledtext'); case EditPage::AS_ARTICLE_WAS_DELETED: $this->dieUsageMsg('wasdeleted'); case EditPage::AS_NO_CREATE_PERMISSION: $this->dieUsageMsg('nocreate-loggedin'); case EditPage::AS_NO_CHANGE_CONTENT_MODEL: $this->dieUsageMsg('cantchangecontentmodel'); case EditPage::AS_BLANK_ARTICLE: $this->dieUsageMsg('blankpage'); case EditPage::AS_CONFLICT_DETECTED: $this->dieUsageMsg('editconflict'); case EditPage::AS_TEXTBOX_EMPTY: $this->dieUsageMsg('emptynewsection'); case EditPage::AS_CHANGE_TAG_ERROR: $this->dieStatus($status); case EditPage::AS_SUCCESS_NEW_ARTICLE: $r['new'] = true; // fall-through // fall-through case EditPage::AS_SUCCESS_UPDATE: $r['result'] = 'Success'; $r['pageid'] = intval($titleObj->getArticleID()); $r['title'] = $titleObj->getPrefixedText(); $r['contentmodel'] = $articleObject->getContentModel(); $newRevId = $articleObject->getLatest(); if ($newRevId == $oldRevId) { $r['nochange'] = true; } else { $r['oldrevid'] = intval($oldRevId); $r['newrevid'] = intval($newRevId); $r['newtimestamp'] = wfTimestamp(TS_ISO_8601, $pageObj->getTimestamp()); } break; case EditPage::AS_SUMMARY_NEEDED: // Shouldn't happen since we set wpIgnoreBlankSummary, but just in case $this->dieUsageMsg('summaryrequired'); case EditPage::AS_END: default: // $status came from WikiPage::doEdit() $errors = $status->getErrorsArray(); $this->dieUsageMsg($errors[0]); // TODO: Add new errors to message map break; } $apiResult->addValue(null, $this->getModuleName(), $r); }
/** * @depends testAutoMerge */ public function testCheckDirectEditingDisallowed_forNonTextContent() { $title = Title::newFromText('Dummy:NonTextPageForEditPage'); $page = WikiPage::factory($title); $article = new Article($title); $article->getContext()->setTitle($title); $ep = new EditPage($article); $ep->setContextTitle($title); $user = $GLOBALS['wgUser']; $edit = ['wpTextbox1' => serialize('non-text content'), 'wpEditToken' => $user->getEditToken(), 'wpEdittime' => '', 'wpStarttime' => wfTimestampNow()]; $req = new FauxRequest($edit, true); $ep->importFormData($req); $this->setExpectedException('MWException', 'This content model is not supported: testing'); $ep->internalAttemptSave($result, false); }
public function execute() { $user = $this->getUser(); $params = $this->extractRequestParams(); if (is_null($params['text']) && is_null($params['appendtext']) && is_null($params['prependtext']) && $params['undo'] == 0) { $this->dieUsageMsg('missingtext'); } $titleObj = Title::newFromText($params['title']); if (!$titleObj || $titleObj->isExternal()) { $this->dieUsageMsg(array('invalidtitle', $params['title'])); } $apiResult = $this->getResult(); if ($params['redirect']) { if ($titleObj->isRedirect()) { $oldTitle = $titleObj; $titles = Title::newFromRedirectArray(Revision::newFromTitle($oldTitle)->getText(Revision::FOR_THIS_USER)); // array_shift( $titles ); $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; } $apiResult->setIndexedTagName($redirValues, 'r'); $apiResult->addValue(null, 'redirects', $redirValues); } } if ($params['createonly'] && $titleObj->exists()) { $this->dieUsageMsg('createonly-exists'); } if ($params['nocreate'] && !$titleObj->exists()) { $this->dieUsageMsg('nocreate-missing'); } // Now let's check whether we're even allowed to do this $errors = $titleObj->getUserPermissionsErrors('edit', $user); if (!$titleObj->exists()) { $errors = array_merge($errors, $titleObj->getUserPermissionsErrors('create', $user)); } if (count($errors)) { $this->dieUsageMsg($errors[0]); } $articleObj = Article::newFromTitle($titleObj, $this->getContext()); $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('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('hashcheckfailed'); } // EditPage wants to parse its stuff from a WebRequest // That interface kind of sucks, but it's workable $requestArray = array('wpTextbox1' => $params['text'], 'wpEditToken' => $params['token'], 'wpIgnoreBlankSummary' => ''); if (!is_null($params['summary'])) { $requestArray['wpSummary'] = $params['summary']; } if (!is_null($params['sectiontitle'])) { $requestArray['wpSectionTitle'] = $params['sectiontitle']; } // Watch out for basetimestamp == '' // wfTimestamp() treats it as NOW, almost certainly causing an edit conflict if (!is_null($params['basetimestamp']) && $params['basetimestamp'] != '') { $requestArray['wpEdittime'] = wfTimestamp(TS_MW, $params['basetimestamp']); } else { $requestArray['wpEdittime'] = $articleObj->getTimestamp(); } if (!is_null($params['starttimestamp']) && $params['starttimestamp'] != '') { $requestArray['wpStarttime'] = wfTimestamp(TS_MW, $params['starttimestamp']); } else { $requestArray['wpStarttime'] = wfTimestampNow(); // Fake wpStartime } if ($params['minor'] || !$params['notminor'] && $user->getGlobalPreference('minordefault')) { $requestArray['wpMinoredit'] = ''; } if ($params['recreate']) { $requestArray['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"); } $requestArray['wpSection'] = $params['section']; } else { $requestArray['wpSection'] = ''; } $watch = $this->getWatchlistValue($params['watchlist'], $titleObj); // Deprecated parameters if ($params['watch']) { $watch = true; } elseif ($params['unwatch']) { $watch = false; } if ($watch) { $requestArray['wpWatchthis'] = ''; } global $wgTitle, $wgRequest; $req = new DerivativeRequest($this->getRequest(), $requestArray, true); // Some functions depend on $wgTitle == $ep->mTitle // TODO: Make them not or check if they still do $wgTitle = $titleObj; $ep = new EditPage($articleObj); $ep->setContextTitle($titleObj); $ep->importFormData($req); // Run hooks // Handle APIEditBeforeSave parameters $r = array(); if (!wfRunHooks('APIEditBeforeSave', array($ep, $ep->textbox1, &$r))) { if (count($r)) { $r['result'] = 'Failure'; $apiResult->addValue(null, $this->getModuleName(), $r); return; } else { $this->dieUsageMsg('hookaborted'); } } // Do the actual save $oldRevId = $articleObj->getRevIdFetched(); $result = null; // Fake $wgRequest for some hooks inside EditPage // @todo FIXME: This interface SUCKS $oldRequest = $wgRequest; $wgRequest = $req; $status = $ep->internalAttemptSave($result, $user->isAllowed('bot') && $params['bot']); $wgRequest = $oldRequest; global $wgMaxArticleSize; switch ($status->value) { case EditPage::AS_HOOK_ERROR: case EditPage::AS_HOOK_ERROR_EXPECTED: $this->dieUsageMsg('hookaborted'); case EditPage::AS_IMAGE_REDIRECT_ANON: $this->dieUsageMsg('noimageredirect-anon'); case EditPage::AS_IMAGE_REDIRECT_LOGGED: $this->dieUsageMsg('noimageredirect-logged'); case EditPage::AS_SPAM_ERROR: $this->dieUsageMsg(array('spamdetected', $result['spam'])); case EditPage::AS_FILTERING: $this->dieUsageMsg('filtered'); case EditPage::AS_BLOCKED_PAGE_FOR_USER: $this->dieUsageMsg('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('noedit-anon'); case EditPage::AS_READ_ONLY_PAGE_LOGGED: $this->dieUsageMsg('noedit'); case EditPage::AS_READ_ONLY_PAGE: $this->dieReadOnly(); case EditPage::AS_RATE_LIMITED: $this->dieUsageMsg('actionthrottledtext'); case EditPage::AS_ARTICLE_WAS_DELETED: $this->dieUsageMsg('wasdeleted'); case EditPage::AS_NO_CREATE_PERMISSION: $this->dieUsageMsg('nocreate-loggedin'); case EditPage::AS_BLANK_ARTICLE: $this->dieUsageMsg('blankpage'); case EditPage::AS_CONFLICT_DETECTED: $this->dieUsageMsg('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('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(); $newRevId = $articleObj->getLatest(); if ($newRevId == $oldRevId) { $r['nochange'] = ''; } else { $r['oldrevid'] = intval($oldRevId); $r['newrevid'] = intval($newRevId); $r['newtimestamp'] = wfTimestamp(TS_ISO_8601, $articleObj->getTimestamp()); wfRunHooks('ApiEditPage::SuccessfulApiEdit', [$newRevId]); wfSetupSession(); } break; case EditPage::AS_SUMMARY_NEEDED: $this->dieUsageMsg('summaryrequired'); case EditPage::AS_END: // $status came from WikiPage::doEdit() $errors = $status->getErrorsArray(); $this->dieUsageMsg($errors[0]); // TODO: Add new errors to message map break; default: if (is_string($status->value) && strlen($status->value)) { $this->dieUsage("An unknown return value was returned by Editpage. The code returned was \"{$status->value}\"", $status->value); } else { $this->dieUsageMsg(array('unknownerror', $status->value)); } } $apiResult->addValue(null, $this->getModuleName(), $r); }
/** * Performs an edit and checks the result. * * @param string|Title $title The title of the page to edit * @param string|null $baseText Some text to create the page with before attempting the edit. * @param User|string|null $user The user to perform the edit as. * @param array $edit An array of request parameters used to define the edit to perform. * Some well known fields are: * * wpTextbox1: the text to submit * * wpSummary: the edit summary * * wpEditToken: the edit token (will be inserted if not provided) * * wpEdittime: timestamp of the edit's base revision (will be inserted * if not provided) * * wpStarttime: timestamp when the edit started (will be inserted if not provided) * * wpSectionTitle: the section to edit * * wpMinorEdit: mark as minor edit * * wpWatchthis: whether to watch the page * @param int|null $expectedCode The expected result code (EditPage::AS_XXX constants). * Set to null to skip the check. * @param string|null $expectedText The text expected to be on the page after the edit. * Set to null to skip the check. * @param string|null $message An optional message to show along with any error message. * * @return WikiPage The page that was just edited, useful for getting the edit's rev_id, etc. */ protected function assertEdit($title, $baseText, $user = null, array $edit, $expectedCode = null, $expectedText = null, $message = null) { if (is_string($title)) { $ns = $this->getDefaultWikitextNS(); $title = Title::newFromText($title, $ns); } $this->assertNotNull($title); if (is_string($user)) { $user = User::newFromName($user); if ($user->getId() === 0) { $user->addToDatabase(); } } $page = WikiPage::factory($title); if ($baseText !== null) { $content = ContentHandler::makeContent($baseText, $title); $page->doEditContent($content, "base text for test"); $this->forceRevisionDate($page, '20120101000000'); //sanity check $page->clear(); $currentText = ContentHandler::getContentText($page->getContent()); # EditPage rtrim() the user input, so we alter our expected text # to reflect that. $this->assertEditedTextEquals($baseText, $currentText); } if ($user == null) { $user = $GLOBALS['wgUser']; } else { $this->setMwGlobals('wgUser', $user); } if (!isset($edit['wpEditToken'])) { $edit['wpEditToken'] = $user->getEditToken(); } if (!isset($edit['wpEdittime'])) { $edit['wpEdittime'] = $page->exists() ? $page->getTimestamp() : ''; } if (!isset($edit['wpStarttime'])) { $edit['wpStarttime'] = wfTimestampNow(); } $req = new FauxRequest($edit, true); // session ?? $article = new Article($title); $article->getContext()->setTitle($title); $ep = new EditPage($article); $ep->setContextTitle($title); $ep->importFormData($req); $bot = isset($edit['bot']) ? (bool) $edit['bot'] : false; // this is where the edit happens! // Note: don't want to use EditPage::AttemptSave, because it messes with $wgOut // and throws exceptions like PermissionsError $status = $ep->internalAttemptSave($result, $bot); if ($expectedCode !== null) { // check edit code $this->assertEquals($expectedCode, $status->value, "Expected result code mismatch. {$message}"); } $page = WikiPage::factory($title); if ($expectedText !== null) { // check resulting page text $content = $page->getContent(); $text = ContentHandler::getContentText($content); # EditPage rtrim() the user input, so we alter our expected text # to reflect that. $this->assertEditedTextEquals($expectedText, $text, "Expected article text mismatch. {$message}"); } return $page; }
public function execute() { $user = $this->getUser(); $params = $this->extractRequestParams(); if (is_null($params['text']) && is_null($params['appendtext']) && is_null($params['prependtext']) && $params['undo'] == 0) { $this->dieUsageMsg('missingtext'); } $pageObj = $this->getTitleOrPageId($params); $titleObj = $pageObj->getTitle(); $apiResult = $this->getResult(); if ($params['redirect']) { if ($titleObj->isRedirect()) { $oldTitle = $titleObj; $titles = Revision::newFromTitle($oldTitle, false, Revision::READ_LATEST)->getContent(Revision::FOR_THIS_USER, $user)->getRedirectChain(); // array_shift( $titles ); $redirValues = array(); /** @var $newTitle Title */ 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; } $apiResult->setIndexedTagName($redirValues, 'r'); $apiResult->addValue(null, 'redirects', $redirValues); // Since the page changed, update $pageObj $pageObj = WikiPage::factory($titleObj); } } if (!isset($params['contentmodel']) || $params['contentmodel'] == '') { $contentHandler = $pageObj->getContentHandler(); } else { $contentHandler = ContentHandler::getForModelID($params['contentmodel']); } // @todo ask handler whether direct editing is supported at all! make allowFlatEdit() method or some such if (!isset($params['contentformat']) || $params['contentformat'] == '') { $params['contentformat'] = $contentHandler->getDefaultFormat(); } $contentFormat = $params['contentformat']; if (!$contentHandler->isSupportedFormat($contentFormat)) { $name = $titleObj->getPrefixedDBkey(); $model = $contentHandler->getModelID(); $this->dieUsage("The requested format {$contentFormat} is not supported for content model " . " {$model} used by {$name}", 'badformat'); } if ($params['createonly'] && $titleObj->exists()) { $this->dieUsageMsg('createonly-exists'); } if ($params['nocreate'] && !$titleObj->exists()) { $this->dieUsageMsg('nocreate-missing'); } // Now let's check whether we're even allowed to do this $errors = $titleObj->getUserPermissionsErrors('edit', $user); if (!$titleObj->exists()) { $errors = array_merge($errors, $titleObj->getUserPermissionsErrors('create', $user)); } if (count($errors)) { $this->dieUsageMsg($errors[0]); } $toMD5 = $params['text']; if (!is_null($params['appendtext']) || !is_null($params['prependtext'])) { $content = $pageObj->getContent(); if (!$content) { if ($titleObj->getNamespace() == NS_MEDIAWIKI) { # If this is a MediaWiki:x message, then load the messages # and return the message value for x. $text = $titleObj->getDefaultMessageText(); if ($text === false) { $text = ''; } try { $content = ContentHandler::makeContent($text, $this->getTitle()); } catch (MWContentSerializationException $ex) { $this->dieUsage($ex->getMessage(), 'parseerror'); return; } } else { # Otherwise, make a new empty content. $content = $contentHandler->makeEmptyContent(); } } // @todo Add support for appending/prepending to the Content interface if (!$content instanceof TextContent) { $mode = $contentHandler->getModelID(); $this->dieUsage("Can't append to pages using content model {$mode}", 'appendnotsupported'); } if (!is_null($params['section'])) { if (!$contentHandler->supportsSections()) { $modelName = $contentHandler->getModelID(); $this->dieUsage("Sections are not supported for this content model: {$modelName}.", 'sectionsnotsupported'); } // Process the content for section edits $section = intval($params['section']); $content = $content->getSection($section); if (!$content) { $this->dieUsage("There is no section {$section}.", 'nosuchsection'); } } if (!$content) { $text = ''; } else { $text = $content->serialize($contentFormat); } $params['text'] = $params['prependtext'] . $text . $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() != $pageObj->getID()) { $this->dieUsageMsg(array('revwrongpage', $undoRev->getID(), $titleObj->getPrefixedText())); } if ($undoafterRev->getPage() != $pageObj->getID()) { $this->dieUsageMsg(array('revwrongpage', $undoafterRev->getID(), $titleObj->getPrefixedText())); } $newContent = $contentHandler->getUndoContent($pageObj->getRevision(), $undoRev, $undoafterRev); if (!$newContent) { $this->dieUsageMsg('undo-failure'); } $params['text'] = $newContent->serialize($params['contentformat']); // 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'] = wfMessage('undo-summary', $params['undo'], $undoRev->getUserText())->inContentLanguage()->text(); } } // See if the MD5 hash checks out if (!is_null($params['md5']) && md5($toMD5) !== $params['md5']) { $this->dieUsageMsg('hashcheckfailed'); } // EditPage wants to parse its stuff from a WebRequest // That interface kind of sucks, but it's workable $requestArray = array('wpTextbox1' => $params['text'], 'format' => $contentFormat, 'model' => $contentHandler->getModelID(), 'wpEditToken' => $params['token'], 'wpIgnoreBlankSummary' => ''); if (!is_null($params['summary'])) { $requestArray['wpSummary'] = $params['summary']; } if (!is_null($params['sectiontitle'])) { $requestArray['wpSectionTitle'] = $params['sectiontitle']; } // TODO: Pass along information from 'undoafter' as well if ($params['undo'] > 0) { $requestArray['wpUndidRevision'] = $params['undo']; } // Watch out for basetimestamp == '' // wfTimestamp() treats it as NOW, almost certainly causing an edit conflict if (!is_null($params['basetimestamp']) && $params['basetimestamp'] != '') { $requestArray['wpEdittime'] = wfTimestamp(TS_MW, $params['basetimestamp']); } else { $requestArray['wpEdittime'] = $pageObj->getTimestamp(); } if (!is_null($params['starttimestamp']) && $params['starttimestamp'] != '') { $requestArray['wpStarttime'] = wfTimestamp(TS_MW, $params['starttimestamp']); } else { $requestArray['wpStarttime'] = wfTimestampNow(); // Fake wpStartime } if ($params['minor'] || !$params['notminor'] && $user->getOption('minordefault')) { $requestArray['wpMinoredit'] = ''; } if ($params['recreate']) { $requestArray['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"); } $requestArray['wpSection'] = $params['section']; } else { $requestArray['wpSection'] = ''; } $watch = $this->getWatchlistValue($params['watchlist'], $titleObj); // Deprecated parameters if ($params['watch']) { $watch = true; } elseif ($params['unwatch']) { $watch = false; } if ($watch) { $requestArray['wpWatchthis'] = ''; } global $wgTitle, $wgRequest; $req = new DerivativeRequest($this->getRequest(), $requestArray, true); // Some functions depend on $wgTitle == $ep->mTitle // TODO: Make them not or check if they still do $wgTitle = $titleObj; $articleContext = new RequestContext(); $articleContext->setRequest($req); $articleContext->setWikiPage($pageObj); $articleContext->setUser($this->getUser()); /** @var $articleObject Article */ $articleObject = Article::newFromWikiPage($pageObj, $articleContext); $ep = new EditPage($articleObject); // allow editing of non-textual content. $ep->allowNonTextContent = true; $ep->setContextTitle($titleObj); $ep->importFormData($req); // Run hooks // Handle APIEditBeforeSave parameters $r = array(); if (!wfRunHooks('APIEditBeforeSave', array($ep, $ep->textbox1, &$r))) { if (count($r)) { $r['result'] = 'Failure'; $apiResult->addValue(null, $this->getModuleName(), $r); return; } else { $this->dieUsageMsg('hookaborted'); } } // Do the actual save $oldRevId = $articleObject->getRevIdFetched(); $result = null; // Fake $wgRequest for some hooks inside EditPage // @todo FIXME: This interface SUCKS $oldRequest = $wgRequest; $wgRequest = $req; $status = $ep->internalAttemptSave($result, $user->isAllowed('bot') && $params['bot']); $wgRequest = $oldRequest; global $wgMaxArticleSize; switch ($status->value) { case EditPage::AS_HOOK_ERROR: case EditPage::AS_HOOK_ERROR_EXPECTED: $this->dieUsageMsg('hookaborted'); case EditPage::AS_PARSE_ERROR: $this->dieUsage($status->getMessage(), 'parseerror'); case EditPage::AS_IMAGE_REDIRECT_ANON: $this->dieUsageMsg('noimageredirect-anon'); case EditPage::AS_IMAGE_REDIRECT_LOGGED: $this->dieUsageMsg('noimageredirect-logged'); case EditPage::AS_SPAM_ERROR: $this->dieUsageMsg(array('spamdetected', $result['spam'])); case EditPage::AS_BLOCKED_PAGE_FOR_USER: $this->dieUsageMsg('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('noedit-anon'); case EditPage::AS_READ_ONLY_PAGE_LOGGED: $this->dieUsageMsg('noedit'); case EditPage::AS_READ_ONLY_PAGE: $this->dieReadOnly(); case EditPage::AS_RATE_LIMITED: $this->dieUsageMsg('actionthrottledtext'); case EditPage::AS_ARTICLE_WAS_DELETED: $this->dieUsageMsg('wasdeleted'); case EditPage::AS_NO_CREATE_PERMISSION: $this->dieUsageMsg('nocreate-loggedin'); case EditPage::AS_BLANK_ARTICLE: $this->dieUsageMsg('blankpage'); case EditPage::AS_CONFLICT_DETECTED: $this->dieUsageMsg('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('emptynewsection'); case EditPage::AS_SUCCESS_NEW_ARTICLE: $r['new'] = ''; // fall-through // fall-through case EditPage::AS_SUCCESS_UPDATE: $r['result'] = 'Success'; $r['pageid'] = intval($titleObj->getArticleID()); $r['title'] = $titleObj->getPrefixedText(); $r['contentmodel'] = $titleObj->getContentModel(); $newRevId = $articleObject->getLatest(); if ($newRevId == $oldRevId) { $r['nochange'] = ''; } else { $r['oldrevid'] = intval($oldRevId); $r['newrevid'] = intval($newRevId); $r['newtimestamp'] = wfTimestamp(TS_ISO_8601, $pageObj->getTimestamp()); } break; case EditPage::AS_SUMMARY_NEEDED: $this->dieUsageMsg('summaryrequired'); case EditPage::AS_END: default: // $status came from WikiPage::doEdit() $errors = $status->getErrorsArray(); $this->dieUsageMsg($errors[0]); // TODO: Add new errors to message map break; } $apiResult->addValue(null, $this->getModuleName(), $r); }
protected function doStore(EditPage $editor) { $title = $editor->getTitle(); // If they used redlink=1 and the page exists, redirect to the main article and send notice if ($this->getRequest()->getBool('redlink') && $title->exists()) { $this->logMessage(wfMessage('sf_autoedit_redlinkexists')->parse(), self::WARNING); } $permErrors = $title->getUserPermissionsErrors('edit', $this->getUser()); // if this title needs to be created, user needs create rights if (!$title->exists()) { $permErrors = array_merge($permErrors, wfArrayDiff2($title->getUserPermissionsErrors('create', $this->getUser()), $permErrors)); } if ($permErrors) { // Auto-block user's IP if the account was "hard" blocked $this->getUser()->spreadAnyEditBlock(); foreach ($permErrors as $error) { $this->logMessage(call_user_func_array('wfMessage', $error)->parse()); } return; } $resultDetails = false; # Allow bots to exempt some edits from bot flagging $bot = $this->getUser()->isAllowed('bot') && $editor->bot; $request = $editor->sfFauxRequest; if ($editor->tokenOk($request)) { $ctx = RequestContext::getMain(); $tempTitle = $ctx->getTitle(); $ctx->setTitle($title); $status = $editor->internalAttemptSave($resultDetails, $bot); $ctx->setTitle($tempTitle); } else { throw new MWException(wfMessage('session_fail_preview')->parse()); } switch ($status->value) { case EditPage::AS_HOOK_ERROR_EXPECTED: // A hook function returned an error // show normal Edit page // remove Preview and Diff standard buttons from editor page Hooks::register('EditPageBeforeEditButtons', function (&$editor, &$buttons, &$tabindex) { foreach (array_keys($buttons) as $key) { if ($key !== 'save') { unset($buttons[$key]); } } }); // Context title needed for correct Cancel link $editor->setContextTitle($title); $editor->showEditForm(); return false; // success // success case EditPage::AS_CONTENT_TOO_BIG: // Content too big (> $wgMaxArticleSize) // Content too big (> $wgMaxArticleSize) case EditPage::AS_ARTICLE_WAS_DELETED: // article was deleted while editting and param wpRecreate == false or form was not posted // article was deleted while editting and param wpRecreate == false or form was not posted case EditPage::AS_CONFLICT_DETECTED: // (non-resolvable) edit conflict // (non-resolvable) edit conflict case EditPage::AS_SUMMARY_NEEDED: // no edit summary given and the user has forceeditsummary set and the user is not editting in his own userspace or talkspace and wpIgnoreBlankSummary == false // no edit summary given and the user has forceeditsummary set and the user is not editting in his own userspace or talkspace and wpIgnoreBlankSummary == false case EditPage::AS_TEXTBOX_EMPTY: // user tried to create a new section without content // user tried to create a new section without content case EditPage::AS_MAX_ARTICLE_SIZE_EXCEEDED: // article is too big (> $wgMaxArticleSize), after merging in the new section // article is too big (> $wgMaxArticleSize), after merging in the new section case EditPage::AS_END: // WikiPage::doEdit() was unsuccessfull throw new MWException(wfMessage('sf_autoedit_fail', $this->mOptions['target'])->parse()); case EditPage::AS_HOOK_ERROR: // Article update aborted by a hook function $this->logMessage('Article update aborted by a hook function', self::DEBUG); return false; // success // TODO: This error code only exists from 1.21 onwards. It is // suitably handled by the default branch, but really should get its // own branch. Uncomment once compatibility to pre1.21 is dropped. // case EditPage::AS_PARSE_ERROR: // can't parse content // // throw new MWException( $status->getHTML() ); // return true; // fail // success // TODO: This error code only exists from 1.21 onwards. It is // suitably handled by the default branch, but really should get its // own branch. Uncomment once compatibility to pre1.21 is dropped. // case EditPage::AS_PARSE_ERROR: // can't parse content // // throw new MWException( $status->getHTML() ); // return true; // fail case EditPage::AS_SUCCESS_NEW_ARTICLE: // Article successfully created $query = $resultDetails['redirect'] ? 'redirect=no' : ''; $anchor = isset($resultDetails['sectionanchor']) ? $resultDetails['sectionanchor'] : ''; $this->getOutput()->redirect($title->getFullURL($query) . $anchor); $this->getResult()->addValue(NULL, 'redirect', $title->getFullURL($query) . $anchor); return false; // success // success case EditPage::AS_SUCCESS_UPDATE: // Article successfully updated $extraQuery = ''; $sectionanchor = $resultDetails['sectionanchor']; // Give extensions a chance to modify URL query on update Hooks::run('ArticleUpdateBeforeRedirect', array($editor->getArticle(), &$sectionanchor, &$extraQuery)); if ($resultDetails['redirect']) { if ($extraQuery == '') { $extraQuery = 'redirect=no'; } else { $extraQuery = 'redirect=no&' . $extraQuery; } } $this->getOutput()->redirect($title->getFullURL($extraQuery) . $sectionanchor); $this->getResult()->addValue(NULL, 'redirect', $title->getFullURL($extraQuery) . $sectionanchor); return false; // success // success case EditPage::AS_BLANK_ARTICLE: // user tried to create a blank page $this->logMessage('User tried to create a blank page', self::DEBUG); $this->getOutput()->redirect($editor->getContextTitle()->getFullURL()); $this->getResult()->addValue(NULL, 'redirect', $editor->getContextTitle()->getFullURL()); return false; // success // success case EditPage::AS_SPAM_ERROR: // summary contained spam according to one of the regexes in $wgSummarySpamRegex $match = $resultDetails['spam']; if (is_array($match)) { $match = $this->getLanguage()->listToText($match); } throw new MWException(wfMessage('spamprotectionmatch', wfEscapeWikiText($match))->parse()); // FIXME: Include better error message // FIXME: Include better error message case EditPage::AS_BLOCKED_PAGE_FOR_USER: // User is blocked from editting editor page throw new UserBlockedError($this->getUser()->getBlock()); case EditPage::AS_IMAGE_REDIRECT_ANON: // anonymous user is not allowed to upload (User::isAllowed('upload') == false) // anonymous user is not allowed to upload (User::isAllowed('upload') == false) case EditPage::AS_IMAGE_REDIRECT_LOGGED: // logged in user is not allowed to upload (User::isAllowed('upload') == false) throw new PermissionsError('upload'); case EditPage::AS_READ_ONLY_PAGE_ANON: // editor anonymous user is not allowed to edit editor page // editor anonymous user is not allowed to edit editor page case EditPage::AS_READ_ONLY_PAGE_LOGGED: // editor logged in user is not allowed to edit editor page throw new PermissionsError('edit'); case EditPage::AS_READ_ONLY_PAGE: // wiki is in readonly mode (wfReadOnly() == true) throw new ReadOnlyError(); case EditPage::AS_RATE_LIMITED: // rate limiter for action 'edit' was tripped throw new ThrottledError(); case EditPage::AS_NO_CREATE_PERMISSION: // user tried to create editor page, but is not allowed to do that ( Title->usercan('create') == false ) $permission = $title->isTalkPage() ? 'createtalk' : 'createpage'; throw new PermissionsError($permission); default: // We don't recognize $status->value. The only way that can happen // is if an extension hook aborted from inside ArticleSave. // Render the status object into $editor->hookError $editor->hookError = '<div class="error">' . $status->getWikitext() . '</div>'; throw new MWException($status->getHTML()); } }
public function execute($par) { global $wgUser, $wgRequest, $wgOut, $wgCommentboxNamespaces; if (!$wgRequest->wasPosted()) { $wgOut->redirect(Title::newMainPage()->getFullURL()); return; } $this->setHeaders(); $Pagename = $wgRequest->getText('wpPageName'); $Author = $wgRequest->getText('wpAuthor', ''); $Comment = $wgRequest->getText('wpComment', ''); $title = Title::newFromText($Pagename); if ($title == NULL || !$title->exists()) { $this->fail('commentbox-error-page-nonexistent'); return; } if (!array_key_exists($title->getNamespace(), $wgCommentboxNamespaces) || !$wgCommentboxNamespaces[$title->getNamespace()]) { $this->fail('commentbox-error-namespace', $title); return; } if ($Comment == '' || $Comment == wfMsgNoTrans('commentbox-prefill')) { $this->fail('commentbox-error-empty-comment', $title); return; } if (!$title->userCan('edit')) { $this->displayRestrictionError(); return; } // TODO: Integrate with SpamBlacklist etc. // Currently, no http/https-links are allowed at all $matches = array(); if (preg_match('@https?://[-.\\w]+@', $Comment, $matches) || preg_match('@https?://[-.\\w]+@', $Author, $matches)) { $wgOut->setPageTitle(wfMsg('spamprotectiontitle')); $wgOut->setRobotPolicy('noindex,nofollow'); $wgOut->setArticleRelated(false); $wgOut->addWikiMsg('spamprotectiontext'); $wgOut->addWikiMsg('spamprotectionmatch', "<nowiki>{$matches[0]}</nowiki>"); $wgOut->returnToMain(false, $title); return; } $article = new Article($title); $text = $article->getContent(); $subject = ''; if (!preg_match(wfMsgForContentNoTrans('commentbox-regex'), $text)) { $subject = wfMsgForContent('commentbox-first-comment-heading') . "\n"; } $sig = $wgUser->isLoggedIn() ? "-- ~~~~" : "-- {$Author} ~~~~~"; // Append <br /> after each newline, except if the user started a new paragraph $Comment = preg_replace('/(?<!\\n)\\n(?!\\n)/', "<br />\n", $Comment); $text .= "\n\n" . $subject . $Comment . "\n<br />" . $sig; $reqArr = array('wpTextbox1' => $text, 'wpSummary' => wfMsgForContent('commentbox-log'), 'wpEditToken' => $wgUser->editToken(), 'wpIgnoreBlankSummary' => '', 'wpStarttime' => wfTimestampNow(), 'wpEdittime' => $article->getTimestamp()); $request = new FauxRequest($reqArr, true); $ep = new EditPage($article); $ep->setContextTitle($title); $ep->importFormData($request); $details = array(); // Passed by ref $status = $ep->internalAttemptSave($details); $retval = $status->value; switch ($retval) { case EditPage::AS_SUCCESS_UPDATE: $wgOut->redirect($title->getFullURL()); break; case EditPage::AS_SPAM_ERROR: $ep->spamPageWithContent($details['spam']); break; case EditPage::AS_BLOCKED_PAGE_FOR_USER: $wgOut->blockedPage(); break; case EditPage::AS_READ_ONLY_PAGE_ANON: case EditPage::AS_READ_ONLY_PAGE_LOGGED: $wgOut->permissionRequired('edit'); break; case EditPage::AS_READ_ONLY_PAGE: $wgOut->readOnlyPage(); } }