mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/DiscussionTools
synced 2024-09-24 10:58:20 +00:00
Merge "Edit API for replies"
This commit is contained in:
commit
740e6eb174
|
@ -14,7 +14,10 @@
|
|||
}
|
||||
},
|
||||
"MessagesDirs": {
|
||||
"DiscussionTools": [ "i18n" ]
|
||||
"DiscussionTools": [
|
||||
"i18n",
|
||||
"i18n/api"
|
||||
]
|
||||
},
|
||||
"callback": "\\MediaWiki\\Extension\\DiscussionTools\\Hooks::onRegistration",
|
||||
"ResourceFileModulePaths": {
|
||||
|
@ -272,6 +275,9 @@
|
|||
"TestAutoloadNamespaces": {
|
||||
"MediaWiki\\Extension\\DiscussionTools\\Tests\\": "tests/phpunit/"
|
||||
},
|
||||
"APIModules": {
|
||||
"discussiontoolsedit": "MediaWiki\\Extension\\DiscussionTools\\ApiDiscussionToolsEdit"
|
||||
},
|
||||
"Hooks": {
|
||||
"BeforePageDisplay": "\\MediaWiki\\Extension\\DiscussionTools\\Hooks::onBeforePageDisplay",
|
||||
"ResourceLoaderGetConfigVars": "\\MediaWiki\\Extension\\DiscussionTools\\Hooks::onResourceLoaderGetConfigVars",
|
||||
|
|
10
i18n/api/en.json
Normal file
10
i18n/api/en.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": []
|
||||
},
|
||||
"apierror-discussiontools-commentid-notfound": "Comment ID not found: $1",
|
||||
"apihelp-discussiontoolsedit-summary": "Edit summary.",
|
||||
"apihelp-discussiontoolsedit-param-commentid": "Comment ID.",
|
||||
"apihelp-discussiontoolsedit-param-html": "HTML",
|
||||
"apihelp-discussiontoolsedit-param-wikitext": "Wikitext"
|
||||
}
|
10
i18n/api/qqq.json
Normal file
10
i18n/api/qqq.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": []
|
||||
},
|
||||
"apierror-discussiontools-commentid-notfound": "{{doc-apierror}}\n\nParameters:\n* $1 - Comment ID",
|
||||
"apihelp-discussiontoolsedit-summary": "{{doc-apihelp-summary|discussiontoolsedit}}",
|
||||
"apihelp-discussiontoolsedit-param-commentid": "{{doc-apihelp-summary|discussiontoolsedit|commentid}}",
|
||||
"apihelp-discussiontoolsedit-param-html": "{{doc-apihelp-summary|discussiontoolsedit|html}}",
|
||||
"apihelp-discussiontoolsedit-param-wikitext": "{{doc-apihelp-summary|discussiontoolsedit|wikitext}}"
|
||||
}
|
179
includes/ApiDiscussionToolsEdit.php
Normal file
179
includes/ApiDiscussionToolsEdit.php
Normal file
|
@ -0,0 +1,179 @@
|
|||
<?php
|
||||
|
||||
namespace MediaWiki\Extension\DiscussionTools;
|
||||
|
||||
use ApiBase;
|
||||
use ApiMain;
|
||||
use ApiParsoidTrait;
|
||||
use DerivativeRequest;
|
||||
use DOMElement;
|
||||
use Title;
|
||||
use Wikimedia\ParamValidator\ParamValidator;
|
||||
use Wikimedia\Parsoid\Utils\DOMUtils;
|
||||
|
||||
class ApiDiscussionToolsEdit extends ApiBase {
|
||||
|
||||
use ApiParsoidTrait;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function __construct( ApiMain $main, string $name ) {
|
||||
parent::__construct( $main, $name );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function execute() {
|
||||
$params = $this->extractRequestParams();
|
||||
$title = Title::newFromText( $params['page'] );
|
||||
$result = null;
|
||||
|
||||
if ( !$title ) {
|
||||
$this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['page'] ) ] );
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( $params['paction'] ) {
|
||||
case 'addcomment':
|
||||
// Fetch the latest revision
|
||||
$revision = $this->getLatestRevision( $title );
|
||||
$oldid = $revision->getId();
|
||||
$response = $this->requestRestbasePageHtml( $revision );
|
||||
$headers = $response['headers'];
|
||||
|
||||
$doc = DOMUtils::parseHTML( $response['body'] );
|
||||
$container = $doc->getElementsByTagName( 'body' )->item( 0 );
|
||||
'@phan-var DOMElement $container';
|
||||
|
||||
$commentId = $params['commentid'] ?? null;
|
||||
|
||||
if ( !$commentId ) {
|
||||
$this->dieWithError( [ 'apierror-missingparam', 'commentid' ] );
|
||||
}
|
||||
|
||||
$parser = CommentParser::newFromGlobalState( $container );
|
||||
$comment = $parser->findCommentById( $commentId );
|
||||
if ( !$comment ) {
|
||||
$this->dieWithError( [ 'apierror-discussiontools-commentid-notfound', $commentId ] );
|
||||
return;
|
||||
}
|
||||
|
||||
$this->requireOnlyOneParameter( $params, 'wikitext', 'html' );
|
||||
|
||||
if ( $params['wikitext'] !== null ) {
|
||||
CommentModifier::addWikitextReply( $comment, $params['wikitext'] );
|
||||
} else {
|
||||
CommentModifier::addHtmlReply( $comment, $params['html'] );
|
||||
}
|
||||
|
||||
$heading = $comment->getHeading();
|
||||
if ( $heading->isPlaceholderHeading() ) {
|
||||
// This comment is in 0th section, there's no section title for the edit summary
|
||||
$summaryPrefix = '';
|
||||
} else {
|
||||
$summaryPrefix = '/* ' . $heading->getRange()->startContainer->textContent . ' */ ';
|
||||
}
|
||||
$summary = $summaryPrefix .
|
||||
$this->msg( 'discussiontools-defaultsummary-reply' )->inContentLanguage()->text();
|
||||
|
||||
$api = new ApiMain(
|
||||
new DerivativeRequest(
|
||||
$this->getRequest(),
|
||||
[
|
||||
'action' => 'visualeditoredit',
|
||||
'paction' => 'save',
|
||||
'page' => $params['page'],
|
||||
'token' => $params['token'],
|
||||
'oldid' => $oldid,
|
||||
'html' => $doc->saveHtml(),
|
||||
'summary' => $summary,
|
||||
'baserevid' => $revision->getId(),
|
||||
'starttimestamp' => wfTimestampNow(),
|
||||
'basetimestamp' => $revision->getTimestamp(),
|
||||
'etag' => $headers['etag'],
|
||||
'watchlist' => $params['watchlist'],
|
||||
'captchaid' => $params['captchaid'],
|
||||
'captchaword' => $params['captchaword']
|
||||
],
|
||||
/* was posted? */ true
|
||||
),
|
||||
/* enable write? */ true
|
||||
);
|
||||
|
||||
$api->execute();
|
||||
|
||||
// TODO: Tags are only added by 'dttags' existing on the original request
|
||||
// context (see Hook::onRecentChangeSave). What tags (if any) should be
|
||||
// added in this API?
|
||||
|
||||
$data = $api->getResult()->getResultData();
|
||||
$result = $data['visualeditoredit'];
|
||||
break;
|
||||
}
|
||||
|
||||
$this->getResult()->addValue( null, $this->getModuleName(), $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getAllowedParams() {
|
||||
return [
|
||||
'paction' => [
|
||||
ParamValidator::PARAM_REQUIRED => true,
|
||||
ParamValidator::PARAM_TYPE => [
|
||||
'addcomment',
|
||||
],
|
||||
ApiBase::PARAM_HELP_MSG => 'apihelp-visualeditoredit-param-paction',
|
||||
],
|
||||
'page' => [
|
||||
ParamValidator::PARAM_REQUIRED => true,
|
||||
ApiBase::PARAM_HELP_MSG => 'apihelp-visualeditoredit-param-page',
|
||||
],
|
||||
'token' => [
|
||||
ParamValidator::PARAM_REQUIRED => true,
|
||||
],
|
||||
'commentid' => null,
|
||||
'wikitext' => [
|
||||
ParamValidator::PARAM_TYPE => 'text',
|
||||
ParamValidator::PARAM_DEFAULT => null,
|
||||
],
|
||||
'html' => [
|
||||
ParamValidator::PARAM_TYPE => 'text',
|
||||
ParamValidator::PARAM_DEFAULT => null,
|
||||
],
|
||||
'watchlist' => [
|
||||
ApiBase::PARAM_HELP_MSG => 'apihelp-edit-param-watchlist',
|
||||
],
|
||||
'captchaid' => [
|
||||
ApiBase::PARAM_HELP_MSG => 'apihelp-visualeditoredit-param-captchaword',
|
||||
],
|
||||
'captchaword' => [
|
||||
ApiBase::PARAM_HELP_MSG => 'apihelp-visualeditoredit-param-captchaword',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function needsToken() {
|
||||
return 'csrf';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function isWriteMode() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -255,52 +255,35 @@ CommentController.prototype.teardown = function ( abandoned ) {
|
|||
}
|
||||
};
|
||||
|
||||
CommentController.prototype.postReply = function ( comment ) {
|
||||
if ( this.replyWidget.getMode() === 'source' ) {
|
||||
modifier.addWikitextReply( comment, this.replyWidget.getValue() );
|
||||
} else {
|
||||
modifier.addHtmlReply( comment, this.replyWidget.getValue() );
|
||||
}
|
||||
};
|
||||
|
||||
CommentController.prototype.save = function ( parsoidData ) {
|
||||
var heading, summaryPrefix, summary, savePromise,
|
||||
mode = this.replyWidget.getMode(),
|
||||
var savePromise,
|
||||
comment = parsoidData.comment,
|
||||
pageData = parsoidData.pageData,
|
||||
replyWidget = this.replyWidget,
|
||||
commentController = this;
|
||||
|
||||
// Update the Parsoid DOM
|
||||
this.postReply( parsoidData.comment );
|
||||
|
||||
heading = comment.getHeading();
|
||||
if ( heading.placeholderHeading ) {
|
||||
// This comment is in 0th section, there's no section title for the edit summary
|
||||
summaryPrefix = '';
|
||||
} else {
|
||||
summaryPrefix = '/* ' + heading.range.startContainer.innerText + ' */ ';
|
||||
}
|
||||
|
||||
summary = summaryPrefix + mw.msg( 'discussiontools-defaultsummary-reply' );
|
||||
|
||||
return this.replyWidget.checkboxesPromise.then( function ( checkboxes ) {
|
||||
var captchaInput = commentController.replyWidget.captchaInput,
|
||||
data = {
|
||||
action: 'discussiontoolsedit',
|
||||
paction: 'addcomment',
|
||||
page: pageData.pageName,
|
||||
oldid: pageData.oldId,
|
||||
summary: summary,
|
||||
baserevid: pageData.oldId,
|
||||
starttimestamp: pageData.startTimeStamp,
|
||||
etag: pageData.etag,
|
||||
commentid: comment.id,
|
||||
assert: mw.user.isAnon() ? 'anon' : 'user',
|
||||
assertuser: mw.user.getName() || undefined,
|
||||
dttags: [
|
||||
'discussiontools',
|
||||
'discussiontools-reply',
|
||||
'discussiontools-' + mode
|
||||
'discussiontools-' + replyWidget.getMode()
|
||||
].join( ',' )
|
||||
};
|
||||
|
||||
if ( replyWidget.getMode() === 'source' ) {
|
||||
data.wikitext = replyWidget.getValue();
|
||||
} else {
|
||||
data.html = replyWidget.getValue();
|
||||
}
|
||||
|
||||
if ( captchaInput ) {
|
||||
data.captchaid = captchaInput.getCaptchaId();
|
||||
data.captchaword = captchaInput.getCaptchaWord();
|
||||
|
@ -312,8 +295,7 @@ CommentController.prototype.save = function ( parsoidData ) {
|
|||
'unwatch';
|
||||
}
|
||||
|
||||
savePromise = mw.libs.ve.targetSaver.saveDoc(
|
||||
parsoidData.doc,
|
||||
savePromise = mw.libs.ve.targetSaver.postContent(
|
||||
data,
|
||||
{
|
||||
// No timeout. Huge talk pages take a long time to save, and falsely reporting an error can
|
||||
|
@ -321,15 +303,9 @@ CommentController.prototype.save = function ( parsoidData ) {
|
|||
api: new mw.Api( { ajax: { timeout: 0 }, parameters: { formatversion: 2 } } )
|
||||
}
|
||||
).catch( function ( code, data ) {
|
||||
// Handle edit conflicts. Load the latest revision of the page, then try again. If the parent
|
||||
// comment has been deleted from the page, or if retry also fails for some other reason, the
|
||||
// error is handled as normal below.
|
||||
// Try again if there's an edit conflict. The latest revision will be fetched on the server.
|
||||
if ( code === 'editconflict' ) {
|
||||
return getLatestRevId( pageData.pageName ).then( function ( latestRevId ) {
|
||||
return controller.getParsoidCommentData( pageData.pageName, latestRevId, comment.id ).then( function ( parsoidData ) {
|
||||
return commentController.save( parsoidData );
|
||||
} );
|
||||
} );
|
||||
return commentController.save( parsoidData );
|
||||
}
|
||||
return $.Deferred().reject( code, data ).promise();
|
||||
} );
|
||||
|
|
|
@ -592,11 +592,11 @@ ReplyWidget.prototype.onReplyClick = function () {
|
|||
}
|
||||
widget.captchaInput = undefined;
|
||||
|
||||
if ( OO.getProp( data, 'visualeditoredit', 'edit', 'captcha' ) ) {
|
||||
if ( OO.getProp( data, 'discussiontoolsedit', 'edit', 'captcha' ) ) {
|
||||
code = 'captcha';
|
||||
|
||||
widget.captchaInput = new mw.libs.confirmEdit.CaptchaInputWidget(
|
||||
OO.getProp( data, 'visualeditoredit', 'edit', 'captcha' )
|
||||
OO.getProp( data, 'discussiontoolsedit', 'edit', 'captcha' )
|
||||
);
|
||||
// Save when pressing 'Enter' in captcha field as it is single line.
|
||||
widget.captchaInput.on( 'enter', function () {
|
||||
|
|
|
@ -309,6 +309,7 @@ function addSiblingListItem( previousItem ) {
|
|||
return listItem;
|
||||
}
|
||||
|
||||
// TODO: No longer used in the client
|
||||
function createWikitextNode( doc, wt ) {
|
||||
var span = doc.createElement( 'span' );
|
||||
|
||||
|
@ -386,6 +387,8 @@ function appendSignature( container ) {
|
|||
/**
|
||||
* Add a reply to a specific comment
|
||||
*
|
||||
* TODO: No longer used in the client
|
||||
*
|
||||
* @param {CommentItem} comment Comment being replied to
|
||||
* @param {HTMLElement} container Container of comment DOM nodes
|
||||
*/
|
||||
|
@ -410,6 +413,8 @@ function addReply( comment, container ) {
|
|||
/**
|
||||
* Create a container of comment DOM nodes from wikitext
|
||||
*
|
||||
* TODO: No longer used in the client
|
||||
*
|
||||
* @param {CommentItem} comment Comment being replied to
|
||||
* @param {string} wikitext Wikitext
|
||||
*/
|
||||
|
@ -435,6 +440,8 @@ function addWikitextReply( comment, wikitext ) {
|
|||
/**
|
||||
* Create a container of comment DOM nodes from HTML
|
||||
*
|
||||
* TODO: No longer used in the client
|
||||
*
|
||||
* @param {CommentItem} comment Comment being replied to
|
||||
* @param {string} html HTML
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue