From e0aae6a65895998319705b72a6967bf929ea8478 Mon Sep 17 00:00:00 2001 From: Roan Kattouw Date: Mon, 3 Mar 2008 18:08:40 +0000 Subject: [PATCH] API: Adding action=edit module --- RELEASE-NOTES | 1 + docs/hooks.txt | 8 ++ includes/AutoLoader.php | 1 + includes/api/ApiEditPage.php | 252 +++++++++++++++++++++++++++++++++++ includes/api/ApiMain.php | 1 + 5 files changed, 263 insertions(+) create mode 100644 includes/api/ApiEditPage.php diff --git a/RELEASE-NOTES b/RELEASE-NOTES index c6ae9bc600..86016dd3dd 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -72,6 +72,7 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN * (bug 12394) Added rctitles parameter to list=recentchanges, making rcid retrieval easier * (bug 13218) Fix inclusion of " character in hyperlinks * Added watch and unwatch parameters to action=delete and action=move +* Added action=edit === Languages updated in 1.13 === diff --git a/docs/hooks.txt b/docs/hooks.txt index db132f7f16..979e8abb8b 100644 --- a/docs/hooks.txt +++ b/docs/hooks.txt @@ -268,6 +268,14 @@ before showing the edit form ( EditPage::edit() ). This is triggered on &action=edit. $EditPage : the EditPage object +'APIEditBeforeSave': before saving a page with api.php?action=edit, +after processing request parameters. Return false to let the request +fail, returning an error message or an tag +if $resultArr was filled. +$EditPage : the EditPage object +$text : the new text of the article (has yet to be saved) +$resultArr : data in this array will be added to the API result + 'ArticleAfterFetchContent': after fetching content of an article from the database $article: the article (object) being loaded from the database $content: the content (string) of the article diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 2e2083b2a2..8ac63ff9c1 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -370,6 +370,7 @@ function __autoload($className) { #'ApiChangeRights' => 'includes/api/ApiChangeRights.php', # Disabled for now 'ApiDelete' => 'includes/api/ApiDelete.php', + 'ApiEditPage' => 'includes/api/ApiEditPage.php', 'ApiMove' => 'includes/api/ApiMove.php', 'ApiProtect' => 'includes/api/ApiProtect.php', 'ApiQueryBlocks' => 'includes/api/ApiQueryBlocks.php', diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php new file mode 100644 index 0000000000..b9a1120c72 --- /dev/null +++ b/includes/api/ApiEditPage.php @@ -0,0 +1,252 @@ +@gmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * http://www.gnu.org/copyleft/gpl.html + */ + +if (!defined('MEDIAWIKI')) { + // Eclipse helper - will be ignored in production + require_once ("ApiBase.php"); +} + +/** + * A query module to list all external URLs found on a given set of pages. + * + * @addtogroup API + */ +class ApiEditPage extends ApiBase { + + public function __construct($query, $moduleName) { + parent :: __construct($query, $moduleName); + } + + public function execute() { + global $wgUser; + $this->getMain()->requestWriteMode(); + + $params = $this->extractRequestParams(); + if(is_null($params['title'])) + $this->dieUsageMsg(array('missingparam', 'title')); + if(is_null($params['text'])) + $this->dieUsageMsg(array('missingparam', 'text')); + if(is_null($params['token'])) + $this->dieUsageMsg(array('missingparam', 'token')); + if(!$wgUser->matchEditToken($params['token'])) + $this->dieUsageMsg(array('sessionfailure')); + + $titleObj = Title::newFromText($params['title']); + if(!$titleObj) + $this->dieUsageMsg(array('invalidtitle', $params['title'])); + + // 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(!empty($errors)) + $this->dieUsageMsg($errors[0]); + + $articleObj = new Article($titleObj); + $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(); + # Fake wpStartime + $reqArr['wpStarttime'] = $reqArr['wpEdittime']; + if($params['minor'] || (!$params['notminor'] && $wgUser->getOption('minordefault'))) + $reqArr['wpMinoredit'] = ''; + if($params['recreate']) + $reqArr['wpRecreate'] = ''; + if(!is_null($params['captchaid'])) + $reqArr['wpCaptchaId'] = $params['captchaid']; + if(!is_null($params['captchaword'])) + $reqArr['wpCaptchaWord'] = $params['captchaword']; + + if($params['watch']) + $watch = true; + else if($params['unwatch']) + $watch = false; + else if($titleObj->userIsWatching()) + $watch = true; + else if($wgUser->getOption('watchdefault')) + $watch = true; + else if($wgUser->getOption('watchcreations') && !$titleObj->exists()) + $watch = true; + else + $watch = false; + if($watch) + $reqArr['wpWatchthis'] = ''; + + $req = new FauxRequest($reqArr, true); + $ep->importFormData($req); + + # Run hooks + # We need to fake $wgRequest for some of them + global $wgRequest; + $wgRequest = $req; + $r = array(); + if(!wfRunHooks('APIEditBeforeSave', array(&$ep, $ep->textbox1, &$r))) + { + if(!empty($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; + # *Something* is setting $wgTitle to a title corresponding to "Msg", + # but that breaks API mode detection through is_null($wgTitle) + global $wgTitle; + $wgTitle = null; + $retval = $ep->internalAttemptSave($result, $wgUser->isAllowed('bot') && $params['bot']); + 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: + global $wgMaxArticleSize; + $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->dieUsageMsg(array('readonlytext')); + 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_TEXTBOX_EMPTY: Can't happen since we don't do sections + case EditPage::AS_END: + # This usually means some kind of race condition + # or DB weirdness occurred. Throw an unknown error here. + $this->dieUsageMsg(array('unknownerror', 'AS_END')); + case EditPage::AS_SUCCESS_NEW_ARTICLE: + $r['new'] = ''; + case EditPage::AS_SUCCESS_UPDATE: + $r['result'] = "Success"; + $r['pageid'] = $titleObj->getArticleID(); + $r['title'] = $titleObj->getPrefixedText(); + $newRevId = $titleObj->getLatestRevId(); + if($newRevId == $oldRevId) + $r['nochange'] = ''; + else + { + $r['oldrevid'] = $oldRevId; + $r['newrevid'] = $newRevId; + } + break; + default: + $this->dieUsageMsg(array('unknownerror', $retval)); + } + $this->getResult()->addValue(null, $this->getModuleName(), $r); + } + + protected function getDescription() { + return 'Create and edit pages.'; + } + + protected function getAllowedParams() { + return array ( + 'title' => null, + 'text' => null, + 'token' => null, + 'summary' => null, + 'minor' => false, + 'notminor' => false, + 'bot' => false, + 'basetimestamp' => null, + 'recreate' => false, + 'captchaword' => null, + 'captchaid' => null, + 'watch' => false, + 'unwatch' => false, + ); + } + + protected function getParamDescription() { + return array ( + 'title' => 'Page title', + 'text' => 'Page content', + 'token' => 'Edit token. You can get one of these through prop=info', + 'summary' => 'Edit summary', + 'minor' => 'Minor edit', + 'notminor' => 'Non-minor edit', + 'bot' => 'Mark this edit as bot', + 'basetimestamp' => array('Timestamp of the base revision (gotten through prop=revisions&rvprop=timestamp).', + 'Used to detect edit conflicts; leave unset to ignore conflicts.' + ), + 'recreate' => 'Override any errors about the article having been deleted in the meantime', + 'watch' => 'Add the page to your watchlist', + 'unwatch' => 'Remove the page from your watchlist', + 'captchaid' => 'CAPTCHA ID from previous request', + 'captchaword' => 'Answer to the CAPTCHA', + ); + } + + protected function getExamples() { + return array ( + "Edit a page (anonymous user):", + " api.php?action=edit&eptitle=Test&epsummary=test%20summary&eptext=article%20content&epedittime=20070824123454&eptokenid=+%5C" + ); + } + + public function getVersion() { + return __CLASS__ . ': $Id: ApiEditPage.php 22289 2007-08-16 13:27:44Z ilabarg1 $'; + } +} \ No newline at end of file diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index 0711a245a4..0526a2488a 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -71,6 +71,7 @@ class ApiMain extends ApiBase { 'block' => 'ApiBlock', 'unblock' => 'ApiUnblock', 'move' => 'ApiMove', + 'edit' => 'ApiEditPage', #'changerights' => 'ApiChangeRights' # Disabled for now ); -- 2.20.1