mediawiki-extensions-Visual.../ApiVisualEditorEdit.php
Brad Jorsch 6dd98b5f3b Update token handling for MediaWiki API change
MediaWiki change I2793a3f2 changes API handling in a way that needs
updates to extensions for proper operation:
* needsToken() now returns a string
* Most custom token types are being replaced with a 'csrf' token (the
  former 'edit' token); any others need a new hook.
* All tokens must use a static salt. Compat with web UI using non-static
  tokens is supported and also serves to handle the now-deprecated token
  fetching.
* Documentation in getParamDescription() should return a string (not
  array) for 'token', as the signal to core that it should be replaced
  with a standardized message.

When compatibility with earlier versions of MediaWiki is no longer
maintained, the entry for 'token' from getAllowedParams() and
getParamDescription() may be removed, as may getTokenSalt(). This patch
leaves them in place.

Note this is intended to be compatible with earlier versions of
MediaWiki, and so should be safe to merge before the core change.

Change-Id: Ia6e512aae366996de4e73a8d7f4f03fcddd77286
2014-08-13 16:19:42 +00:00

208 lines
5.7 KiB
PHP

<?php
/**
* Parsoid API wrapper.
*
* @file
* @ingroup Extensions
* @copyright 2011-2014 VisualEditor Team and others; see AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
class ApiVisualEditorEdit extends ApiVisualEditor {
protected function saveWikitext( $title, $wikitext, $params ) {
$apiParams = array(
'action' => 'edit',
'title' => $title->getPrefixedDBkey(),
'text' => $wikitext,
'summary' => $params['summary'],
'basetimestamp' => $params['basetimestamp'],
'starttimestamp' => $params['starttimestamp'],
'token' => $params['token'],
);
if ( $params['minor'] ) {
$apiParams['minor'] = true;
} else {
$apiParams['notminor'] = true;
}
// FIXME add some way that the user's preferences can be respected
$apiParams['watchlist'] = $params['watch'] ? 'watch' : 'unwatch';
if ( $params['captchaid'] ) {
$apiParams['captchaid'] = $params['captchaid'];
}
if ( $params['captchaword'] ) {
$apiParams['captchaword'] = $params['captchaword'];
}
$api = new ApiMain(
new DerivativeRequest(
$this->getRequest(),
$apiParams + $this->getRequest()->getValues(),
true // was posted
),
true // enable write
);
$api->execute();
return $api->getResultData();
}
public function execute() {
global $wgVisualEditorNamespaces, $wgVisualEditorUseChangeTagging;
$user = $this->getUser();
$params = $this->extractRequestParams();
$page = Title::newFromText( $params['page'] );
if ( !$page ) {
$this->dieUsageMsg( 'invalidtitle', $params['page'] );
}
if ( !in_array( $page->getNamespace(), $wgVisualEditorNamespaces ) ) {
$this->dieUsage( "VisualEditor is not enabled in namespace " .
$page->getNamespace(), 'novenamespace' );
}
$parserParams = array();
if ( isset( $params['oldid'] ) ) {
$parserParams['oldid'] = $params['oldid'];
}
$html = $params['html'];
if ( substr( $html, 0, 11 ) === 'rawdeflate,' ) {
$html = gzinflate( base64_decode( substr( $html, 11 ) ) );
}
if ( $params['cachekey'] !== null ) {
$wikitext = $this->trySerializationCache( $params['cachekey'] );
if ( !is_string( $wikitext ) ) {
$this->dieUsage( 'No cached serialization found with that key', 'badcachekey' );
}
} else {
$wikitext = $this->postHTML( $page, $html, $parserParams );
if ( $wikitext === false ) {
$this->dieUsage( 'Error contacting the Parsoid server', 'parsoidserver' );
}
}
$saveresult = $this->saveWikitext( $page, $wikitext, $params );
$editStatus = $saveresult['edit']['result'];
// Error
if ( $editStatus !== 'Success' ) {
$result = array(
'result' => 'error',
'edit' => $saveresult['edit']
);
// Success
} else {
if ( isset( $saveresult['edit']['newrevid'] ) && $wgVisualEditorUseChangeTagging ) {
ChangeTags::addTags( 'visualeditor', null,
intval( $saveresult['edit']['newrevid'] ),
null
);
if ( $params['needcheck'] ) {
ChangeTags::addTags( 'visualeditor-needcheck', null,
intval( $saveresult['edit']['newrevid'] ),
null
);
}
}
// Return result of parseWikitext instead of saveWikitext so that the
// frontend can update the page rendering without a refresh.
$result = $this->parseWikitext( $page );
if ( $result === false ) {
$this->dieUsage( 'Error contacting the Parsoid server', 'parsoidserver' );
}
$result['isRedirect'] = $page->isRedirect();
$content = new WikitextContent( $wikitext );
$parserOutput = $content->getParserOutput( $page );
if ( $parserOutput ) {
$result['displayTitleHtml'] = $parserOutput->getDisplayTitle();
} else {
wfDebug( '[VE] ApiVisualEditorEdit - parserOutput was false' );
}
if ( isset( $saveresult['edit']['newrevid'] ) ) {
$result['newrevid'] = intval( $saveresult['edit']['newrevid'] );
}
$result['result'] = 'success';
}
$this->getResult()->addValue( null, $this->getModuleName(), $result );
}
public function getAllowedParams() {
return array(
'page' => array(
ApiBase::PARAM_REQUIRED => true,
),
'token' => array(
ApiBase::PARAM_REQUIRED => true,
),
'wikitext' => null,
'basetimestamp' => null,
'starttimestamp' => null,
'needcheck' => array(
ApiBase::PARAM_TYPE => 'boolean'
),
'oldid' => null,
'minor' => null,
'watch' => null,
'html' => null,
'summary' => null,
'captchaid' => null,
'captchaword' => null,
'cachekey' => null,
);
}
public function needsToken() {
return 'csrf';
}
public function getTokenSalt() {
return '';
}
public function mustBePosted() {
return true;
}
public function isWriteMode() {
return true;
}
public function getParamDescription() {
return array(
'page' => 'The page to perform actions on.',
'oldid' => 'The revision number to use. Defaults to latest revision. Use 0 for new page.',
'minor' => 'Flag for minor edit.',
'html' => 'HTML to send to Parsoid in exchange for wikitext',
'summary' => 'Edit summary',
'basetimestamp' => 'When saving, set this to the timestamp of the revision that was'
. ' edited. Used to detect edit conflicts.',
'starttimestamp' => 'When saving, set this to the timestamp of when the page was loaded.'
. ' Used to detect edit conflicts.',
'token' => 'Edit token',
'needcheck' => 'When saving, set this parameter if the revision might have roundtrip'
. ' problems. This will result in the edit being tagged.',
'captchaid' => 'Captcha ID (when saving with a captcha response).',
'captchaword' => 'Answer to the captcha (when saving with a captcha response).',
'cachekey' => 'Use the result of a previous serializeforcache request with this key.'
. 'Overrides html.',
);
}
public function getDescription() {
return 'Save an HTML5 page to MediaWiki (converted to wikitext via the Parsoid service).';
}
}