MediaWiki
master
|
00001 <?php 00034 class ApiEditPage extends ApiBase { 00035 00036 public function __construct( $query, $moduleName ) { 00037 parent::__construct( $query, $moduleName ); 00038 } 00039 00040 public function execute() { 00041 $user = $this->getUser(); 00042 $params = $this->extractRequestParams(); 00043 00044 if ( is_null( $params['text'] ) && is_null( $params['appendtext'] ) && 00045 is_null( $params['prependtext'] ) && 00046 $params['undo'] == 0 ) 00047 { 00048 $this->dieUsageMsg( 'missingtext' ); 00049 } 00050 00051 $pageObj = $this->getTitleOrPageId( $params ); 00052 $titleObj = $pageObj->getTitle(); 00053 if ( $titleObj->isExternal() ) { 00054 $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) ); 00055 } 00056 00057 if ( !isset( $params['contentmodel'] ) || $params['contentmodel'] == '' ) { 00058 $contentHandler = $pageObj->getContentHandler(); 00059 } else { 00060 $contentHandler = ContentHandler::getForModelID( $params['contentmodel'] ); 00061 } 00062 00063 // @todo ask handler whether direct editing is supported at all! make allowFlatEdit() method or some such 00064 00065 if ( !isset( $params['contentformat'] ) || $params['contentformat'] == '' ) { 00066 $params['contentformat'] = $contentHandler->getDefaultFormat(); 00067 } 00068 00069 $contentFormat = $params['contentformat']; 00070 00071 if ( !$contentHandler->isSupportedFormat( $contentFormat ) ) { 00072 $name = $titleObj->getPrefixedDBkey(); 00073 $model = $contentHandler->getModelID(); 00074 00075 $this->dieUsage( "The requested format $contentFormat is not supported for content model ". 00076 " $model used by $name", 'badformat' ); 00077 } 00078 00079 $apiResult = $this->getResult(); 00080 00081 if ( $params['redirect'] ) { 00082 if ( $titleObj->isRedirect() ) { 00083 $oldTitle = $titleObj; 00084 00085 $titles = Revision::newFromTitle( $oldTitle, false, Revision::READ_LATEST ) 00086 ->getContent( Revision::FOR_THIS_USER, $user ) 00087 ->getRedirectChain(); 00088 // array_shift( $titles ); 00089 00090 $redirValues = array(); 00091 foreach ( $titles as $id => $newTitle ) { 00092 00093 if ( !isset( $titles[ $id - 1 ] ) ) { 00094 $titles[ $id - 1 ] = $oldTitle; 00095 } 00096 00097 $redirValues[] = array( 00098 'from' => $titles[ $id - 1 ]->getPrefixedText(), 00099 'to' => $newTitle->getPrefixedText() 00100 ); 00101 00102 $titleObj = $newTitle; 00103 } 00104 00105 $apiResult->setIndexedTagName( $redirValues, 'r' ); 00106 $apiResult->addValue( null, 'redirects', $redirValues ); 00107 } 00108 } 00109 00110 if ( $params['createonly'] && $titleObj->exists() ) { 00111 $this->dieUsageMsg( 'createonly-exists' ); 00112 } 00113 if ( $params['nocreate'] && !$titleObj->exists() ) { 00114 $this->dieUsageMsg( 'nocreate-missing' ); 00115 } 00116 00117 // Now let's check whether we're even allowed to do this 00118 $errors = $titleObj->getUserPermissionsErrors( 'edit', $user ); 00119 if ( !$titleObj->exists() ) { 00120 $errors = array_merge( $errors, $titleObj->getUserPermissionsErrors( 'create', $user ) ); 00121 } 00122 if ( count( $errors ) ) { 00123 $this->dieUsageMsg( $errors[0] ); 00124 } 00125 00126 $toMD5 = $params['text']; 00127 if ( !is_null( $params['appendtext'] ) || !is_null( $params['prependtext'] ) ) 00128 { 00129 $content = $pageObj->getContent(); 00130 00131 if ( !$content ) { 00132 if ( $titleObj->getNamespace() == NS_MEDIAWIKI ) { 00133 # If this is a MediaWiki:x message, then load the messages 00134 # and return the message value for x. 00135 $text = $titleObj->getDefaultMessageText(); 00136 if ( $text === false ) { 00137 $text = ''; 00138 } 00139 00140 try { 00141 $content = ContentHandler::makeContent( $text, $this->getTitle() ); 00142 } catch ( MWContentSerializationException $ex ) { 00143 $this->dieUsage( $ex->getMessage(), 'parseerror' ); 00144 return; 00145 } 00146 } else { 00147 # Otherwise, make a new empty content. 00148 $content = $contentHandler->makeEmptyContent(); 00149 } 00150 } 00151 00152 // @todo: Add support for appending/prepending to the Content interface 00153 00154 if ( !( $content instanceof TextContent ) ) { 00155 $mode = $contentHandler->getModelID(); 00156 $this->dieUsage( "Can't append to pages using content model $mode", 'appendnotsupported' ); 00157 } 00158 00159 if ( !is_null( $params['section'] ) ) { 00160 if ( !$contentHandler->supportsSections() ) { 00161 $modelName = $contentHandler->getModelID(); 00162 $this->dieUsage( "Sections are not supported for this content model: $modelName.", 'sectionsnotsupported' ); 00163 } 00164 00165 // Process the content for section edits 00166 $section = intval( $params['section'] ); 00167 $content = $content->getSection( $section ); 00168 00169 if ( !$content ) { 00170 $this->dieUsage( "There is no section {$section}.", 'nosuchsection' ); 00171 } 00172 } 00173 00174 if ( !$content ) { 00175 $text = ''; 00176 } else { 00177 $text = $content->serialize( $contentFormat ); 00178 } 00179 00180 $params['text'] = $params['prependtext'] . $text . $params['appendtext']; 00181 $toMD5 = $params['prependtext'] . $params['appendtext']; 00182 } 00183 00184 if ( $params['undo'] > 0 ) { 00185 if ( $params['undoafter'] > 0 ) { 00186 if ( $params['undo'] < $params['undoafter'] ) { 00187 list( $params['undo'], $params['undoafter'] ) = 00188 array( $params['undoafter'], $params['undo'] ); 00189 } 00190 $undoafterRev = Revision::newFromID( $params['undoafter'] ); 00191 } 00192 $undoRev = Revision::newFromID( $params['undo'] ); 00193 if ( is_null( $undoRev ) || $undoRev->isDeleted( Revision::DELETED_TEXT ) ) { 00194 $this->dieUsageMsg( array( 'nosuchrevid', $params['undo'] ) ); 00195 } 00196 00197 if ( $params['undoafter'] == 0 ) { 00198 $undoafterRev = $undoRev->getPrevious(); 00199 } 00200 if ( is_null( $undoafterRev ) || $undoafterRev->isDeleted( Revision::DELETED_TEXT ) ) { 00201 $this->dieUsageMsg( array( 'nosuchrevid', $params['undoafter'] ) ); 00202 } 00203 00204 if ( $undoRev->getPage() != $pageObj->getID() ) { 00205 $this->dieUsageMsg( array( 'revwrongpage', $undoRev->getID(), $titleObj->getPrefixedText() ) ); 00206 } 00207 if ( $undoafterRev->getPage() != $pageObj->getID() ) { 00208 $this->dieUsageMsg( array( 'revwrongpage', $undoafterRev->getID(), $titleObj->getPrefixedText() ) ); 00209 } 00210 00211 $newContent = $contentHandler->getUndoContent( $pageObj->getRevision(), $undoRev, $undoafterRev ); 00212 00213 if ( !$newContent ) { 00214 $this->dieUsageMsg( 'undo-failure' ); 00215 } 00216 00217 $params['text'] = $newContent->serialize( $params['contentformat'] ); 00218 00219 // If no summary was given and we only undid one rev, 00220 // use an autosummary 00221 if ( is_null( $params['summary'] ) && $titleObj->getNextRevisionID( $undoafterRev->getID() ) == $params['undo'] ) { 00222 $params['summary'] = wfMessage( 'undo-summary', $params['undo'], $undoRev->getUserText() )->inContentLanguage()->text(); 00223 } 00224 } 00225 00226 // See if the MD5 hash checks out 00227 if ( !is_null( $params['md5'] ) && md5( $toMD5 ) !== $params['md5'] ) { 00228 $this->dieUsageMsg( 'hashcheckfailed' ); 00229 } 00230 00231 // EditPage wants to parse its stuff from a WebRequest 00232 // That interface kind of sucks, but it's workable 00233 $requestArray = array( 00234 'wpTextbox1' => $params['text'], 00235 'format' => $contentFormat, 00236 'model' => $contentHandler->getModelID(), 00237 'wpEditToken' => $params['token'], 00238 'wpIgnoreBlankSummary' => '' 00239 ); 00240 00241 if ( !is_null( $params['summary'] ) ) { 00242 $requestArray['wpSummary'] = $params['summary']; 00243 } 00244 00245 if ( !is_null( $params['sectiontitle'] ) ) { 00246 $requestArray['wpSectionTitle'] = $params['sectiontitle']; 00247 } 00248 00249 // TODO: Pass along information from 'undoafter' as well 00250 if ( $params['undo'] > 0 ) { 00251 $requestArray['wpUndidRevision'] = $params['undo']; 00252 } 00253 00254 // Watch out for basetimestamp == '' 00255 // wfTimestamp() treats it as NOW, almost certainly causing an edit conflict 00256 if ( !is_null( $params['basetimestamp'] ) && $params['basetimestamp'] != '' ) { 00257 $requestArray['wpEdittime'] = wfTimestamp( TS_MW, $params['basetimestamp'] ); 00258 } else { 00259 $requestArray['wpEdittime'] = $pageObj->getTimestamp(); 00260 } 00261 00262 if ( !is_null( $params['starttimestamp'] ) && $params['starttimestamp'] != '' ) { 00263 $requestArray['wpStarttime'] = wfTimestamp( TS_MW, $params['starttimestamp'] ); 00264 } else { 00265 $requestArray['wpStarttime'] = wfTimestampNow(); // Fake wpStartime 00266 } 00267 00268 if ( $params['minor'] || ( !$params['notminor'] && $user->getOption( 'minordefault' ) ) ) { 00269 $requestArray['wpMinoredit'] = ''; 00270 } 00271 00272 if ( $params['recreate'] ) { 00273 $requestArray['wpRecreate'] = ''; 00274 } 00275 00276 if ( !is_null( $params['section'] ) ) { 00277 $section = intval( $params['section'] ); 00278 if ( $section == 0 && $params['section'] != '0' && $params['section'] != 'new' ) { 00279 $this->dieUsage( "The section parameter must be set to an integer or 'new'", "invalidsection" ); 00280 } 00281 $requestArray['wpSection'] = $params['section']; 00282 } else { 00283 $requestArray['wpSection'] = ''; 00284 } 00285 00286 $watch = $this->getWatchlistValue( $params['watchlist'], $titleObj ); 00287 00288 // Deprecated parameters 00289 if ( $params['watch'] ) { 00290 $watch = true; 00291 } elseif ( $params['unwatch'] ) { 00292 $watch = false; 00293 } 00294 00295 if ( $watch ) { 00296 $requestArray['wpWatchthis'] = ''; 00297 } 00298 00299 global $wgTitle, $wgRequest; 00300 00301 $req = new DerivativeRequest( $this->getRequest(), $requestArray, true ); 00302 00303 // Some functions depend on $wgTitle == $ep->mTitle 00304 // TODO: Make them not or check if they still do 00305 $wgTitle = $titleObj; 00306 00307 $articleObject = new Article( $titleObj ); 00308 $ep = new EditPage( $articleObject ); 00309 00310 // allow editing of non-textual content. 00311 $ep->allowNonTextContent = true; 00312 00313 $ep->setContextTitle( $titleObj ); 00314 $ep->importFormData( $req ); 00315 00316 // Run hooks 00317 // Handle APIEditBeforeSave parameters 00318 $r = array(); 00319 if ( !wfRunHooks( 'APIEditBeforeSave', array( $ep, $ep->textbox1, &$r ) ) ) { 00320 if ( count( $r ) ) { 00321 $r['result'] = 'Failure'; 00322 $apiResult->addValue( null, $this->getModuleName(), $r ); 00323 return; 00324 } else { 00325 $this->dieUsageMsg( 'hookaborted' ); 00326 } 00327 } 00328 00329 // Do the actual save 00330 $oldRevId = $articleObject->getRevIdFetched(); 00331 $result = null; 00332 // Fake $wgRequest for some hooks inside EditPage 00333 // @todo FIXME: This interface SUCKS 00334 $oldRequest = $wgRequest; 00335 $wgRequest = $req; 00336 00337 $status = $ep->internalAttemptSave( $result, $user->isAllowed( 'bot' ) && $params['bot'] ); 00338 $wgRequest = $oldRequest; 00339 global $wgMaxArticleSize; 00340 00341 switch( $status->value ) { 00342 case EditPage::AS_HOOK_ERROR: 00343 case EditPage::AS_HOOK_ERROR_EXPECTED: 00344 $this->dieUsageMsg( 'hookaborted' ); 00345 00346 case EditPage::AS_PARSE_ERROR: 00347 $this->dieUsage( $status->getMessage(), 'parseerror' ); 00348 00349 case EditPage::AS_IMAGE_REDIRECT_ANON: 00350 $this->dieUsageMsg( 'noimageredirect-anon' ); 00351 00352 case EditPage::AS_IMAGE_REDIRECT_LOGGED: 00353 $this->dieUsageMsg( 'noimageredirect-logged' ); 00354 00355 case EditPage::AS_SPAM_ERROR: 00356 $this->dieUsageMsg( array( 'spamdetected', $result['spam'] ) ); 00357 00358 case EditPage::AS_BLOCKED_PAGE_FOR_USER: 00359 $this->dieUsageMsg( 'blockedtext' ); 00360 00361 case EditPage::AS_MAX_ARTICLE_SIZE_EXCEEDED: 00362 case EditPage::AS_CONTENT_TOO_BIG: 00363 $this->dieUsageMsg( array( 'contenttoobig', $wgMaxArticleSize ) ); 00364 00365 case EditPage::AS_READ_ONLY_PAGE_ANON: 00366 $this->dieUsageMsg( 'noedit-anon' ); 00367 00368 case EditPage::AS_READ_ONLY_PAGE_LOGGED: 00369 $this->dieUsageMsg( 'noedit' ); 00370 00371 case EditPage::AS_READ_ONLY_PAGE: 00372 $this->dieReadOnly(); 00373 00374 case EditPage::AS_RATE_LIMITED: 00375 $this->dieUsageMsg( 'actionthrottledtext' ); 00376 00377 case EditPage::AS_ARTICLE_WAS_DELETED: 00378 $this->dieUsageMsg( 'wasdeleted' ); 00379 00380 case EditPage::AS_NO_CREATE_PERMISSION: 00381 $this->dieUsageMsg( 'nocreate-loggedin' ); 00382 00383 case EditPage::AS_BLANK_ARTICLE: 00384 $this->dieUsageMsg( 'blankpage' ); 00385 00386 case EditPage::AS_CONFLICT_DETECTED: 00387 $this->dieUsageMsg( 'editconflict' ); 00388 00389 // case EditPage::AS_SUMMARY_NEEDED: Can't happen since we set wpIgnoreBlankSummary 00390 case EditPage::AS_TEXTBOX_EMPTY: 00391 $this->dieUsageMsg( 'emptynewsection' ); 00392 00393 case EditPage::AS_SUCCESS_NEW_ARTICLE: 00394 $r['new'] = ''; 00395 00396 case EditPage::AS_SUCCESS_UPDATE: 00397 $r['result'] = 'Success'; 00398 $r['pageid'] = intval( $titleObj->getArticleID() ); 00399 $r['title'] = $titleObj->getPrefixedText(); 00400 $r['contentmodel'] = $titleObj->getContentModel(); 00401 $newRevId = $articleObject->getLatest(); 00402 if ( $newRevId == $oldRevId ) { 00403 $r['nochange'] = ''; 00404 } else { 00405 $r['oldrevid'] = intval( $oldRevId ); 00406 $r['newrevid'] = intval( $newRevId ); 00407 $r['newtimestamp'] = wfTimestamp( TS_ISO_8601, 00408 $pageObj->getTimestamp() ); 00409 } 00410 break; 00411 00412 case EditPage::AS_SUMMARY_NEEDED: 00413 $this->dieUsageMsg( 'summaryrequired' ); 00414 00415 case EditPage::AS_END: 00416 default: 00417 // $status came from WikiPage::doEdit() 00418 $errors = $status->getErrorsArray(); 00419 $this->dieUsageMsg( $errors[0] ); // TODO: Add new errors to message map 00420 break; 00421 } 00422 $apiResult->addValue( null, $this->getModuleName(), $r ); 00423 } 00424 00425 public function mustBePosted() { 00426 return true; 00427 } 00428 00429 public function isWriteMode() { 00430 return true; 00431 } 00432 00433 public function getDescription() { 00434 return 'Create and edit pages.'; 00435 } 00436 00437 public function getPossibleErrors() { 00438 global $wgMaxArticleSize; 00439 00440 return array_merge( parent::getPossibleErrors(), 00441 $this->getTitleOrPageIdErrorMessage(), 00442 array( 00443 array( 'missingtext' ), 00444 array( 'createonly-exists' ), 00445 array( 'nocreate-missing' ), 00446 array( 'nosuchrevid', 'undo' ), 00447 array( 'nosuchrevid', 'undoafter' ), 00448 array( 'revwrongpage', 'id', 'text' ), 00449 array( 'undo-failure' ), 00450 array( 'hashcheckfailed' ), 00451 array( 'hookaborted' ), 00452 array( 'code' => 'parseerror', 'info' => 'Failed to parse the given text.' ), 00453 array( 'noimageredirect-anon' ), 00454 array( 'noimageredirect-logged' ), 00455 array( 'spamdetected', 'spam' ), 00456 array( 'summaryrequired' ), 00457 array( 'blockedtext' ), 00458 array( 'contenttoobig', $wgMaxArticleSize ), 00459 array( 'noedit-anon' ), 00460 array( 'noedit' ), 00461 array( 'actionthrottledtext' ), 00462 array( 'wasdeleted' ), 00463 array( 'nocreate-loggedin' ), 00464 array( 'blankpage' ), 00465 array( 'editconflict' ), 00466 array( 'emptynewsection' ), 00467 array( 'unknownerror', 'retval' ), 00468 array( 'code' => 'nosuchsection', 'info' => 'There is no section section.' ), 00469 array( 'code' => 'invalidsection', 'info' => 'The section parameter must be set to an integer or \'new\'' ), 00470 array( 'code' => 'sectionsnotsupported', 'info' => 'Sections are not supported for this type of page.' ), 00471 array( 'code' => 'editnotsupported', 'info' => 'Editing of this type of page is not supported using ' 00472 . 'the text based edit API.' ), 00473 array( 'code' => 'appendnotsupported', 'info' => 'This type of page can not be edited by appending ' 00474 . 'or prepending text.' ), 00475 array( 'code' => 'badformat', 'info' => 'The requested serialization format can not be applied to ' 00476 . 'the page\'s content model' ), 00477 array( 'customcssprotected' ), 00478 array( 'customjsprotected' ), 00479 ) 00480 ); 00481 } 00482 00483 public function getAllowedParams() { 00484 return array( 00485 'title' => array( 00486 ApiBase::PARAM_TYPE => 'string', 00487 ), 00488 'pageid' => array( 00489 ApiBase::PARAM_TYPE => 'integer', 00490 ), 00491 'section' => null, 00492 'sectiontitle' => array( 00493 ApiBase::PARAM_TYPE => 'string', 00494 ApiBase::PARAM_REQUIRED => false, 00495 ), 00496 'text' => null, 00497 'token' => array( 00498 ApiBase::PARAM_TYPE => 'string', 00499 ApiBase::PARAM_REQUIRED => true 00500 ), 00501 'summary' => null, 00502 'minor' => false, 00503 'notminor' => false, 00504 'bot' => false, 00505 'basetimestamp' => null, 00506 'starttimestamp' => null, 00507 'recreate' => false, 00508 'createonly' => false, 00509 'nocreate' => false, 00510 'watch' => array( 00511 ApiBase::PARAM_DFLT => false, 00512 ApiBase::PARAM_DEPRECATED => true, 00513 ), 00514 'unwatch' => array( 00515 ApiBase::PARAM_DFLT => false, 00516 ApiBase::PARAM_DEPRECATED => true, 00517 ), 00518 'watchlist' => array( 00519 ApiBase::PARAM_DFLT => 'preferences', 00520 ApiBase::PARAM_TYPE => array( 00521 'watch', 00522 'unwatch', 00523 'preferences', 00524 'nochange' 00525 ), 00526 ), 00527 'md5' => null, 00528 'prependtext' => null, 00529 'appendtext' => null, 00530 'undo' => array( 00531 ApiBase::PARAM_TYPE => 'integer' 00532 ), 00533 'undoafter' => array( 00534 ApiBase::PARAM_TYPE => 'integer' 00535 ), 00536 'redirect' => array( 00537 ApiBase::PARAM_TYPE => 'boolean', 00538 ApiBase::PARAM_DFLT => false, 00539 ), 00540 'contentformat' => array( 00541 ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(), 00542 ), 00543 'contentmodel' => array( 00544 ApiBase::PARAM_TYPE => ContentHandler::getContentModels(), 00545 ) 00546 ); 00547 } 00548 00549 public function getParamDescription() { 00550 $p = $this->getModulePrefix(); 00551 return array( 00552 'title' => "Title of the page you want to edit. Cannot be used together with {$p}pageid", 00553 'pageid' => "Page ID of the page you want to edit. Cannot be used together with {$p}title", 00554 'section' => 'Section number. 0 for the top section, \'new\' for a new section', 00555 'sectiontitle' => 'The title for a new section', 00556 'text' => 'Page content', 00557 'token' => array( 'Edit token. You can get one of these through prop=info.', 00558 "The token should always be sent as the last parameter, or at least, after the {$p}text parameter" 00559 ), 00560 'summary' => "Edit summary. Also section title when {$p}section=new and {$p}sectiontitle is not set", 00561 'minor' => 'Minor edit', 00562 'notminor' => 'Non-minor edit', 00563 'bot' => 'Mark this edit as bot', 00564 'basetimestamp' => array( 'Timestamp of the base revision (obtained through prop=revisions&rvprop=timestamp).', 00565 'Used to detect edit conflicts; leave unset to ignore conflicts' 00566 ), 00567 'starttimestamp' => array( 'Timestamp when you obtained the edit token.', 00568 'Used to detect edit conflicts; leave unset to ignore conflicts' 00569 ), 00570 'recreate' => 'Override any errors about the article having been deleted in the meantime', 00571 'createonly' => 'Don\'t edit the page if it exists already', 00572 'nocreate' => 'Throw an error if the page doesn\'t exist', 00573 'watch' => 'Add the page to your watchlist', 00574 'unwatch' => 'Remove the page from your watchlist', 00575 'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch', 00576 'md5' => array( "The MD5 hash of the {$p}text parameter, or the {$p}prependtext and {$p}appendtext parameters concatenated.", 00577 'If set, the edit won\'t be done unless the hash is correct' ), 00578 'prependtext' => "Add this text to the beginning of the page. Overrides {$p}text", 00579 'appendtext' => array( "Add this text to the end of the page. Overrides {$p}text.", 00580 "Use {$p}section=new to append a new section" ), 00581 'undo' => "Undo this revision. Overrides {$p}text, {$p}prependtext and {$p}appendtext", 00582 'undoafter' => 'Undo all revisions from undo to this one. If not set, just undo one revision', 00583 'redirect' => 'Automatically resolve redirects', 00584 'contentformat' => 'Content serialization format used for the input text', 00585 'contentmodel' => 'Content model of the new content', 00586 ); 00587 } 00588 00589 public function getResultProperties() { 00590 return array( 00591 '' => array( 00592 'new' => 'boolean', 00593 'result' => array( 00594 ApiBase::PROP_TYPE => array( 00595 'Success', 00596 'Failure' 00597 ), 00598 ), 00599 'pageid' => array( 00600 ApiBase::PROP_TYPE => 'integer', 00601 ApiBase::PROP_NULLABLE => true 00602 ), 00603 'title' => array( 00604 ApiBase::PROP_TYPE => 'string', 00605 ApiBase::PROP_NULLABLE => true 00606 ), 00607 'nochange' => 'boolean', 00608 'oldrevid' => array( 00609 ApiBase::PROP_TYPE => 'integer', 00610 ApiBase::PROP_NULLABLE => true 00611 ), 00612 'newrevid' => array( 00613 ApiBase::PROP_TYPE => 'integer', 00614 ApiBase::PROP_NULLABLE => true 00615 ), 00616 'newtimestamp' => array( 00617 ApiBase::PROP_TYPE => 'string', 00618 ApiBase::PROP_NULLABLE => true 00619 ) 00620 ) 00621 ); 00622 } 00623 00624 public function needsToken() { 00625 return true; 00626 } 00627 00628 public function getTokenSalt() { 00629 return ''; 00630 } 00631 00632 public function getExamples() { 00633 return array( 00634 00635 'api.php?action=edit&title=Test&summary=test%20summary&text=article%20content&basetimestamp=20070824123454&token=%2B\\' 00636 => 'Edit a page (anonymous user)', 00637 00638 'api.php?action=edit&title=Test&summary=NOTOC&minor=&prependtext=__NOTOC__%0A&basetimestamp=20070824123454&token=%2B\\' 00639 => 'Prepend __NOTOC__ to a page (anonymous user)', 00640 'api.php?action=edit&title=Test&undo=13585&undoafter=13579&basetimestamp=20070824123454&token=%2B\\' 00641 => 'Undo r13579 through r13585 with autosummary (anonymous user)', 00642 ); 00643 } 00644 00645 public function getHelpUrls() { 00646 return 'https://www.mediawiki.org/wiki/API:Edit'; 00647 } 00648 00649 public function getVersion() { 00650 return __CLASS__ . ': $Id$'; 00651 } 00652 }